diff options
Diffstat (limited to 'drivers/soc/renesas')
| -rw-r--r-- | drivers/soc/renesas/Kconfig | 510 | ||||
| -rw-r--r-- | drivers/soc/renesas/Makefile | 20 | ||||
| -rw-r--r-- | drivers/soc/renesas/pwc-rzv2m.c | 143 | ||||
| -rw-r--r-- | drivers/soc/renesas/r9a06g032-smp.c | 96 | ||||
| -rw-r--r-- | drivers/soc/renesas/r9a08g045-sysc.c | 88 | ||||
| -rw-r--r-- | drivers/soc/renesas/r9a08g046-sysc.c | 86 | ||||
| -rw-r--r-- | drivers/soc/renesas/r9a09g047-sys.c | 137 | ||||
| -rw-r--r-- | drivers/soc/renesas/r9a09g056-sys.c | 139 | ||||
| -rw-r--r-- | drivers/soc/renesas/r9a09g057-sys.c | 153 | ||||
| -rw-r--r-- | drivers/soc/renesas/rcar-mfis.c | 384 | ||||
| -rw-r--r-- | drivers/soc/renesas/rcar-rst.c | 186 | ||||
| -rw-r--r-- | drivers/soc/renesas/renesas-soc.c | 560 | ||||
| -rw-r--r-- | drivers/soc/renesas/rz-sysc.c | 172 | ||||
| -rw-r--r-- | drivers/soc/renesas/rz-sysc.h | 54 | ||||
| -rw-r--r-- | drivers/soc/renesas/rzn1_irqmux.c | 126 |
15 files changed, 2854 insertions, 0 deletions
diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig new file mode 100644 index 000000000..eb8ac0d47 --- /dev/null +++ b/drivers/soc/renesas/Kconfig @@ -0,0 +1,510 @@ +# SPDX-License-Identifier: GPL-2.0 +menuconfig SOC_RENESAS + bool "Renesas SoC driver support" if COMPILE_TEST && !ARCH_RENESAS + default y if ARCH_RENESAS + select GPIOLIB + select PINCTRL + select SOC_BUS + +if SOC_RENESAS + +config ARCH_RCAR_GEN1 + bool + select PM + select PM_GENERIC_DOMAINS + select RENESAS_INTC_IRQPIN + select RST_RCAR + select SYS_SUPPORTS_SH_TMU + +config ARCH_RCAR_GEN2 + bool + select HAVE_ARM_ARCH_TIMER + select PM + select PM_GENERIC_DOMAINS + select RENESAS_IRQC + select RST_RCAR + select SYS_SUPPORTS_SH_CMT + select SYS_SUPPORTS_SH_TMU + +config ARCH_RCAR_GEN3 + bool + select PM + select PM_GENERIC_DOMAINS + select RENESAS_IRQC + select RST_RCAR + select SYS_SUPPORTS_SH_CMT + select SYS_SUPPORTS_SH_TMU + +config ARCH_RCAR_GEN4 + bool + select ARCH_RCAR_GEN3 + +config ARCH_RCAR_GEN5 + bool + select ARCH_RCAR_GEN4 + +config ARCH_RMOBILE + bool + select PM + select PM_GENERIC_DOMAINS + select SYS_SUPPORTS_SH_CMT + select SYS_SUPPORTS_SH_TMU + select SYSC_RMOBILE + +config ARCH_RZG2L + bool + select PM + select PM_GENERIC_DOMAINS + select RENESAS_RZG2L_IRQC + +config ARCH_RZN1 + bool + select PM + select PM_GENERIC_DOMAINS + select ARM_AMBA + select RZN1_IRQMUX if GPIO_DWAPB + +if ARM && ARCH_RENESAS + +#comment "Renesas ARM SoCs System Type" + +config ARCH_EMEV2 + bool "ARM32 Platform support for Emma Mobile EV2" + default ARCH_RENESAS + select HAVE_ARM_SCU if SMP + select SYS_SUPPORTS_EM_STI + +config ARCH_R7S72100 + bool "ARM32 Platform support for R7S72100 (RZ/A1H)" + default ARCH_RENESAS + select ARM_ERRATA_754322 + select PM + select PM_GENERIC_DOMAINS + select RENESAS_OSTM + select RENESAS_RZA1_IRQC + select SYS_SUPPORTS_SH_MTU2 + +config ARCH_R7S9210 + bool "ARM32 Platform support for R7S9210 (RZ/A2)" + default ARCH_RENESAS + select PM + select PM_GENERIC_DOMAINS + select RENESAS_OSTM + select RENESAS_RZA1_IRQC + +config ARCH_R8A73A4 + bool "ARM32 Platform support for R8A73A4 (R-Mobile APE6)" + default ARCH_RENESAS + select ARCH_RMOBILE + select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 + select HAVE_ARM_ARCH_TIMER + select RENESAS_IRQC + +config ARCH_R8A7740 + bool "ARM32 Platform support for R8A7740 (R-Mobile A1)" + default ARCH_RENESAS + select ARCH_RMOBILE + select ARM_ERRATA_754322 + select RENESAS_INTC_IRQPIN + +config ARCH_R8A7742 + bool "ARM32 Platform support for R8A7742 (RZ/G1H)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 + select SYSC_R8A7742 + +config ARCH_R8A7743 + bool "ARM32 Platform support for R8A7743 (RZ/G1M)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select SYSC_R8A7743 + +config ARCH_R8A7744 + bool "ARM32 Platform support for R8A7744 (RZ/G1N)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select SYSC_R8A7743 + +config ARCH_R8A7745 + bool "ARM32 Platform support for R8A7745 (RZ/G1E)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 + select SYSC_R8A7745 + +config ARCH_R8A77470 + bool "ARM32 Platform support for R8A77470 (RZ/G1C)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 + select SYSC_R8A77470 + +config ARCH_R8A7778 + bool "ARM32 Platform support for R8A7778 (R-Car M1A)" + default ARCH_RENESAS + select ARCH_RCAR_GEN1 + select ARM_ERRATA_754322 + +config ARCH_R8A7779 + bool "ARM32 Platform support for R8A7779 (R-Car H1)" + default ARCH_RENESAS + select ARCH_RCAR_GEN1 + select ARM_ERRATA_754322 + select ARM_GLOBAL_TIMER + select HAVE_ARM_SCU if SMP + select HAVE_ARM_TWD if SMP + select SYSC_R8A7779 + +config ARCH_R8A7790 + bool "ARM32 Platform support for R8A7790 (R-Car H2)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select ARM_ERRATA_814220 + select I2C + select SYSC_R8A7790 + +config ARCH_R8A7791 + bool "ARM32 Platform support for R8A7791 (R-Car M2-W)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select I2C + select SYSC_R8A7791 + +config ARCH_R8A7792 + bool "ARM32 Platform support for R8A7792 (R-Car V2H)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select SYSC_R8A7792 + +config ARCH_R8A7793 + bool "ARM32 Platform support for R8A7793 (R-Car M2-N)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_798181 if SMP + select I2C + select SYSC_R8A7791 + +config ARCH_R8A7794 + bool "ARM32 Platform support for R8A7794 (R-Car E2)" + default ARCH_RENESAS + select ARCH_RCAR_GEN2 + select ARM_ERRATA_814220 + select SYSC_R8A7794 + +config ARCH_R9A06G032 + bool "ARM32 Platform support for R9A06G032 (RZ/N1D)" + default ARCH_RENESAS + select ARCH_RZN1 + select ARM_ERRATA_814220 + +config ARCH_SH73A0 + bool "ARM32 Platform support for SH73A0 (SH-Mobile AG5)" + default ARCH_RENESAS + select ARCH_RMOBILE + select ARM_ERRATA_754322 + select ARM_GLOBAL_TIMER + select HAVE_ARM_SCU if SMP + select HAVE_ARM_TWD if SMP + select RENESAS_INTC_IRQPIN + +endif # ARM + +if ARM64 + +config ARCH_R8A774A1 + bool "ARM64 Platform support for R8A774A1 (RZ/G2M)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A774A1 + help + This enables support for the Renesas RZ/G2M SoC. + +config ARCH_R8A774B1 + bool "ARM64 Platform support for R8A774B1 (RZ/G2N)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A774B1 + help + This enables support for the Renesas RZ/G2N SoC. + +config ARCH_R8A774C0 + bool "ARM64 Platform support for R8A774C0 (RZ/G2E)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A774C0 + help + This enables support for the Renesas RZ/G2E SoC. + +config ARCH_R8A774E1 + bool "ARM64 Platform support for R8A774E1 (RZ/G2H)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A774E1 + help + This enables support for the Renesas RZ/G2H SoC. + +config ARCH_R8A77951 + bool "ARM64 Platform support for R8A77951 (R-Car H3 ES2.0+)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A7795 + help + This enables support for the Renesas R-Car H3 SoC (revisions 2.0 and + later). + This includes different gradings like R-Car H3e, H3e-2G, and H3Ne. + +config ARCH_R8A77960 + bool "ARM64 Platform support for R8A77960 (R-Car M3-W)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77960 + help + This enables support for the Renesas R-Car M3-W SoC. + +config ARCH_R8A77961 + bool "ARM64 Platform support for R8A77961 (R-Car M3-W+)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77961 + help + This enables support for the Renesas R-Car M3-W+ SoC. + This includes different gradings like R-Car M3e and M3e-2G. + +config ARCH_R8A77965 + bool "ARM64 Platform support for R8A77965 (R-Car M3-N)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77965 + help + This enables support for the Renesas R-Car M3-N SoC. + This includes different gradings like R-Car M3Ne and M3Ne-2G. + +config ARCH_R8A77970 + bool "ARM64 Platform support for R8A77970 (R-Car V3M)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77970 + help + This enables support for the Renesas R-Car V3M SoC. + +config ARCH_R8A77980 + bool "ARM64 Platform support for R8A77980 (R-Car V3H)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77980 + help + This enables support for the Renesas R-Car V3H SoC. + +config ARCH_R8A77990 + bool "ARM64 Platform support for R8A77990 (R-Car E3)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77990 + help + This enables support for the Renesas R-Car E3 SoC. + This includes different gradings like R-Car E3e. + +config ARCH_R8A77995 + bool "ARM64 Platform support for R8A77995 (R-Car D3)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN3 + select SYSC_R8A77995 + help + This enables support for the Renesas R-Car D3 SoC. + This includes different gradings like R-Car D3e. + +config ARCH_R8A779A0 + bool "ARM64 Platform support for R8A779A0 (R-Car V3U)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN4 + select SYSC_R8A779A0 + help + This enables support for the Renesas R-Car V3U SoC. + +config ARCH_R8A779F0 + bool "ARM64 Platform support for R8A779F0 (R-Car S4-8)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN4 + select SYSC_R8A779F0 + help + This enables support for the Renesas R-Car S4-8 SoC. + +config ARCH_R8A779G0 + bool "ARM64 Platform support for R8A779G0 (R-Car V4H)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN4 + select SYSC_R8A779G0 + help + This enables support for the Renesas R-Car V4H SoC. + +config ARCH_R8A779H0 + bool "ARM64 Platform support for R8A779H0 (R-Car V4M)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN4 + select SYSC_R8A779H0 + help + This enables support for the Renesas R-Car V4M SoC. + +config ARCH_R8A78000 + bool "ARM64 Platform support for R8A78000 (R-Car X5H)" + default y if ARCH_RENESAS + select ARCH_RCAR_GEN5 + select MDLC_R8A78000 + help + This enables support for the Renesas R-Car X5H SoC. + +config ARCH_R9A07G043 + bool "ARM64 Platform support for R9A07G043U (RZ/G2UL)" + default y if ARCH_RENESAS + select ARCH_RZG2L + help + This enables support for the Renesas RZ/G2UL SoC variants. + +config ARCH_R9A07G044 + bool "ARM64 Platform support for R9A07G044 (RZ/G2L)" + default y if ARCH_RENESAS + select ARCH_RZG2L + help + This enables support for the Renesas RZ/G2L SoC variants. + +config ARCH_R9A07G054 + bool "ARM64 Platform support for R9A07G054 (RZ/V2L)" + default y if ARCH_RENESAS + select ARCH_RZG2L + help + This enables support for the Renesas RZ/V2L SoC variants. + +config ARCH_R9A08G045 + bool "ARM64 Platform support for R9A08G045 (RZ/G3S)" + default y if ARCH_RENESAS + select ARCH_RZG2L + select SYSC_R9A08G045 + help + This enables support for the Renesas RZ/G3S SoC variants. + +config ARCH_R9A08G046 + bool "ARM64 Platform support for R9A08G046 (RZ/G3L)" + default y if ARCH_RENESAS + select ARCH_RZG2L + select SYSC_R9A08G046 + help + This enables support for the Renesas RZ/G3L SoC variants. + +config ARCH_R9A09G011 + bool "ARM64 Platform support for R9A09G011 (RZ/V2M)" + default y if ARCH_RENESAS + select PM + select PM_GENERIC_DOMAINS + select PWC_RZV2M + help + This enables support for the Renesas RZ/V2M SoC. + +config ARCH_R9A09G047 + bool "ARM64 Platform support for R9A09G047 (RZ/G3E)" + default y if ARCH_RENESAS + select SYS_R9A09G047 + help + This enables support for the Renesas RZ/G3E SoC variants. + +config ARCH_R9A09G056 + bool "ARM64 Platform support for R9A09G056 (RZ/V2N)" + default y if ARCH_RENESAS + select SYS_R9A09G056 + help + This enables support for the Renesas RZ/V2N SoC variants. + +config ARCH_R9A09G057 + bool "ARM64 Platform support for R9A09G057 (RZ/V2H(P))" + default y if ARCH_RENESAS + select RENESAS_RZV2H_ICU + select SYS_R9A09G057 + help + This enables support for the Renesas RZ/V2H(P) SoC variants. + +config ARCH_R9A09G077 + bool "ARM64 Platform support for R9A09G077 (RZ/T2H)" + default y if ARCH_RENESAS + select RENESAS_RZT2H_ICU + help + This enables support for the Renesas RZ/T2H SoC variants. + +config ARCH_R9A09G087 + bool "ARM64 Platform support for R9A09G087 (RZ/N2H)" + default y if ARCH_RENESAS + select RENESAS_RZT2H_ICU + help + This enables support for the Renesas RZ/N2H SoC variants. + +endif # ARM64 + +if RISCV + +config ARCH_R9A07G043 + bool "RISC-V Platform support for R9A07G043F (RZ/Five)" + depends on NONPORTABLE + depends on !DMA_DIRECT_REMAP + depends on RISCV_ALTERNATIVE + depends on !RISCV_ISA_ZICBOM + depends on RISCV_SBI + select ARCH_RZG2L + select AX45MP_L2_CACHE + select CACHEMAINT_FOR_DMA + select DMA_GLOBAL_POOL + select ERRATA_ANDES + select ERRATA_ANDES_CMO + help + This enables support for the Renesas RZ/Five SoC. + +endif # RISCV + +config RCAR_MFIS + tristate "Renesas R-Car MFIS driver" + depends on ARCH_RENESAS || COMPILE_TEST + depends on MAILBOX + help + Select this option to enable the Renesas R-Car MFIS core driver for + the MFIS device found on SoCs like R-Car. On families like Gen5, this + is needed to communicate with the SCP. + +config PWC_RZV2M + bool "Renesas RZ/V2M PWC support" if COMPILE_TEST + +config RST_RCAR + bool "Reset Controller support for R-Car" if COMPILE_TEST + +config RZN1_IRQMUX + bool "Renesas RZ/N1 GPIO IRQ multiplexer support" if COMPILE_TEST + +config SYSC_RZ + bool "System controller for RZ SoCs" if COMPILE_TEST + select MFD_SYSCON + +config SYSC_R9A08G045 + bool "Renesas System controller support for R9A08G045 (RZ/G3S)" if COMPILE_TEST + select SYSC_RZ + +config SYSC_R9A08G046 + bool "Renesas System controller support for R9A08G046 (RZ/G3L)" if COMPILE_TEST + select SYSC_RZ + +config SYS_R9A09G047 + bool "Renesas System controller support for R9A09G047 (RZ/G3E)" if COMPILE_TEST + select SYSC_RZ + +config SYS_R9A09G056 + bool "Renesas System controller support for R9A09G056 (RZ/V2N)" if COMPILE_TEST + select SYSC_RZ + +config SYS_R9A09G057 + bool "Renesas System controller support for R9A09G057 (RZ/V2H)" if COMPILE_TEST + select SYSC_RZ + +endif # SOC_RENESAS diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile new file mode 100644 index 000000000..81bde85c2 --- /dev/null +++ b/drivers/soc/renesas/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0 +# Generic, must be first because of soc_device_register() +obj-$(CONFIG_SOC_RENESAS) += renesas-soc.o + +# SoC +ifdef CONFIG_SMP +obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o +endif +obj-$(CONFIG_SYSC_R9A08G045) += r9a08g045-sysc.o +obj-$(CONFIG_SYSC_R9A08G046) += r9a08g046-sysc.o +obj-$(CONFIG_SYS_R9A09G047) += r9a09g047-sys.o +obj-$(CONFIG_SYS_R9A09G056) += r9a09g056-sys.o +obj-$(CONFIG_SYS_R9A09G057) += r9a09g057-sys.o + +# Family +obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o +obj-$(CONFIG_RCAR_MFIS) += rcar-mfis.o +obj-$(CONFIG_RST_RCAR) += rcar-rst.o +obj-$(CONFIG_RZN1_IRQMUX) += rzn1_irqmux.o +obj-$(CONFIG_SYSC_RZ) += rz-sysc.o diff --git a/drivers/soc/renesas/pwc-rzv2m.c b/drivers/soc/renesas/pwc-rzv2m.c new file mode 100644 index 000000000..6209168b3 --- /dev/null +++ b/drivers/soc/renesas/pwc-rzv2m.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Renesas Electronics Corporation + */ + +#include <linux/delay.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> + +#define PWC_PWCRST 0x00 +#define PWC_PWCCKEN 0x04 +#define PWC_PWCCTL 0x50 +#define PWC_GPIO 0x80 + +#define PWC_PWCRST_RSTSOFTAX 0x1 +#define PWC_PWCCKEN_ENGCKMAIN 0x1 +#define PWC_PWCCTL_PWOFF 0x1 + +struct rzv2m_pwc_priv { + void __iomem *base; + struct device *dev; + struct gpio_chip gp; + DECLARE_BITMAP(ch_en_bits, 2); +}; + +static int rzv2m_pwc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct rzv2m_pwc_priv *priv = gpiochip_get_data(chip); + u32 reg; + + /* BIT 16 enables write to BIT 0, and BIT 17 enables write to BIT 1 */ + reg = BIT(offset + 16); + if (value) + reg |= BIT(offset); + + writel(reg, priv->base + PWC_GPIO); + + assign_bit(offset, priv->ch_en_bits, value); + + return 0; +} + +static int rzv2m_pwc_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct rzv2m_pwc_priv *priv = gpiochip_get_data(chip); + + return test_bit(offset, priv->ch_en_bits); +} + +static int rzv2m_pwc_gpio_direction_output(struct gpio_chip *gc, + unsigned int nr, int value) +{ + if (nr > 1) + return -EINVAL; + + rzv2m_pwc_gpio_set(gc, nr, value); + + return 0; +} + +static const struct gpio_chip rzv2m_pwc_gc = { + .label = "gpio_rzv2m_pwc", + .owner = THIS_MODULE, + .get = rzv2m_pwc_gpio_get, + .set = rzv2m_pwc_gpio_set, + .direction_output = rzv2m_pwc_gpio_direction_output, + .can_sleep = false, + .ngpio = 2, + .base = -1, +}; + +static int rzv2m_pwc_poweroff(struct sys_off_data *data) +{ + struct rzv2m_pwc_priv *priv = data->cb_data; + + writel(PWC_PWCRST_RSTSOFTAX, priv->base + PWC_PWCRST); + writel(PWC_PWCCKEN_ENGCKMAIN, priv->base + PWC_PWCCKEN); + writel(PWC_PWCCTL_PWOFF, priv->base + PWC_PWCCTL); + + mdelay(150); + + dev_err(priv->dev, "Failed to power off the system"); + + return NOTIFY_DONE; +} + +static int rzv2m_pwc_probe(struct platform_device *pdev) +{ + struct rzv2m_pwc_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + /* + * The register used by this driver cannot be read, therefore set the + * outputs to their default values and initialize priv->ch_en_bits + * accordingly. BIT 16 enables write to BIT 0, BIT 17 enables write to + * BIT 1, and the default value of both BIT 0 and BIT 1 is 0. + */ + writel(BIT(17) | BIT(16), priv->base + PWC_GPIO); + bitmap_zero(priv->ch_en_bits, 2); + + priv->gp = rzv2m_pwc_gc; + priv->gp.parent = pdev->dev.parent; + priv->gp.fwnode = dev_fwnode(&pdev->dev); + + ret = devm_gpiochip_add_data(&pdev->dev, &priv->gp, priv); + if (ret) + return ret; + + if (device_property_read_bool(&pdev->dev, "renesas,rzv2m-pwc-power")) + ret = devm_register_power_off_handler(&pdev->dev, + rzv2m_pwc_poweroff, priv); + + return ret; +} + +static const struct of_device_id rzv2m_pwc_of_match[] = { + { .compatible = "renesas,rzv2m-pwc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzv2m_pwc_of_match); + +static struct platform_driver rzv2m_pwc_driver = { + .probe = rzv2m_pwc_probe, + .driver = { + .name = "rzv2m_pwc", + .of_match_table = rzv2m_pwc_of_match, + }, +}; +module_platform_driver(rzv2m_pwc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fabrizio Castro <castro.fabrizio.jz@renesas.com>"); +MODULE_DESCRIPTION("Renesas RZ/V2M PWC driver"); diff --git a/drivers/soc/renesas/r9a06g032-smp.c b/drivers/soc/renesas/r9a06g032-smp.c new file mode 100644 index 000000000..a1926e8d7 --- /dev/null +++ b/drivers/soc/renesas/r9a06g032-smp.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R9A06G032 Second CA7 enabler. + * + * Copyright (C) 2018 Renesas Electronics Europe Limited + * + * Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com> + * Derived from actions,s500-smp + */ + +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/smp.h> + +/* + * The second CPU is parked in ROM at boot time. It requires waking it after + * writing an address into the BOOTADDR register of sysctrl. + * + * So the default value of the "cpu-release-addr" corresponds to BOOTADDR... + * + * *However* the BOOTADDR register is not available when the kernel + * starts in NONSEC mode. + * + * So for NONSEC mode, the bootloader re-parks the second CPU into a pen + * in SRAM, and changes the "cpu-release-addr" of linux's DT to a SRAM address, + * which is not restricted. + */ + +static void __iomem *cpu_bootaddr; + +static DEFINE_SPINLOCK(cpu_lock); + +static int +r9a06g032_smp_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + if (!cpu_bootaddr) + return -ENODEV; + + spin_lock(&cpu_lock); + + writel(__pa_symbol(secondary_startup), cpu_bootaddr); + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + spin_unlock(&cpu_lock); + + return 0; +} + +static void __init r9a06g032_smp_prepare_cpus(unsigned int max_cpus) +{ + struct device_node *dn; + int ret = -EINVAL, dns; + u32 bootaddr; + + dn = of_get_cpu_node(1, NULL); + if (!dn) { + pr_err("CPU#1: missing device tree node\n"); + return; + } + /* + * Determine the address from which the CPU is polling. + * The bootloader *does* change this property. + * Note: The property can be either 64 or 32 bits, so handle both cases + */ + if (of_find_property(dn, "cpu-release-addr", &dns)) { + if (dns == sizeof(u64)) { + u64 temp; + + ret = of_property_read_u64(dn, + "cpu-release-addr", &temp); + bootaddr = temp; + } else { + ret = of_property_read_u32(dn, + "cpu-release-addr", + &bootaddr); + } + } + of_node_put(dn); + if (ret) { + pr_err("CPU#1: invalid cpu-release-addr property\n"); + return; + } + pr_info("CPU#1: cpu-release-addr %08x\n", bootaddr); + + cpu_bootaddr = ioremap(bootaddr, sizeof(bootaddr)); +} + +static const struct smp_operations r9a06g032_smp_ops __initconst = { + .smp_prepare_cpus = r9a06g032_smp_prepare_cpus, + .smp_boot_secondary = r9a06g032_smp_boot_secondary, +}; + +CPU_METHOD_OF_DECLARE(r9a06g032_smp, + "renesas,r9a06g032-smp", &r9a06g032_smp_ops); diff --git a/drivers/soc/renesas/r9a08g045-sysc.c b/drivers/soc/renesas/r9a08g045-sysc.c new file mode 100644 index 000000000..63e4aa6a7 --- /dev/null +++ b/drivers/soc/renesas/r9a08g045-sysc.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G3S System controller driver + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ + +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/init.h> + +#include "rz-sysc.h" + +#define SYS_XSPI_MAP_STAADD_CS0 0x348 +#define SYS_XSPI_MAP_ENDADD_CS0 0x34c +#define SYS_XSPI_MAP_STAADD_CS1 0x350 +#define SYS_XSPI_MAP_ENDADD_CS1 0x354 +#define SYS_GETH0_CFG 0x380 +#define SYS_GETH1_CFG 0x390 +#define SYS_PCIE_CFG 0x3a0 +#define SYS_PCIE_MON 0x3a4 +#define SYS_PCIE_ERR_MON 0x3ac +#define SYS_PCIE_PHY 0x3b4 +#define SYS_I2C0_CFG 0x400 +#define SYS_I2C1_CFG 0x410 +#define SYS_I2C2_CFG 0x420 +#define SYS_I2C3_CFG 0x430 +#define SYS_I3C_CFG 0x440 +#define SYS_USB_PWRRDY 0xd70 +#define SYS_PCIE_RST_RSM_B 0xd74 + +static const struct rz_sysc_soc_id_init_data rzg3s_sysc_soc_id_init_data __initconst = { + .family = "RZ/G3S", + .id = 0x85e0447, + .devid_offset = 0xa04, + .revision_mask = GENMASK(31, 28), + .specific_id_mask = GENMASK(27, 0), +}; + +static bool rzg3s_regmap_readable_writeable_reg(unsigned int reg) +{ + switch (reg) { + case SYS_XSPI_MAP_STAADD_CS0: + case SYS_XSPI_MAP_ENDADD_CS0: + case SYS_XSPI_MAP_STAADD_CS1: + case SYS_XSPI_MAP_ENDADD_CS1: + case SYS_PCIE_CFG: + case SYS_PCIE_PHY: + case SYS_I2C0_CFG: + case SYS_I2C1_CFG: + case SYS_I2C2_CFG: + case SYS_I2C3_CFG: + case SYS_I3C_CFG: + case SYS_USB_PWRRDY: + case SYS_PCIE_RST_RSM_B: + return true; + default: + return false; + } +} + +static bool rzg3s_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzg3s_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_GETH0_CFG: + case SYS_GETH1_CFG: + case SYS_PCIE_MON: + case SYS_PCIE_ERR_MON: + return true; + default: + return false; + } +} + +static bool rzg3s_regmap_writeable_reg(struct device *dev, unsigned int reg) +{ + return rzg3s_regmap_readable_writeable_reg(reg); +} + +const struct rz_sysc_init_data rzg3s_sysc_init_data __initconst = { + .soc_id_init_data = &rzg3s_sysc_soc_id_init_data, + .readable_reg = rzg3s_regmap_readable_reg, + .writeable_reg = rzg3s_regmap_writeable_reg, + .max_register = 0xe20, +}; diff --git a/drivers/soc/renesas/r9a08g046-sysc.c b/drivers/soc/renesas/r9a08g046-sysc.c new file mode 100644 index 000000000..90db9d383 --- /dev/null +++ b/drivers/soc/renesas/r9a08g046-sysc.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G3L System controller (SYSC) driver + * + * Copyright (C) 2026 Renesas Electronics Corp. + */ + +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/init.h> + +#include "rz-sysc.h" + +#define SYS_XSPI_MAP_STAADD_CS0 0x348 +#define SYS_XSPI_MAP_ENDADD_CS0 0x34c +#define SYS_XSPI_MAP_STAADD_CS1 0x350 +#define SYS_XSPI_MAP_ENDADD_CS1 0x354 +#define SYS_GETH0_CFG 0x380 +#define SYS_GETH1_CFG 0x390 +#define SYS_PCIE_CFG 0x3a0 +#define SYS_PCIE_MON 0x3a4 +#define SYS_PCIE_PHY 0x3b4 +#define SYS_I2C0_CFG 0x400 +#define SYS_I2C1_CFG 0x410 +#define SYS_I2C2_CFG 0x420 +#define SYS_I2C3_CFG 0x430 +#define SYS_I3C_CFG 0x440 +#define SYS_PWRRDY_N 0xd70 +#define SYS_IPCONT_SEL_CLONECH 0xe2c + +static bool rzg3l_regmap_readable_writeable_reg(unsigned int reg) +{ + switch (reg) { + case SYS_XSPI_MAP_STAADD_CS0: + case SYS_XSPI_MAP_ENDADD_CS0: + case SYS_XSPI_MAP_STAADD_CS1: + case SYS_XSPI_MAP_ENDADD_CS1: + case SYS_PCIE_CFG: + case SYS_PCIE_PHY: + case SYS_I2C0_CFG: + case SYS_I2C1_CFG: + case SYS_I2C2_CFG: + case SYS_I2C3_CFG: + case SYS_I3C_CFG: + case SYS_PWRRDY_N: + case SYS_IPCONT_SEL_CLONECH: + return true; + default: + return false; + } +} + +static bool rzg3l_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzg3l_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_GETH0_CFG: + case SYS_GETH1_CFG: + case SYS_PCIE_MON: + return true; + default: + return false; + } +} + +static bool rzg3l_regmap_writeable_reg(struct device *dev, unsigned int reg) +{ + return rzg3l_regmap_readable_writeable_reg(reg); +} + +static const struct rz_sysc_soc_id_init_data rzg3l_sysc_soc_id_init_data __initconst = { + .family = "RZ/G3L", + .id = 0x87d9447, + .devid_offset = 0xa04, + .revision_mask = GENMASK(31, 28), + .specific_id_mask = GENMASK(27, 0), +}; + +const struct rz_sysc_init_data rzg3l_sysc_init_data __initconst = { + .soc_id_init_data = &rzg3l_sysc_soc_id_init_data, + .readable_reg = rzg3l_regmap_readable_reg, + .writeable_reg = rzg3l_regmap_writeable_reg, + .max_register = 0xe2c, +}; diff --git a/drivers/soc/renesas/r9a09g047-sys.c b/drivers/soc/renesas/r9a09g047-sys.c new file mode 100644 index 000000000..b617fb0bd --- /dev/null +++ b/drivers/soc/renesas/r9a09g047-sys.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G3E System controller (SYS) driver + * + * Copyright (C) 2025 Renesas Electronics Corp. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> + +#include "rz-sysc.h" + +/* Register Offsets */ +#define SYS_LSI_MODE 0x300 +/* + * BOOTPLLCA[1:0] + * [0,0] => 1.1GHZ + * [0,1] => 1.5GHZ + * [1,0] => 1.6GHZ + * [1,1] => 1.7GHZ + */ +#define SYS_LSI_MODE_STAT_BOOTPLLCA55 GENMASK(12, 11) +#define SYS_LSI_MODE_CA55_1_7GHZ 0x3 + +#define SYS_LSI_PRR 0x308 +#define SYS_LSI_PRR_CA55_DIS BIT(8) +#define SYS_LSI_PRR_NPU_DIS BIT(1) + +#define SYS_LSI_OTPTSU1TRMVAL0 0x330 +#define SYS_LSI_OTPTSU1TRMVAL1 0x334 +#define SYS_SPI_STAADDCS0 0x900 +#define SYS_SPI_ENDADDCS0 0x904 +#define SYS_SPI_STAADDCS1 0x908 +#define SYS_SPI_ENDADDCS1 0x90c +#define SYS_VSP_CLK 0xe00 +#define SYS_GBETH0_CFG 0xf00 +#define SYS_GBETH1_CFG 0xf04 +#define SYS_PCIE_INTX_CH0 0x1000 +#define SYS_PCIE_MSI1_CH0 0x1004 +#define SYS_PCIE_MSI2_CH0 0x1008 +#define SYS_PCIE_MSI3_CH0 0x100c +#define SYS_PCIE_MSI4_CH0 0x1010 +#define SYS_PCIE_MSI5_CH0 0x1014 +#define SYS_PCIE_PME_CH0 0x1018 +#define SYS_PCIE_ACK_CH0 0x101c +#define SYS_PCIE_MISC_CH0 0x1020 +#define SYS_PCIE_MODE_CH0 0x1024 +#define SYS_ADC_CFG 0x1600 + +static void rzg3e_sys_print_id(struct device *dev, + void __iomem *sysc_base, + struct soc_device_attribute *soc_dev_attr) +{ + bool is_quad_core, npu_enabled; + u32 prr_val, mode_val; + + prr_val = readl(sysc_base + SYS_LSI_PRR); + mode_val = readl(sysc_base + SYS_LSI_MODE); + + /* Check CPU and NPU configuration */ + is_quad_core = !(prr_val & SYS_LSI_PRR_CA55_DIS); + npu_enabled = !(prr_val & SYS_LSI_PRR_NPU_DIS); + + dev_info(dev, "Detected Renesas %s Core %s %s Rev %s%s\n", + is_quad_core ? "Quad" : "Dual", soc_dev_attr->family, + soc_dev_attr->soc_id, soc_dev_attr->revision, + npu_enabled ? " with Ethos-U55" : ""); + + /* Check CA55 PLL configuration */ + if (FIELD_GET(SYS_LSI_MODE_STAT_BOOTPLLCA55, mode_val) != SYS_LSI_MODE_CA55_1_7GHZ) + dev_warn(dev, "CA55 PLL is not set to 1.7GHz\n"); +} + +static const struct rz_sysc_soc_id_init_data rzg3e_sys_soc_id_init_data __initconst = { + .family = "RZ/G3E", + .id = 0x8679447, + .devid_offset = 0x304, + .revision_mask = GENMASK(31, 28), + .specific_id_mask = GENMASK(27, 0), + .print_id = rzg3e_sys_print_id, +}; + +static bool rzg3e_regmap_readable_writeable_reg(unsigned int reg) +{ + switch (reg) { + case SYS_SPI_STAADDCS0: + case SYS_SPI_ENDADDCS0: + case SYS_SPI_STAADDCS1: + case SYS_SPI_ENDADDCS1: + case SYS_VSP_CLK: + case SYS_GBETH0_CFG: + case SYS_GBETH1_CFG: + case SYS_PCIE_INTX_CH0: + case SYS_PCIE_MSI1_CH0: + case SYS_PCIE_MSI2_CH0: + case SYS_PCIE_MSI3_CH0: + case SYS_PCIE_MSI4_CH0: + case SYS_PCIE_MSI5_CH0: + case SYS_PCIE_PME_CH0: + case SYS_PCIE_ACK_CH0: + case SYS_PCIE_MISC_CH0: + case SYS_PCIE_MODE_CH0: + case SYS_ADC_CFG: + return true; + default: + return false; + } +} + +static bool rzg3e_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzg3e_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_LSI_OTPTSU1TRMVAL0: + case SYS_LSI_OTPTSU1TRMVAL1: + return true; + default: + return false; + } +} + +static bool rzg3e_regmap_writeable_reg(struct device *dev, unsigned int reg) +{ + return rzg3e_regmap_readable_writeable_reg(reg); +} + +const struct rz_sysc_init_data rzg3e_sys_init_data __initconst = { + .soc_id_init_data = &rzg3e_sys_soc_id_init_data, + .readable_reg = rzg3e_regmap_readable_reg, + .writeable_reg = rzg3e_regmap_writeable_reg, + .max_register = 0x170c, +}; diff --git a/drivers/soc/renesas/r9a09g056-sys.c b/drivers/soc/renesas/r9a09g056-sys.c new file mode 100644 index 000000000..e6a666884 --- /dev/null +++ b/drivers/soc/renesas/r9a09g056-sys.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/V2N System controller (SYS) driver + * + * Copyright (C) 2025 Renesas Electronics Corp. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> + +#include "rz-sysc.h" + +/* Register Offsets */ +#define SYS_LSI_MODE 0x300 +#define SYS_LSI_MODE_SEC_EN BIT(16) +/* + * BOOTPLLCA[1:0] + * [0,0] => 1.1GHZ + * [0,1] => 1.5GHZ + * [1,0] => 1.6GHZ + * [1,1] => 1.7GHZ + */ +#define SYS_LSI_MODE_STAT_BOOTPLLCA55 GENMASK(12, 11) +#define SYS_LSI_MODE_CA55_1_7GHZ 0x3 + +#define SYS_LSI_PRR 0x308 +#define SYS_LSI_PRR_GPU_DIS BIT(0) +#define SYS_LSI_PRR_ISP_DIS BIT(4) + +#define SYS_RZV2N_FEATURE_G31 BIT(0) +#define SYS_RZV2N_FEATURE_C55 BIT(1) +#define SYS_RZV2N_FEATURE_SEC BIT(2) + +#define SYS_LSI_OTPTSU0TRMVAL0 0x320 +#define SYS_LSI_OTPTSU0TRMVAL1 0x324 +#define SYS_LSI_OTPTSU1TRMVAL0 0x330 +#define SYS_LSI_OTPTSU1TRMVAL1 0x334 +#define SYS_GBETH0_CFG 0xf00 +#define SYS_GBETH1_CFG 0xf04 +#define SYS_PCIE_INTX_CH0 0x1000 +#define SYS_PCIE_MSI1_CH0 0x1004 +#define SYS_PCIE_MSI2_CH0 0x1008 +#define SYS_PCIE_MSI3_CH0 0x100c +#define SYS_PCIE_MSI4_CH0 0x1010 +#define SYS_PCIE_MSI5_CH0 0x1014 +#define SYS_PCIE_PME_CH0 0x1018 +#define SYS_PCIE_ACK_CH0 0x101c +#define SYS_PCIE_MISC_CH0 0x1020 +#define SYS_PCIE_MODE_CH0 0x1024 +#define SYS_ADC_CFG 0x1600 + +static void rzv2n_sys_print_id(struct device *dev, + void __iomem *sysc_base, + struct soc_device_attribute *soc_dev_attr) +{ + u32 prr_val, mode_val; + u8 feature_flags; + + prr_val = readl(sysc_base + SYS_LSI_PRR); + mode_val = readl(sysc_base + SYS_LSI_MODE); + + /* Check GPU, ISP and Cryptographic configuration */ + feature_flags = !(prr_val & SYS_LSI_PRR_GPU_DIS) ? SYS_RZV2N_FEATURE_G31 : 0; + feature_flags |= !(prr_val & SYS_LSI_PRR_ISP_DIS) ? SYS_RZV2N_FEATURE_C55 : 0; + feature_flags |= (mode_val & SYS_LSI_MODE_SEC_EN) ? SYS_RZV2N_FEATURE_SEC : 0; + + dev_info(dev, "Detected Renesas %s %sn%d Rev %s%s%s%s%s\n", soc_dev_attr->family, + soc_dev_attr->soc_id, 41 + feature_flags, soc_dev_attr->revision, + feature_flags ? " with" : "", + feature_flags & SYS_RZV2N_FEATURE_G31 ? " GE3D (Mali-G31)" : "", + feature_flags & SYS_RZV2N_FEATURE_SEC ? " Cryptographic engine" : "", + feature_flags & SYS_RZV2N_FEATURE_C55 ? " ISP (Mali-C55)" : ""); + + /* Check CA55 PLL configuration */ + if (FIELD_GET(SYS_LSI_MODE_STAT_BOOTPLLCA55, mode_val) != SYS_LSI_MODE_CA55_1_7GHZ) + dev_warn(dev, "CA55 PLL is not set to 1.7GHz\n"); +} + +static const struct rz_sysc_soc_id_init_data rzv2n_sys_soc_id_init_data __initconst = { + .family = "RZ/V2N", + .id = 0x867d447, + .devid_offset = 0x304, + .revision_mask = GENMASK(31, 28), + .specific_id_mask = GENMASK(27, 0), + .print_id = rzv2n_sys_print_id, +}; + +static bool rzv2n_regmap_readable_writeable_reg(unsigned int reg) +{ + switch (reg) { + case SYS_GBETH0_CFG: + case SYS_GBETH1_CFG: + case SYS_PCIE_INTX_CH0: + case SYS_PCIE_MSI1_CH0: + case SYS_PCIE_MSI2_CH0: + case SYS_PCIE_MSI3_CH0: + case SYS_PCIE_MSI4_CH0: + case SYS_PCIE_MSI5_CH0: + case SYS_PCIE_PME_CH0: + case SYS_PCIE_ACK_CH0: + case SYS_PCIE_MISC_CH0: + case SYS_PCIE_MODE_CH0: + case SYS_ADC_CFG: + return true; + default: + return false; + } +} + +static bool rzv2n_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzv2n_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_LSI_OTPTSU0TRMVAL0: + case SYS_LSI_OTPTSU0TRMVAL1: + case SYS_LSI_OTPTSU1TRMVAL0: + case SYS_LSI_OTPTSU1TRMVAL1: + return true; + default: + return false; + } +} + +static bool rzv2n_regmap_writeable_reg(struct device *dev, unsigned int reg) +{ + return rzv2n_regmap_readable_writeable_reg(reg); +} + +const struct rz_sysc_init_data rzv2n_sys_init_data __initconst = { + .soc_id_init_data = &rzv2n_sys_soc_id_init_data, + .readable_reg = rzv2n_regmap_readable_reg, + .writeable_reg = rzv2n_regmap_writeable_reg, + .max_register = 0x170c, +}; diff --git a/drivers/soc/renesas/r9a09g057-sys.c b/drivers/soc/renesas/r9a09g057-sys.c new file mode 100644 index 000000000..308492c31 --- /dev/null +++ b/drivers/soc/renesas/r9a09g057-sys.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/V2H System controller (SYS) driver + * + * Copyright (C) 2025 Renesas Electronics Corp. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> + +#include "rz-sysc.h" + +/* Register Offsets */ +#define SYS_LSI_MODE 0x300 +/* + * BOOTPLLCA[1:0] + * [0,0] => 1.1GHZ + * [0,1] => 1.5GHZ + * [1,0] => 1.6GHZ + * [1,1] => 1.7GHZ + */ +#define SYS_LSI_MODE_STAT_BOOTPLLCA55 GENMASK(12, 11) +#define SYS_LSI_MODE_CA55_1_7GHZ 0x3 + +#define SYS_LSI_PRR 0x308 +#define SYS_LSI_PRR_GPU_DIS BIT(0) +#define SYS_LSI_PRR_ISP_DIS BIT(4) + +#define SYS_LSI_OTPTSU0TRMVAL0 0x320 +#define SYS_LSI_OTPTSU0TRMVAL1 0x324 +#define SYS_LSI_OTPTSU1TRMVAL0 0x330 +#define SYS_LSI_OTPTSU1TRMVAL1 0x334 +#define SYS_GBETH0_CFG 0xf00 +#define SYS_GBETH1_CFG 0xf04 +#define SYS_PCIE_INTX_CH0 0x1000 +#define SYS_PCIE_MSI1_CH0 0x1004 +#define SYS_PCIE_MSI2_CH0 0x1008 +#define SYS_PCIE_MSI3_CH0 0x100c +#define SYS_PCIE_MSI4_CH0 0x1010 +#define SYS_PCIE_MSI5_CH0 0x1014 +#define SYS_PCIE_PME_CH0 0x1018 +#define SYS_PCIE_ACK_CH0 0x101c +#define SYS_PCIE_MISC_CH0 0x1020 +#define SYS_PCIE_MODE_CH0 0x1024 +#define SYS_PCIE_INTX_CH1 0x1030 +#define SYS_PCIE_MSI1_CH1 0x1034 +#define SYS_PCIE_MSI2_CH1 0x1038 +#define SYS_PCIE_MSI3_CH1 0x103c +#define SYS_PCIE_MSI4_CH1 0x1040 +#define SYS_PCIE_MSI5_CH1 0x1044 +#define SYS_PCIE_PME_CH1 0x1048 +#define SYS_PCIE_ACK_CH1 0x104c +#define SYS_PCIE_MISC_CH1 0x1050 +#define SYS_PCIE_MODE_CH1 0x1054 +#define SYS_PCIE_MODE 0x1060 +#define SYS_ADC_CFG 0x1600 + +static void rzv2h_sys_print_id(struct device *dev, + void __iomem *sysc_base, + struct soc_device_attribute *soc_dev_attr) +{ + bool gpu_enabled, isp_enabled; + u32 prr_val, mode_val; + + prr_val = readl(sysc_base + SYS_LSI_PRR); + mode_val = readl(sysc_base + SYS_LSI_MODE); + + /* Check GPU and ISP configuration */ + gpu_enabled = !(prr_val & SYS_LSI_PRR_GPU_DIS); + isp_enabled = !(prr_val & SYS_LSI_PRR_ISP_DIS); + + dev_info(dev, "Detected Renesas %s %s Rev %s%s%s\n", + soc_dev_attr->family, soc_dev_attr->soc_id, soc_dev_attr->revision, + gpu_enabled ? " with GE3D (Mali-G31)" : "", + isp_enabled ? " with ISP (Mali-C55)" : ""); + + /* Check CA55 PLL configuration */ + if (FIELD_GET(SYS_LSI_MODE_STAT_BOOTPLLCA55, mode_val) != SYS_LSI_MODE_CA55_1_7GHZ) + dev_warn(dev, "CA55 PLL is not set to 1.7GHz\n"); +} + +static const struct rz_sysc_soc_id_init_data rzv2h_sys_soc_id_init_data __initconst = { + .family = "RZ/V2H", + .id = 0x847a447, + .devid_offset = 0x304, + .revision_mask = GENMASK(31, 28), + .specific_id_mask = GENMASK(27, 0), + .print_id = rzv2h_sys_print_id, +}; + +static bool rzv2h_regmap_readable_writeable_reg(unsigned int reg) +{ + switch (reg) { + case SYS_GBETH0_CFG: + case SYS_GBETH1_CFG: + case SYS_PCIE_INTX_CH0: + case SYS_PCIE_MSI1_CH0: + case SYS_PCIE_MSI2_CH0: + case SYS_PCIE_MSI3_CH0: + case SYS_PCIE_MSI4_CH0: + case SYS_PCIE_MSI5_CH0: + case SYS_PCIE_PME_CH0: + case SYS_PCIE_ACK_CH0: + case SYS_PCIE_MISC_CH0: + case SYS_PCIE_MODE_CH0: + case SYS_PCIE_INTX_CH1: + case SYS_PCIE_MSI1_CH1: + case SYS_PCIE_MSI2_CH1: + case SYS_PCIE_MSI3_CH1: + case SYS_PCIE_MSI4_CH1: + case SYS_PCIE_MSI5_CH1: + case SYS_PCIE_PME_CH1: + case SYS_PCIE_ACK_CH1: + case SYS_PCIE_MISC_CH1: + case SYS_PCIE_MODE_CH1: + case SYS_PCIE_MODE: + case SYS_ADC_CFG: + return true; + default: + return false; + } +} + +static bool rzv2h_regmap_readable_reg(struct device *dev, unsigned int reg) +{ + if (rzv2h_regmap_readable_writeable_reg(reg)) + return true; + + switch (reg) { + case SYS_LSI_OTPTSU0TRMVAL0: + case SYS_LSI_OTPTSU0TRMVAL1: + case SYS_LSI_OTPTSU1TRMVAL0: + case SYS_LSI_OTPTSU1TRMVAL1: + return true; + default: + return false; + } +} + +static bool rzv2h_regmap_writeable_reg(struct device *dev, unsigned int reg) +{ + return rzv2h_regmap_readable_writeable_reg(reg); +} + +const struct rz_sysc_init_data rzv2h_sys_init_data __initconst = { + .soc_id_init_data = &rzv2h_sys_soc_id_init_data, + .readable_reg = rzv2h_regmap_readable_reg, + .writeable_reg = rzv2h_regmap_writeable_reg, + .max_register = 0x170c, +}; diff --git a/drivers/soc/renesas/rcar-mfis.c b/drivers/soc/renesas/rcar-mfis.c new file mode 100644 index 000000000..3435c3e16 --- /dev/null +++ b/drivers/soc/renesas/rcar-mfis.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Renesas R-Car MFIS (Multifunctional Interface) driver + * + * Copyright (C) Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * Wolfram Sang <wsa+renesas@sang-engineering.com> + */ +#include <dt-bindings/soc/renesas,r8a78000-mfis.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mailbox_controller.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +#define MFISWPCNTR 0x0900 +#define MFISWACNTR 0x0904 + +#define MFIS_X5H_IICR(i) ((i) * 0x1000 + 0x00) +#define MFIS_X5H_EICR(i) ((i) * 0x1000 + 0x04) + +#define MFIS_UNPROTECT_KEY 0xACCE0000 + +struct mfis_priv; + +struct mfis_reg { + void __iomem *base; + resource_size_t start; + struct mfis_priv *priv; +}; + +struct mfis_info { + u32 unprotect_mask; + unsigned int mb_num_channels; + unsigned int mb_reg_comes_from_dt:1; + unsigned int mb_tx_uses_eicr:1; + unsigned int mb_channels_are_unidir:1; + u32 (*mb_calc_reg)(u32 chan_num, bool tx_uses_eicr, bool is_only_rx); +}; + +struct mfis_chan_priv { + u32 reg; + int irq; +}; + +struct mfis_priv { + spinlock_t unprotect_lock; /* guards access to the unprotection reg */ + struct device *dev; + struct mfis_reg common_reg; + struct mfis_reg mbox_reg; + const struct mfis_info *info; + + /* mailbox private data */ + struct mbox_controller mbox; + struct mfis_chan_priv *chan_privs; +}; + +static u32 mfis_read(struct mfis_reg *mreg, unsigned int reg) +{ + return ioread32(mreg->base + reg); +} + +static void mfis_write(struct mfis_reg *mreg, u32 reg, u32 val) +{ + struct mfis_priv *priv = mreg->priv; + u32 unprotect_mask = priv->info->unprotect_mask; + unsigned long flags; + u32 unprotect_code; + + /* + * [Gen4] key: 0xACCE0000, mask: 0x0000FFFF + * [Gen5] key: 0xACC00000, mask: 0x000FFFFF + */ + unprotect_code = (MFIS_UNPROTECT_KEY & ~unprotect_mask) | + ((mreg->start + reg) & unprotect_mask); + + spin_lock_irqsave(&priv->unprotect_lock, flags); + iowrite32(unprotect_code, priv->common_reg.base + MFISWACNTR); + iowrite32(val, mreg->base + reg); + spin_unlock_irqrestore(&priv->unprotect_lock, flags); +} + +/******************************************************** + * Mailbox * + ********************************************************/ + +#define mfis_mb_mbox_to_priv(_m) container_of((_m), struct mfis_priv, mbox) + +static irqreturn_t mfis_mb_iicr_interrupt(int irq, void *data) +{ + struct mbox_chan *chan = data; + struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox); + struct mfis_chan_priv *chan_priv = chan->con_priv; + + mbox_chan_received_data(chan, NULL); + /* Stop remote(!) doorbell */ + mfis_write(&priv->mbox_reg, chan_priv->reg, 0); + + return IRQ_HANDLED; +} + +static int mfis_mb_startup(struct mbox_chan *chan) +{ + struct mfis_chan_priv *chan_priv = chan->con_priv; + + if (!chan_priv->irq) + return 0; + + return request_irq(chan_priv->irq, mfis_mb_iicr_interrupt, 0, + dev_name(chan->mbox->dev), chan); +} + +static void mfis_mb_shutdown(struct mbox_chan *chan) +{ + struct mfis_chan_priv *chan_priv = chan->con_priv; + + if (chan_priv->irq) + free_irq(chan_priv->irq, chan); +} + +static int mfis_mb_iicr_send_data(struct mbox_chan *chan, void *data) +{ + struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox); + struct mfis_chan_priv *chan_priv = chan->con_priv; + + /* Our doorbell still active? */ + if (mfis_read(&priv->mbox_reg, chan_priv->reg) & BIT(0)) + return -EBUSY; + + /* Start our doorbell */ + mfis_write(&priv->mbox_reg, chan_priv->reg, BIT(0)); + + return 0; +} + +static bool mfis_mb_iicr_last_tx_done(struct mbox_chan *chan) +{ + struct mfis_priv *priv = mfis_mb_mbox_to_priv(chan->mbox); + struct mfis_chan_priv *chan_priv = chan->con_priv; + + /* Our doorbell still active? */ + return !(mfis_read(&priv->mbox_reg, chan_priv->reg) & BIT(0)); +} + +/* For MFIS variants using the IICR/EICR register pair */ +static const struct mbox_chan_ops mfis_iicr_ops = { + .startup = mfis_mb_startup, + .shutdown = mfis_mb_shutdown, + .send_data = mfis_mb_iicr_send_data, + .last_tx_done = mfis_mb_iicr_last_tx_done, +}; + +static u32 mfis_mb_r8a779g0_calc_reg(u32 chan_num, bool tx_uses_eicr, bool is_only_rx) +{ + unsigned int i, k; + u32 reg; + + i = chan_num & 3; + k = chan_num >> 2; + + if (is_only_rx) { + if (k < 2) + reg = 0x9404 + 0x1020 * k + 0x08 * i; + else + reg = 0xb504 + 0x08 * i; + } else { + if (k < 2) + reg = 0x1400 + 0x1008 * i + 0x20 * k; + else + reg = 0x1500 + 0x1008 * i; + } + + return reg; +} + +static u32 mfis_mb_r8a78000_calc_reg(u32 chan_num, bool tx_uses_eicr, bool is_only_rx) +{ + return (tx_uses_eicr ^ is_only_rx) ? MFIS_X5H_EICR(chan_num) : + MFIS_X5H_IICR(chan_num); +} + +static struct mbox_chan *mfis_mb_of_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + struct mfis_priv *priv = mfis_mb_mbox_to_priv(mbox); + struct mfis_chan_priv *chan_priv; + bool tx_uses_eicr, is_only_rx; + u32 chan_num, chan_flags; + struct mbox_chan *chan; + + if (sp->args_count != 2) + return ERR_PTR(-EINVAL); + + chan_num = sp->args[0]; + chan_flags = sp->args[1]; + + if (chan_num >= priv->info->mb_num_channels) + return ERR_PTR(-EINVAL); + + /* Channel layout is described in mfis_mb_probe() */ + if (priv->info->mb_channels_are_unidir) { + is_only_rx = chan_flags & MFIS_CHANNEL_RX; + chan = mbox->chans + 2 * chan_num + is_only_rx; + } else { + is_only_rx = false; + chan = mbox->chans + chan_num; + } + + if (priv->info->mb_reg_comes_from_dt) { + tx_uses_eicr = chan_flags & MFIS_CHANNEL_EICR; + if (tx_uses_eicr) + chan += mbox->num_chans / 2; + } else { + tx_uses_eicr = priv->info->mb_tx_uses_eicr; + } + + chan_priv = chan->con_priv; + chan_priv->reg = priv->info->mb_calc_reg(chan_num, tx_uses_eicr, is_only_rx); + + if (!priv->info->mb_channels_are_unidir || is_only_rx) { + char irqname[8]; + char suffix = tx_uses_eicr ? 'i' : 'e'; + + /* "ch0i" or "ch0e" */ + scnprintf(irqname, sizeof(irqname), "ch%u%c", chan_num, suffix); + + chan_priv->irq = of_irq_get_byname(mbox->dev->of_node, irqname); + if (chan_priv->irq < 0) + return ERR_PTR(chan_priv->irq); + if (chan_priv->irq == 0) + return ERR_PTR(-ENOENT); + } + + return chan; +} + +static int mfis_mb_probe(struct mfis_priv *priv) +{ + unsigned int num_chan = priv->info->mb_num_channels; + struct device *dev = priv->dev; + struct mbox_controller *mbox; + struct mbox_chan *chan; + + if (priv->info->mb_channels_are_unidir) { + /* Channel layout: Ch0-TX, Ch0-RX, Ch1-TX... */ + num_chan *= 2; + } + + if (priv->info->mb_reg_comes_from_dt) { + /* Channel layout: <n> IICR channels, <n> EICR channels */ + num_chan *= 2; + } + + chan = devm_kcalloc(dev, num_chan, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + priv->chan_privs = devm_kcalloc(dev, num_chan, sizeof(*priv->chan_privs), + GFP_KERNEL); + if (!priv->chan_privs) + return -ENOMEM; + + mbox = &priv->mbox; + + for (unsigned int i = 0; i < num_chan; i++) + chan[i].con_priv = &priv->chan_privs[i]; + + mbox->chans = chan; + mbox->num_chans = num_chan; + mbox->txdone_poll = true; + mbox->ops = &mfis_iicr_ops; + mbox->dev = dev; + mbox->of_xlate = mfis_mb_of_xlate; + + return devm_mbox_controller_register(dev, mbox); +} + +/******************************************************** + * Common * + ********************************************************/ +static int mfis_reg_probe(struct platform_device *pdev, struct mfis_priv *priv, + struct mfis_reg *mreg, const char *name, bool required) +{ + struct resource *res; + void __iomem *base; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + + /* If there is no mailbox resource, registers are in the common space */ + if (!res && !required) { + *mreg = priv->common_reg; + } else { + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mreg->base = base; + mreg->start = res->start; + mreg->priv = priv; + } + + return 0; +} + +static int mfis_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mfis_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->info = of_device_get_match_data(dev); + if (!priv->info) + return -ENOENT; + + spin_lock_init(&priv->unprotect_lock); + + ret = mfis_reg_probe(pdev, priv, &priv->common_reg, "common", true); + if (ret) + return ret; + + ret = mfis_reg_probe(pdev, priv, &priv->mbox_reg, "mboxes", false); + if (ret) + return ret; + + return mfis_mb_probe(priv); +} + +static const struct mfis_info mfis_info_r8a779g0 = { + .unprotect_mask = 0x0000ffff, + .mb_num_channels = 12, + .mb_channels_are_unidir = true, + .mb_calc_reg = mfis_mb_r8a779g0_calc_reg, +}; + +static const struct mfis_info mfis_info_r8a78000 = { + .unprotect_mask = 0x000fffff, + .mb_num_channels = 64, + .mb_reg_comes_from_dt = true, + .mb_channels_are_unidir = true, + .mb_calc_reg = mfis_mb_r8a78000_calc_reg, +}; + +static const struct mfis_info mfis_info_r8a78000_scp = { + .unprotect_mask = 0x000fffff, + .mb_num_channels = 32, + .mb_tx_uses_eicr = true, + .mb_channels_are_unidir = true, + .mb_calc_reg = mfis_mb_r8a78000_calc_reg, +}; + +static const struct of_device_id mfis_mfd_of_match[] = { + { .compatible = "renesas,r8a779g0-mfis", .data = &mfis_info_r8a779g0, }, + { .compatible = "renesas,r8a779h0-mfis", .data = &mfis_info_r8a779g0, }, + { .compatible = "renesas,r8a78000-mfis", .data = &mfis_info_r8a78000, }, + { .compatible = "renesas,r8a78000-mfis-scp", .data = &mfis_info_r8a78000_scp, }, + {} +}; +MODULE_DEVICE_TABLE(of, mfis_mfd_of_match); + +static struct platform_driver mfis_driver = { + .driver = { + .name = "rcar-mfis", + .of_match_table = mfis_mfd_of_match, + .suppress_bind_attrs = true, + }, + .probe = mfis_probe, +}; +module_platform_driver(mfis_driver); + +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); +MODULE_AUTHOR("Wolfram Sang <wsa+renesas@sang-engineering.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car MFIS driver"); diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c new file mode 100644 index 000000000..054199090 --- /dev/null +++ b/drivers/soc/renesas/rcar-rst.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car Gen1 RESET/WDT, R-Car Gen2, Gen3, and RZ/G RST Driver + * + * Copyright (C) 2016 Glider bvba + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/soc/renesas/rcar-rst.h> + +#define WDTRSTCR_RESET 0xA55A0002 +#define WDTRSTCR 0x0054 +#define GEN4_WDTRSTCR_RESET 0xA55A8002 +#define GEN4_WDTRSTCR 0x0010 + +#define CR7BAR 0x0070 +#define CR7BAREN BIT(4) +#define CR7BAR_MASK 0xFFFC0000 + +static void __iomem *rcar_rst_base; +static u32 saved_mode __initdata; +static int (*rcar_rst_set_rproc_boot_addr_func)(u64 boot_addr); + +static int rcar_rst_enable_wdt_reset(void __iomem *base) +{ + iowrite32(WDTRSTCR_RESET, base + WDTRSTCR); + return 0; +} + +static int rcar_rst_v3u_enable_wdt_reset(void __iomem *base) +{ + iowrite32(GEN4_WDTRSTCR_RESET, base + GEN4_WDTRSTCR); + return 0; +} + +/* + * Most of the R-Car Gen3 SoCs have an ARM Realtime Core. + * Firmware boot address has to be set in CR7BAR before + * starting the realtime core. + * Boot address must be aligned on a 256k boundary. + */ +static int rcar_rst_set_gen3_rproc_boot_addr(u64 boot_addr) +{ + if (boot_addr & ~(u64)CR7BAR_MASK) { + pr_err("Invalid boot address got %llx\n", boot_addr); + return -EINVAL; + } + + iowrite32(boot_addr, rcar_rst_base + CR7BAR); + iowrite32(boot_addr | CR7BAREN, rcar_rst_base + CR7BAR); + + return 0; +} + +struct rst_config { + unsigned int modemr; /* Mode Monitoring Register Offset */ + int (*configure)(void __iomem *base); /* Platform specific config */ + int (*set_rproc_boot_addr)(u64 boot_addr); +}; + +static const struct rst_config rcar_rst_gen1 __initconst = { + .modemr = 0x20, +}; + +static const struct rst_config rcar_rst_gen2 __initconst = { + .modemr = 0x60, + .configure = rcar_rst_enable_wdt_reset, +}; + +static const struct rst_config rcar_rst_gen3 __initconst = { + .modemr = 0x60, + .set_rproc_boot_addr = rcar_rst_set_gen3_rproc_boot_addr, +}; + +/* V3U firmware doesn't enable WDT reset and there won't be updates anymore */ +static const struct rst_config rcar_rst_v3u __initconst = { + .modemr = 0x00, /* MODEMR0 and it has CPG related bits */ + .configure = rcar_rst_v3u_enable_wdt_reset, +}; + +static const struct rst_config rcar_rst_gen4 __initconst = { + .modemr = 0x00, /* MODEMR0 and it has CPG related bits */ +}; + +static const struct of_device_id rcar_rst_matches[] __initconst = { + /* RZ/G1 is handled like R-Car Gen2 */ + { .compatible = "renesas,r8a7742-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7743-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7744-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7745-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 }, + /* RZ/G2 is handled like R-Car Gen3 */ + { .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a774e1-rst", .data = &rcar_rst_gen3 }, + /* R-Car Gen1 */ + { .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 }, + { .compatible = "renesas,r8a7779-reset-wdt", .data = &rcar_rst_gen1 }, + /* R-Car Gen2 */ + { .compatible = "renesas,r8a7790-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7791-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7792-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7793-rst", .data = &rcar_rst_gen2 }, + { .compatible = "renesas,r8a7794-rst", .data = &rcar_rst_gen2 }, + /* R-Car Gen3 */ + { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77961-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77990-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 }, + /* R-Car Gen4 */ + { .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u }, + { .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 }, + { .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 }, + { .compatible = "renesas,r8a779h0-rst", .data = &rcar_rst_gen4 }, + { /* sentinel */ } +}; + +static int __init rcar_rst_init(void) +{ + const struct of_device_id *match; + const struct rst_config *cfg; + struct device_node *np; + void __iomem *base; + int error = 0; + + np = of_find_matching_node_and_match(NULL, rcar_rst_matches, &match); + if (!np) + return -ENODEV; + + base = of_iomap(np, 0); + if (!base) { + pr_warn("%pOF: Cannot map regs\n", np); + error = -ENOMEM; + goto out_put; + } + + rcar_rst_base = base; + cfg = match->data; + rcar_rst_set_rproc_boot_addr_func = cfg->set_rproc_boot_addr; + + saved_mode = ioread32(base + cfg->modemr); + if (cfg->configure) { + error = cfg->configure(base); + if (error) { + pr_warn("%pOF: Cannot run SoC specific configuration\n", + np); + goto out_put; + } + } + + pr_debug("%pOF: MODE = 0x%08x\n", np, saved_mode); + +out_put: + of_node_put(np); + return error; +} + +int __init rcar_rst_read_mode_pins(u32 *mode) +{ + int error; + + if (!rcar_rst_base) { + error = rcar_rst_init(); + if (error) + return error; + } + + *mode = saved_mode; + return 0; +} + +int rcar_rst_set_rproc_boot_addr(u64 boot_addr) +{ + if (!rcar_rst_set_rproc_boot_addr_func) + return -EIO; + + return rcar_rst_set_rproc_boot_addr_func(boot_addr); +} +EXPORT_SYMBOL_GPL(rcar_rst_set_rproc_boot_addr); diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c new file mode 100644 index 000000000..18acca730 --- /dev/null +++ b/drivers/soc/renesas/renesas-soc.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas SoC Identification + * + * Copyright (C) 2014-2016 Glider bvba + */ + +#include <linux/bitfield.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/sys_soc.h> + +struct renesas_family { + const char name[16]; + u32 reg; /* CCCR or PRR, if not in DT */ +}; + +static const struct renesas_family fam_rcar_gen1 __initconst __maybe_unused = { + .name = "R-Car Gen1", + .reg = 0xff000044, /* PRR (Product Register) */ +}; + +static const struct renesas_family fam_rcar_gen2 __initconst __maybe_unused = { + .name = "R-Car Gen2", + .reg = 0xff000044, /* PRR (Product Register) */ +}; + +static const struct renesas_family fam_rcar_gen3 __initconst __maybe_unused = { + .name = "R-Car Gen3", + .reg = 0xfff00044, /* PRR (Product Register) */ +}; + +static const struct renesas_family fam_rcar_gen4 __initconst __maybe_unused = { + .name = "R-Car Gen4", +}; + +static const struct renesas_family fam_rcar_gen5 __initconst __maybe_unused = { + .name = "R-Car Gen5", +}; + +static const struct renesas_family fam_rmobile __initconst __maybe_unused = { + .name = "R-Mobile", + .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ +}; + +static const struct renesas_family fam_rza1 __initconst __maybe_unused = { + .name = "RZ/A1", +}; + +static const struct renesas_family fam_rza2 __initconst __maybe_unused = { + .name = "RZ/A2", +}; + +static const struct renesas_family fam_rzfive __initconst __maybe_unused = { + .name = "RZ/Five", +}; + +static const struct renesas_family fam_rzg1 __initconst __maybe_unused = { + .name = "RZ/G1", + .reg = 0xff000044, /* PRR (Product Register) */ +}; + +static const struct renesas_family fam_rzg2 __initconst __maybe_unused = { + .name = "RZ/G2", + .reg = 0xfff00044, /* PRR (Product Register) */ +}; + +static const struct renesas_family fam_rzg2l __initconst __maybe_unused = { + .name = "RZ/G2L", +}; + +static const struct renesas_family fam_rzg2ul __initconst __maybe_unused = { + .name = "RZ/G2UL", +}; + +static const struct renesas_family fam_rzv2l __initconst __maybe_unused = { + .name = "RZ/V2L", +}; + +static const struct renesas_family fam_rzv2m __initconst __maybe_unused = { + .name = "RZ/V2M", +}; + +static const struct renesas_family fam_shmobile __initconst __maybe_unused = { + .name = "SH-Mobile", + .reg = 0xe600101c, /* CCCR (Common Chip Code Register) */ +}; + +struct renesas_soc { + const struct renesas_family *family; + u32 id; +}; + +static const struct renesas_soc soc_rz_a1h __initconst __maybe_unused = { + .family = &fam_rza1, +}; + +static const struct renesas_soc soc_rz_a2m __initconst __maybe_unused = { + .family = &fam_rza2, + .id = 0x3b, +}; + +static const struct renesas_soc soc_rmobile_ape6 __initconst __maybe_unused = { + .family = &fam_rmobile, + .id = 0x3f, +}; + +static const struct renesas_soc soc_rmobile_a1 __initconst __maybe_unused = { + .family = &fam_rmobile, + .id = 0x40, +}; + +static const struct renesas_soc soc_rz_five __initconst __maybe_unused = { + .family = &fam_rzfive, + .id = 0x847c447, +}; + +static const struct renesas_soc soc_rz_g1h __initconst __maybe_unused = { + .family = &fam_rzg1, + .id = 0x45, +}; + +static const struct renesas_soc soc_rz_g1m __initconst __maybe_unused = { + .family = &fam_rzg1, + .id = 0x47, +}; + +static const struct renesas_soc soc_rz_g1n __initconst __maybe_unused = { + .family = &fam_rzg1, + .id = 0x4b, +}; + +static const struct renesas_soc soc_rz_g1e __initconst __maybe_unused = { + .family = &fam_rzg1, + .id = 0x4c, +}; + +static const struct renesas_soc soc_rz_g1c __initconst __maybe_unused = { + .family = &fam_rzg1, + .id = 0x53, +}; + +static const struct renesas_soc soc_rz_g2m __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x52, +}; + +static const struct renesas_soc soc_rz_g2n __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x55, +}; + +static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x57, +}; + +static const struct renesas_soc soc_rz_g2h __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x4f, +}; + +static const struct renesas_soc soc_rz_g2l __initconst __maybe_unused = { + .family = &fam_rzg2l, + .id = 0x841c447, +}; + +static const struct renesas_soc soc_rz_g2ul __initconst __maybe_unused = { + .family = &fam_rzg2ul, + .id = 0x8450447, +}; + +static const struct renesas_soc soc_rz_v2l __initconst __maybe_unused = { + .family = &fam_rzv2l, + .id = 0x8447447, +}; + +static const struct renesas_soc soc_rz_v2m __initconst __maybe_unused = { + .family = &fam_rzv2m, +}; + +static const struct renesas_soc soc_rcar_m1a __initconst __maybe_unused = { + .family = &fam_rcar_gen1, +}; + +static const struct renesas_soc soc_rcar_h1 __initconst __maybe_unused = { + .family = &fam_rcar_gen1, + .id = 0x3b, +}; + +static const struct renesas_soc soc_rcar_h2 __initconst __maybe_unused = { + .family = &fam_rcar_gen2, + .id = 0x45, +}; + +static const struct renesas_soc soc_rcar_m2_w __initconst __maybe_unused = { + .family = &fam_rcar_gen2, + .id = 0x47, +}; + +static const struct renesas_soc soc_rcar_v2h __initconst __maybe_unused = { + .family = &fam_rcar_gen2, + .id = 0x4a, +}; + +static const struct renesas_soc soc_rcar_m2_n __initconst __maybe_unused = { + .family = &fam_rcar_gen2, + .id = 0x4b, +}; + +static const struct renesas_soc soc_rcar_e2 __initconst __maybe_unused = { + .family = &fam_rcar_gen2, + .id = 0x4c, +}; + +static const struct renesas_soc soc_rcar_h3 __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x4f, +}; + +static const struct renesas_soc soc_rcar_m3_w __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x52, +}; + +static const struct renesas_soc soc_rcar_m3_n __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x55, +}; + +static const struct renesas_soc soc_rcar_v3m __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x54, +}; + +static const struct renesas_soc soc_rcar_v3h __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x56, +}; + +static const struct renesas_soc soc_rcar_e3 __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x57, +}; + +static const struct renesas_soc soc_rcar_d3 __initconst __maybe_unused = { + .family = &fam_rcar_gen3, + .id = 0x58, +}; + +static const struct renesas_soc soc_rcar_v3u __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x59, +}; + +static const struct renesas_soc soc_rcar_s4 __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x5a, +}; + +static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x5c, +}; + +static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x5d, +}; + +static const struct renesas_soc soc_rcar_x5h __initconst __maybe_unused = { + .family = &fam_rcar_gen5, + .id = 0x60, +}; + +static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { + .family = &fam_shmobile, + .id = 0x37, +}; + +static const struct of_device_id renesas_socs[] __initconst __maybe_unused = { +#ifdef CONFIG_ARCH_R7S72100 + { .compatible = "renesas,r7s72100", .data = &soc_rz_a1h }, +#endif +#ifdef CONFIG_ARCH_R7S9210 + { .compatible = "renesas,r7s9210", .data = &soc_rz_a2m }, +#endif +#ifdef CONFIG_ARCH_R8A73A4 + { .compatible = "renesas,r8a73a4", .data = &soc_rmobile_ape6 }, +#endif +#ifdef CONFIG_ARCH_R8A7740 + { .compatible = "renesas,r8a7740", .data = &soc_rmobile_a1 }, +#endif +#ifdef CONFIG_ARCH_R8A7742 + { .compatible = "renesas,r8a7742", .data = &soc_rz_g1h }, +#endif +#ifdef CONFIG_ARCH_R8A7743 + { .compatible = "renesas,r8a7743", .data = &soc_rz_g1m }, +#endif +#ifdef CONFIG_ARCH_R8A7744 + { .compatible = "renesas,r8a7744", .data = &soc_rz_g1n }, +#endif +#ifdef CONFIG_ARCH_R8A7745 + { .compatible = "renesas,r8a7745", .data = &soc_rz_g1e }, +#endif +#ifdef CONFIG_ARCH_R8A77470 + { .compatible = "renesas,r8a77470", .data = &soc_rz_g1c }, +#endif +#ifdef CONFIG_ARCH_R8A774A1 + { .compatible = "renesas,r8a774a1", .data = &soc_rz_g2m }, +#endif +#ifdef CONFIG_ARCH_R8A774B1 + { .compatible = "renesas,r8a774b1", .data = &soc_rz_g2n }, +#endif +#ifdef CONFIG_ARCH_R8A774C0 + { .compatible = "renesas,r8a774c0", .data = &soc_rz_g2e }, +#endif +#ifdef CONFIG_ARCH_R8A774E1 + { .compatible = "renesas,r8a774e1", .data = &soc_rz_g2h }, +#endif +#ifdef CONFIG_ARCH_R8A7778 + { .compatible = "renesas,r8a7778", .data = &soc_rcar_m1a }, +#endif +#ifdef CONFIG_ARCH_R8A7779 + { .compatible = "renesas,r8a7779", .data = &soc_rcar_h1 }, +#endif +#ifdef CONFIG_ARCH_R8A7790 + { .compatible = "renesas,r8a7790", .data = &soc_rcar_h2 }, +#endif +#ifdef CONFIG_ARCH_R8A7791 + { .compatible = "renesas,r8a7791", .data = &soc_rcar_m2_w }, +#endif +#ifdef CONFIG_ARCH_R8A7792 + { .compatible = "renesas,r8a7792", .data = &soc_rcar_v2h }, +#endif +#ifdef CONFIG_ARCH_R8A7793 + { .compatible = "renesas,r8a7793", .data = &soc_rcar_m2_n }, +#endif +#ifdef CONFIG_ARCH_R8A7794 + { .compatible = "renesas,r8a7794", .data = &soc_rcar_e2 }, +#endif +#ifdef CONFIG_ARCH_R8A77951 + { .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 }, + { .compatible = "renesas,r8a779m0", .data = &soc_rcar_h3 }, + { .compatible = "renesas,r8a779m1", .data = &soc_rcar_h3 }, + { .compatible = "renesas,r8a779m8", .data = &soc_rcar_h3 }, + { .compatible = "renesas,r8a779mb", .data = &soc_rcar_h3 }, +#endif +#ifdef CONFIG_ARCH_R8A77960 + { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, +#endif +#ifdef CONFIG_ARCH_R8A77961 + { .compatible = "renesas,r8a77961", .data = &soc_rcar_m3_w }, + { .compatible = "renesas,r8a779m2", .data = &soc_rcar_m3_w }, + { .compatible = "renesas,r8a779m3", .data = &soc_rcar_m3_w }, +#endif +#ifdef CONFIG_ARCH_R8A77965 + { .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n }, + { .compatible = "renesas,r8a779m4", .data = &soc_rcar_m3_n }, + { .compatible = "renesas,r8a779m5", .data = &soc_rcar_m3_n }, + { .compatible = "renesas,r8a779md", .data = &soc_rcar_m3_n }, +#endif +#ifdef CONFIG_ARCH_R8A77970 + { .compatible = "renesas,r8a77970", .data = &soc_rcar_v3m }, +#endif +#ifdef CONFIG_ARCH_R8A77980 + { .compatible = "renesas,r8a77980", .data = &soc_rcar_v3h }, +#endif +#ifdef CONFIG_ARCH_R8A77990 + { .compatible = "renesas,r8a77990", .data = &soc_rcar_e3 }, + { .compatible = "renesas,r8a779m6", .data = &soc_rcar_e3 }, +#endif +#ifdef CONFIG_ARCH_R8A77995 + { .compatible = "renesas,r8a77995", .data = &soc_rcar_d3 }, + { .compatible = "renesas,r8a779m7", .data = &soc_rcar_d3 }, +#endif +#ifdef CONFIG_ARCH_R8A779A0 + { .compatible = "renesas,r8a779a0", .data = &soc_rcar_v3u }, +#endif +#ifdef CONFIG_ARCH_R8A779F0 + { .compatible = "renesas,r8a779f0", .data = &soc_rcar_s4 }, +#endif +#ifdef CONFIG_ARCH_R8A779G0 + { .compatible = "renesas,r8a779g0", .data = &soc_rcar_v4h }, +#endif +#ifdef CONFIG_ARCH_R8A779H0 + { .compatible = "renesas,r8a779h0", .data = &soc_rcar_v4m }, +#endif +#ifdef CONFIG_ARCH_R8A78000 + { .compatible = "renesas,r8a78000", .data = &soc_rcar_x5h }, +#endif +#ifdef CONFIG_ARCH_R9A07G043 +#ifdef CONFIG_RISCV + { .compatible = "renesas,r9a07g043", .data = &soc_rz_five }, +#else + { .compatible = "renesas,r9a07g043", .data = &soc_rz_g2ul }, +#endif +#endif +#ifdef CONFIG_ARCH_R9A07G044 + { .compatible = "renesas,r9a07g044", .data = &soc_rz_g2l }, +#endif +#ifdef CONFIG_ARCH_R9A07G054 + { .compatible = "renesas,r9a07g054", .data = &soc_rz_v2l }, +#endif +#ifdef CONFIG_ARCH_R9A09G011 + { .compatible = "renesas,r9a09g011", .data = &soc_rz_v2m }, +#endif +#ifdef CONFIG_ARCH_SH73A0 + { .compatible = "renesas,sh73a0", .data = &soc_shmobile_ag5 }, +#endif + { /* sentinel */ } +}; + +struct renesas_id { + unsigned int offset; + u32 mask; +}; + +static const struct renesas_id id_bsid __initconst = { + .offset = 0, + .mask = 0xff0000, + /* + * TODO: Upper 4 bits of BSID are for chip version, but the format is + * not known at this time so we don't know how to specify eshi and eslo + */ +}; + +static const struct renesas_id id_rzg2l __initconst = { + .offset = 0xa04, + .mask = 0xfffffff, +}; + +static const struct renesas_id id_rzv2m __initconst = { + .offset = 0x104, + .mask = 0xff, +}; + +static const struct renesas_id id_prr __initconst = { + .offset = 0, + .mask = 0xff00, +}; + +static const struct renesas_id id_mfis __initconst = { + .offset = 0x44, + .mask = 0xff00, +}; + +static const struct of_device_id renesas_ids[] __initconst = { + { .compatible = "renesas,bsid", .data = &id_bsid }, + { .compatible = "renesas,r8a78000-mfis", .data = &id_mfis }, + { .compatible = "renesas,r9a07g043-sysc", .data = &id_rzg2l }, + { .compatible = "renesas,r9a07g044-sysc", .data = &id_rzg2l }, + { .compatible = "renesas,r9a07g054-sysc", .data = &id_rzg2l }, + { .compatible = "renesas,r9a08g045-sysc", .data = &id_rzg2l }, + { .compatible = "renesas,r9a09g011-sys", .data = &id_rzv2m }, + { .compatible = "renesas,prr", .data = &id_prr }, + { /* sentinel */ } +}; + +static int __init renesas_soc_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + unsigned int product, eshi = 0, eslo; + const struct renesas_family *family; + const struct of_device_id *match; + const struct renesas_soc *soc; + const struct renesas_id *id; + void __iomem *chipid = NULL; + const char *rev_prefix = ""; + struct soc_device *soc_dev; + struct device_node *np; + const char *soc_id; + int ret; + + match = of_machine_get_match(renesas_socs); + if (!match) + return -ENODEV; + + soc_id = strchr(match->compatible, ',') + 1; + soc = match->data; + family = soc->family; + + np = of_find_matching_node_and_match(NULL, renesas_ids, &match); + if (np) { + id = match->data; + chipid = of_iomap(np, 0); + of_node_put(np); + } else if (soc->id && family->reg) { + /* Try hardcoded CCCR/PRR fallback */ + id = &id_prr; + chipid = ioremap(family->reg, 4); + } + + soc_dev_attr = kzalloc_obj(*soc_dev_attr); + if (!soc_dev_attr) { + if (chipid) + iounmap(chipid); + return -ENOMEM; + } + + soc_dev_attr->family = kstrdup_const(family->name, GFP_KERNEL); + soc_dev_attr->soc_id = kstrdup_const(soc_id, GFP_KERNEL); + + if (chipid) { + product = readl(chipid + id->offset); + iounmap(chipid); + + if (id == &id_prr || id == &id_mfis) { + /* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */ + if ((product & 0x7fff) == 0x5210) + product ^= 0x11; + /* R-Car M3-W ES1.3 incorrectly identifies as ES2.1 */ + if ((product & 0x7fff) == 0x5211) + product ^= 0x12; + + eshi = ((product >> 4) & 0x0f) + 1; + eslo = product & 0xf; + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u", + eshi, eslo); + } else if (id == &id_rzg2l) { + eshi = ((product >> 28) & 0x0f); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u", + eshi); + rev_prefix = "Rev "; + } else if (id == &id_rzv2m) { + eshi = ((product >> 4) & 0x0f); + eslo = product & 0xf; + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u.%u", + eshi, eslo); + } + + if (soc->id && field_get(id->mask, product) != soc->id) { + pr_warn("SoC mismatch (product = 0x%x)\n", product); + ret = -ENODEV; + goto free_soc_dev_attr; + } + } + + pr_info("Detected Renesas %s %s %s%s\n", soc_dev_attr->family, + soc_dev_attr->soc_id, rev_prefix, soc_dev_attr->revision ?: ""); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + ret = PTR_ERR(soc_dev); + goto free_soc_dev_attr; + } + + return 0; + +free_soc_dev_attr: + kfree(soc_dev_attr->revision); + kfree_const(soc_dev_attr->soc_id); + kfree_const(soc_dev_attr->family); + kfree(soc_dev_attr); + return ret; +} +early_initcall(renesas_soc_init); diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c new file mode 100644 index 000000000..161e8c38e --- /dev/null +++ b/drivers/soc/renesas/rz-sysc.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ System controller driver + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> + +#include "rz-sysc.h" + +/** + * struct rz_sysc - RZ SYSC private data structure + * @base: SYSC base address + * @dev: SYSC device pointer + */ +struct rz_sysc { + void __iomem *base; + struct device *dev; +}; + +static int rz_sysc_soc_init(struct rz_sysc *sysc, const struct of_device_id *match) +{ + const struct rz_sysc_init_data *sysc_data = match->data; + const struct rz_sysc_soc_id_init_data *soc_data = sysc_data->soc_id_init_data; + struct soc_device_attribute *soc_dev_attr; + const char *soc_id_start, *soc_id_end; + u32 val, revision, specific_id; + struct soc_device *soc_dev; + char soc_id[32] = {0}; + size_t size; + + soc_id_start = strchr(match->compatible, ',') + 1; + soc_id_end = strchr(match->compatible, '-'); + size = soc_id_end - soc_id_start + 1; + if (size > 32) + size = sizeof(soc_id); + strscpy(soc_id, soc_id_start, size); + + soc_dev_attr = devm_kzalloc(sysc->dev, sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->family = devm_kstrdup(sysc->dev, soc_data->family, GFP_KERNEL); + if (!soc_dev_attr->family) + return -ENOMEM; + + soc_dev_attr->soc_id = devm_kstrdup(sysc->dev, soc_id, GFP_KERNEL); + if (!soc_dev_attr->soc_id) + return -ENOMEM; + + val = readl(sysc->base + soc_data->devid_offset); + revision = field_get(soc_data->revision_mask, val); + specific_id = field_get(soc_data->specific_id_mask, val); + soc_dev_attr->revision = devm_kasprintf(sysc->dev, GFP_KERNEL, "%u", revision); + if (!soc_dev_attr->revision) + return -ENOMEM; + + if (soc_data->id && specific_id != soc_data->id) { + dev_warn(sysc->dev, "SoC mismatch (product = 0x%x)\n", specific_id); + return -ENODEV; + } + + /* Try to call SoC-specific device identification */ + if (soc_data->print_id) { + soc_data->print_id(sysc->dev, sysc->base, soc_dev_attr); + } else { + dev_info(sysc->dev, "Detected Renesas %s %s Rev %s\n", + soc_dev_attr->family, soc_dev_attr->soc_id, soc_dev_attr->revision); + } + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) + return PTR_ERR(soc_dev); + + return 0; +} + +static const struct of_device_id rz_sysc_match[] = { +#ifdef CONFIG_SYSC_R9A08G045 + { .compatible = "renesas,r9a08g045-sysc", .data = &rzg3s_sysc_init_data }, +#endif +#ifdef CONFIG_SYSC_R9A08G046 + { .compatible = "renesas,r9a08g046-sysc", .data = &rzg3l_sysc_init_data }, +#endif +#ifdef CONFIG_SYS_R9A09G047 + { .compatible = "renesas,r9a09g047-sys", .data = &rzg3e_sys_init_data }, +#endif +#ifdef CONFIG_SYS_R9A09G056 + { .compatible = "renesas,r9a09g056-sys", .data = &rzv2n_sys_init_data }, +#endif +#ifdef CONFIG_SYS_R9A09G057 + { .compatible = "renesas,r9a09g057-sys", .data = &rzv2h_sys_init_data }, +#endif + { } +}; +MODULE_DEVICE_TABLE(of, rz_sysc_match); + +static int rz_sysc_probe(struct platform_device *pdev) +{ + const struct rz_sysc_init_data *data; + const struct of_device_id *match; + struct device *dev = &pdev->dev; + struct regmap *regmap; + struct rz_sysc *sysc; + int ret; + + struct regmap_config *regmap_cfg __free(kfree) = kzalloc_obj(*regmap_cfg); + if (!regmap_cfg) + return -ENOMEM; + + match = of_match_node(rz_sysc_match, dev->of_node); + if (!match) + return -ENODEV; + + data = match->data; + + 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; + ret = rz_sysc_soc_init(sysc, match); + if (ret) + return ret; + + regmap_cfg->name = "rz_sysc_regs"; + regmap_cfg->reg_bits = 32; + regmap_cfg->reg_stride = 4; + regmap_cfg->val_bits = 32; + regmap_cfg->fast_io = true; + regmap_cfg->max_register = data->max_register; + regmap_cfg->readable_reg = data->readable_reg; + regmap_cfg->writeable_reg = data->writeable_reg; + + regmap = devm_regmap_init_mmio(dev, sysc->base, regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return of_syscon_register_regmap(dev->of_node, regmap); +} + +static struct platform_driver rz_sysc_driver = { + .driver = { + .name = "renesas-rz-sysc", + .suppress_bind_attrs = true, + .of_match_table = rz_sysc_match + }, + .probe = rz_sysc_probe +}; + +static int __init rz_sysc_init(void) +{ + return platform_driver_register(&rz_sysc_driver); +} +subsys_initcall(rz_sysc_init); + +MODULE_DESCRIPTION("Renesas RZ System Controller Driver"); +MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/renesas/rz-sysc.h b/drivers/soc/renesas/rz-sysc.h new file mode 100644 index 000000000..921ee0d26 --- /dev/null +++ b/drivers/soc/renesas/rz-sysc.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Renesas RZ System Controller + * + * Copyright (C) 2024 Renesas Electronics Corp. + */ + +#ifndef __SOC_RENESAS_RZ_SYSC_H__ +#define __SOC_RENESAS_RZ_SYSC_H__ + +#include <linux/device.h> +#include <linux/sys_soc.h> +#include <linux/types.h> + +/** + * struct rz_syc_soc_id_init_data - RZ SYSC SoC identification initialization data + * @family: RZ SoC family + * @id: RZ SoC expected ID + * @devid_offset: SYSC SoC ID register offset + * @revision_mask: SYSC SoC ID revision mask + * @specific_id_mask: SYSC SoC ID specific ID mask + * @print_id: print SoC-specific extended device identification + */ +struct rz_sysc_soc_id_init_data { + const char * const family; + u32 id; + u32 devid_offset; + u32 revision_mask; + u32 specific_id_mask; + void (*print_id)(struct device *dev, void __iomem *sysc_base, + struct soc_device_attribute *soc_dev_attr); +}; + +/** + * struct rz_sysc_init_data - RZ SYSC initialization data + * @soc_id_init_data: RZ SYSC SoC ID initialization data + * @writeable_reg: Regmap writeable register check function + * @readable_reg: Regmap readable register check function + * @max_register: Maximum SYSC register offset to be used by the regmap config + */ +struct rz_sysc_init_data { + const struct rz_sysc_soc_id_init_data *soc_id_init_data; + bool (*writeable_reg)(struct device *dev, unsigned int reg); + bool (*readable_reg)(struct device *dev, unsigned int reg); + u32 max_register; +}; + +extern const struct rz_sysc_init_data rzg3e_sys_init_data; +extern const struct rz_sysc_init_data rzg3l_sysc_init_data; +extern const struct rz_sysc_init_data rzg3s_sysc_init_data; +extern const struct rz_sysc_init_data rzv2h_sys_init_data; +extern const struct rz_sysc_init_data rzv2n_sys_init_data; + +#endif /* __SOC_RENESAS_RZ_SYSC_H__ */ diff --git a/drivers/soc/renesas/rzn1_irqmux.c b/drivers/soc/renesas/rzn1_irqmux.c new file mode 100644 index 000000000..c3887db7a --- /dev/null +++ b/drivers/soc/renesas/rzn1_irqmux.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RZ/N1 GPIO Interrupt Multiplexer + * + * Copyright 2025 Schneider Electric + * Author: Herve Codina <herve.codina@bootlin.com> + */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> + +/* + * Up to 8 output lines are connected to GIC SPI interrupt controller + * starting at IRQ 103. + */ +#define RZN1_IRQMUX_GIC_SPI_BASE 103 +#define RZN1_IRQMUX_NUM_OUTPUTS 8 + +static int rzn1_irqmux_parent_args_to_line_index(struct device *dev, + const struct of_phandle_args *parent_args) +{ + /* + * The parent interrupt should be one of the GIC controller. + * Three arguments must be provided. + * - args[0]: GIC_SPI + * - args[1]: The GIC interrupt number + * - args[2]: The interrupt flags + * + * We retrieve the line index based on the GIC interrupt number + * provided. + */ + + if (parent_args->args_count != 3 || parent_args->args[0] != GIC_SPI) { + dev_err(dev, "Invalid interrupt-map item\n"); + return -EINVAL; + } + + if (parent_args->args[1] < RZN1_IRQMUX_GIC_SPI_BASE || + parent_args->args[1] >= RZN1_IRQMUX_GIC_SPI_BASE + RZN1_IRQMUX_NUM_OUTPUTS) { + dev_err(dev, "Invalid GIC interrupt %u\n", parent_args->args[1]); + return -EINVAL; + } + + return parent_args->args[1] - RZN1_IRQMUX_GIC_SPI_BASE; +} + +static int rzn1_irqmux_probe(struct platform_device *pdev) +{ + DECLARE_BITMAP(index_done, RZN1_IRQMUX_NUM_OUTPUTS) = {}; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct of_imap_parser imap_parser; + struct of_imap_item imap_item; + u32 __iomem *regs; + int index; + int ret; + u32 tmp; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* We support only #interrupt-cells = <1> and #address-cells = <0> */ + ret = of_property_read_u32(np, "#interrupt-cells", &tmp); + if (ret) + return ret; + if (tmp != 1) + return -EINVAL; + + ret = of_property_read_u32(np, "#address-cells", &tmp); + if (ret) + return ret; + if (tmp != 0) + return -EINVAL; + + ret = of_imap_parser_init(&imap_parser, np, &imap_item); + if (ret) + return ret; + + for_each_of_imap_item(&imap_parser, &imap_item) { + index = rzn1_irqmux_parent_args_to_line_index(dev, &imap_item.parent_args); + if (index < 0) { + of_node_put(imap_item.parent_args.np); + return index; + } + + if (test_and_set_bit(index, index_done)) { + of_node_put(imap_item.parent_args.np); + dev_err(dev, "Mux output line %d already defined in interrupt-map\n", + index); + return -EINVAL; + } + + /* + * The child #address-cells is 0 (already checked). The first + * value in imap item is the src hwirq. + */ + writel(imap_item.child_imap[0], regs + index); + } + + return 0; +} + +static const struct of_device_id rzn1_irqmux_of_match[] = { + { .compatible = "renesas,rzn1-gpioirqmux", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rzn1_irqmux_of_match); + +static struct platform_driver rzn1_irqmux_driver = { + .probe = rzn1_irqmux_probe, + .driver = { + .name = "rzn1_irqmux", + .of_match_table = rzn1_irqmux_of_match, + }, +}; +module_platform_driver(rzn1_irqmux_driver); + +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); +MODULE_DESCRIPTION("Renesas RZ/N1 GPIO IRQ Multiplexer Driver"); +MODULE_LICENSE("GPL"); |
