[PATCH v2 02/15] soc: renesas: Add SYSC driver for Renesas RZ family
Biju Das
biju.das.jz at bp.renesas.com
Tue Nov 26 01:28:33 PST 2024
Hi Claudiu,
Thanks for the patch.
> -----Original Message-----
> From: Claudiu <claudiu.beznea at tuxon.dev>
> Sent: 26 November 2024 09:21
> Subject: [PATCH v2 02/15] soc: renesas: Add SYSC driver for Renesas RZ family
>
> From: Claudiu Beznea <claudiu.beznea.uj at bp.renesas.com>
>
> The RZ/G3S system controller (SYSC) has various registers that control signals specific to individual
> IPs. IP drivers must control these signals at different configuration phases.
>
> Add SYSC driver that allows individual SYSC consumers to control these signals. The SYSC driver
> exports a syscon regmap enabling IP drivers to use a specific SYSC offset and mask from the device
> tree, which can then be accessed through regmap_update_bits().
>
> Currently, the SYSC driver provides control to the USB PWRRDY signal, which is routed to the USB PHY.
> This signal needs to be managed before or after powering the USB PHY off or on.
>
> Other SYSC signals candidates (as exposed in the the hardware manual of the RZ/G3S SoC) include:
>
> * PCIe:
> - ALLOW_ENTER_L1 signal controlled through the SYS_PCIE_CFG register
> - PCIE_RST_RSM_B signal controlled through the SYS_PCIE_RST_RSM_B
> register
> - MODE_RXTERMINATION signal controlled through SYS_PCIE_PHY register
>
> * SPI:
> - SEL_SPI_OCTA signal controlled through SYS_IPCONT_SEL_SPI_OCTA
> register
>
> * I2C/I3C:
> - af_bypass I2C signals controlled through SYS_I2Cx_CFG registers
> (x=0..3)
> - af_bypass I3C signal controlled through SYS_I3C_CFG register
>
> * Ethernet:
> - FEC_GIGA_ENABLE Ethernet signals controlled through SYS_GETHx_CFG
> registers (x=0..1)
>
> As different Renesas RZ SoC shares most of the SYSC functionalities available on the RZ/G3S SoC, the
> driver if formed of a SYSC core part and a SoC specific part allowing individual SYSC SoC to provide
> functionalities to the SYSC core.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj at bp.renesas.com>
Reviewed-by: Biju Das <biju.das.jz at bp.renesas.com>
Cheers,
Biju
> ---
>
> Change in v2:
> - this was patch 04/16 in v1
> - dropped the initial approach proposed in v1 where a with a reset
> controller driver was proposed to handle the USB PWRRDY signal
> - implemented it with syscon regmap and the SYSC signal concept
> (introduced in this patch)
>
> drivers/soc/renesas/Kconfig | 7 +
> drivers/soc/renesas/Makefile | 2 +
> drivers/soc/renesas/r9a08g045-sysc.c | 31 +++
> drivers/soc/renesas/rz-sysc.c | 286 +++++++++++++++++++++++++++
> drivers/soc/renesas/rz-sysc.h | 52 +++++
> 5 files changed, 378 insertions(+)
> create mode 100644 drivers/soc/renesas/r9a08g045-sysc.c
> create mode 100644 drivers/soc/renesas/rz-sysc.c create mode 100644 drivers/soc/renesas/rz-sysc.h
>
> diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index
> 9f7fe02310b9..0686c3ad9e27 100644
> --- a/drivers/soc/renesas/Kconfig
> +++ b/drivers/soc/renesas/Kconfig
> @@ -378,4 +378,11 @@ config PWC_RZV2M
> config RST_RCAR
> bool "Reset Controller support for R-Car" if COMPILE_TEST
>
> +config SYSC_RZ
> + bool "System controller for RZ SoCs" if COMPILE_TEST
> +
> +config SYSC_R9A08G045
> + bool "Renesas RZ/G3S System controller support" if COMPILE_TEST
> + select SYSC_RZ
> +
> endif # SOC_RENESAS
> diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index
> 734f8f8cefa4..8cd139b3dd0a 100644
> --- a/drivers/soc/renesas/Makefile
> +++ b/drivers/soc/renesas/Makefile
> @@ -6,7 +6,9 @@ obj-$(CONFIG_SOC_RENESAS) += renesas-soc.o
> ifdef CONFIG_SMP
> obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o
> endif
> +obj-$(CONFIG_SYSC_R9A08G045) += r9a08g045-sysc.o
>
> # Family
> obj-$(CONFIG_PWC_RZV2M) += pwc-rzv2m.o
> obj-$(CONFIG_RST_RCAR) += rcar-rst.o
> +obj-$(CONFIG_SYSC_RZ) += rz-sysc.o
> diff --git a/drivers/soc/renesas/r9a08g045-sysc.c b/drivers/soc/renesas/r9a08g045-sysc.c
> new file mode 100644
> index 000000000000..ceea738aee72
> --- /dev/null
> +++ b/drivers/soc/renesas/r9a08g045-sysc.c
> @@ -0,0 +1,31 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RZ/G3S System controller driver
> + *
> + * Copyright (C) 2024 Renesas Electronics Corp.
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bits.h>
> +#include <linux/init.h>
> +
> +#include "rz-sysc.h"
> +
> +#define SYS_USB_PWRRDY 0xd70
> +#define SYS_USB_PWRRDY_PWRRDY_N BIT(0)
> +#define SYS_MAX_REG 0xe20
> +
> +static const struct rz_sysc_signal_init_data rzg3s_sysc_signals_init_data[] __initconst = {
> + {
> + .name = "usb-pwrrdy",
> + .offset = SYS_USB_PWRRDY,
> + .mask = SYS_USB_PWRRDY_PWRRDY_N,
> + .refcnt_incr_val = 0
> + }
> +};
> +
> +const struct rz_sysc_init_data rzg3s_sysc_init_data = {
> + .signals_init_data = rzg3s_sysc_signals_init_data,
> + .num_signals = ARRAY_SIZE(rzg3s_sysc_signals_init_data),
> + .max_register_offset = SYS_MAX_REG,
> +};
> diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c new file mode 100644 index
> 000000000000..dc0edacd7170
> --- /dev/null
> +++ b/drivers/soc/renesas/rz-sysc.c
> @@ -0,0 +1,286 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * RZ System controller driver
> + *
> + * Copyright (C) 2024 Renesas Electronics Corp.
> + */
> +
> +#include <linux/dcache.h>
> +#include <linux/debugfs.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/refcount.h>
> +#include <linux/regmap.h>
> +#include <linux/seq_file.h>
> +
> +#include "rz-sysc.h"
> +
> +/**
> + * struct rz_sysc - RZ SYSC private data structure
> + * @base: SYSC base address
> + * @dev: SYSC device pointer
> + * @signals: SYSC signals
> + * @num_signals: number of SYSC signals */ struct rz_sysc {
> + void __iomem *base;
> + struct device *dev;
> + struct rz_sysc_signal *signals;
> + u8 num_signals;
> +};
> +
> +static int rz_sysc_reg_read(void *context, unsigned int off, unsigned
> +int *val) {
> + struct rz_sysc *sysc = context;
> +
> + *val = readl(sysc->base + off);
> +
> + return 0;
> +}
> +
> +static struct rz_sysc_signal *rz_sysc_off_to_signal(struct rz_sysc *sysc, unsigned int offset,
> + unsigned int mask)
> +{
> + struct rz_sysc_signal *signals = sysc->signals;
> +
> + for (u32 i = 0; i < sysc->num_signals; i++) {
> + if (signals[i].init_data->offset != offset)
> + continue;
> +
> + /*
> + * In case mask == 0 we just return the signal data w/o checking the mask.
> + * This is useful when calling through rz_sysc_reg_write() to check
> + * if the requested setting is for a mapped signal or not.
> + */
> + if (mask) {
> + if (signals[i].init_data->mask == mask)
> + return &signals[i];
> + } else {
> + return &signals[i];
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static int rz_sysc_reg_update_bits(void *context, unsigned int off,
> + unsigned int mask, unsigned int val) {
> + struct rz_sysc *sysc = context;
> + struct rz_sysc_signal *signal;
> + bool update = false;
> +
> + signal = rz_sysc_off_to_signal(sysc, off, mask);
> + if (signal) {
> + if (signal->init_data->refcnt_incr_val == val) {
> + if (!refcount_read(&signal->refcnt)) {
> + refcount_set(&signal->refcnt, 1);
> + update = true;
> + } else {
> + refcount_inc(&signal->refcnt);
> + }
> + } else {
> + update = refcount_dec_and_test(&signal->refcnt);
> + }
> + } else {
> + update = true;
> + }
> +
> + if (update) {
> + u32 tmp;
> +
> + tmp = readl(sysc->base + off);
> + tmp &= ~mask;
> + tmp |= val & mask;
> + writel(tmp, sysc->base + off);
> + }
> +
> + return 0;
> +}
> +
> +static int rz_sysc_reg_write(void *context, unsigned int off, unsigned
> +int val) {
> + struct rz_sysc *sysc = context;
> + struct rz_sysc_signal *signal;
> +
> + /*
> + * Force using regmap_update_bits() for signals to have reference counter
> + * per individual signal in case there are multiple signals controlled
> + * through the same register.
> + */
> + signal = rz_sysc_off_to_signal(sysc, off, 0);
> + if (signal) {
> + dev_err(sysc->dev,
> + "regmap_write() not allowed on register controlling a signal. Use
> regmap_update_bits()!");
> + return -EOPNOTSUPP;
> + }
> +
> + writel(val, sysc->base + off);
> +
> + return 0;
> +}
> +
> +static bool rz_sysc_writeable_reg(struct device *dev, unsigned int off)
> +{
> + struct rz_sysc *sysc = dev_get_drvdata(dev);
> + struct rz_sysc_signal *signal;
> +
> + /* Any register containing a signal is writeable. */
> + signal = rz_sysc_off_to_signal(sysc, off, 0);
> + if (signal)
> + return true;
> +
> + return false;
> +}
> +
> +static bool rz_sysc_readable_reg(struct device *dev, unsigned int off)
> +{
> + struct rz_sysc *sysc = dev_get_drvdata(dev);
> + struct rz_sysc_signal *signal;
> +
> + /* Any register containing a signal is readable. */
> + signal = rz_sysc_off_to_signal(sysc, off, 0);
> + if (signal)
> + return true;
> +
> + return false;
> +}
> +
> +static int rz_sysc_signals_show(struct seq_file *s, void *what) {
> + struct rz_sysc *sysc = s->private;
> +
> + seq_printf(s, "%-20s Enable count\n", "Signal");
> + seq_printf(s, "%-20s ------------\n", "--------------------");
> +
> + for (u8 i = 0; i < sysc->num_signals; i++) {
> + seq_printf(s, "%-20s %d\n", sysc->signals[i].init_data->name,
> + refcount_read(&sysc->signals[i].refcnt));
> + }
> +
> + return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(rz_sysc_signals);
> +
> +static void rz_sysc_debugfs_remove(void *data) {
> + debugfs_remove_recursive(data);
> +}
> +
> +static int rz_sysc_signals_init(struct rz_sysc *sysc,
> + const struct rz_sysc_signal_init_data *init_data,
> + u32 num_signals)
> +{
> + struct dentry *root;
> + int ret;
> +
> + sysc->signals = devm_kcalloc(sysc->dev, num_signals, sizeof(*sysc->signals),
> + GFP_KERNEL);
> + if (!sysc->signals)
> + return -ENOMEM;
> +
> + for (u32 i = 0; i < num_signals; i++) {
> + struct rz_sysc_signal_init_data *id;
> +
> + id = devm_kzalloc(sysc->dev, sizeof(*id), GFP_KERNEL);
> + if (!id)
> + return -ENOMEM;
> +
> + id->name = devm_kstrdup(sysc->dev, init_data->name, GFP_KERNEL);
> + if (!id->name)
> + return -ENOMEM;
> +
> + id->offset = init_data->offset;
> + id->mask = init_data->mask;
> + id->refcnt_incr_val = init_data->refcnt_incr_val;
> +
> + sysc->signals[i].init_data = id;
> + refcount_set(&sysc->signals[i].refcnt, 0);
> + }
> +
> + sysc->num_signals = num_signals;
> +
> + root = debugfs_create_dir("renesas-rz-sysc", NULL);
> + ret = devm_add_action_or_reset(sysc->dev, rz_sysc_debugfs_remove, root);
> + if (ret)
> + return ret;
> + debugfs_create_file("signals", 0444, root, sysc,
> +&rz_sysc_signals_fops);
> +
> + return 0;
> +}
> +
> +static struct regmap_config rz_sysc_regmap = {
> + .name = "rz_sysc_regs",
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .fast_io = true,
> + .reg_read = rz_sysc_reg_read,
> + .reg_write = rz_sysc_reg_write,
> + .reg_update_bits = rz_sysc_reg_update_bits,
> + .writeable_reg = rz_sysc_writeable_reg,
> + .readable_reg = rz_sysc_readable_reg,
> +};
> +
> +static const struct of_device_id rz_sysc_match[] = { #ifdef
> +CONFIG_SYSC_R9A08G045
> + { .compatible = "renesas,r9a08g045-sysc", .data =
> +&rzg3s_sysc_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;
> + struct device *dev = &pdev->dev;
> + struct rz_sysc *sysc;
> + struct regmap *regmap;
> + int ret;
> +
> + data = device_get_match_data(dev);
> + if (!data || !data->max_register_offset)
> + return -EINVAL;
> +
> + 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_signals_init(sysc, data->signals_init_data, data->num_signals);
> + if (ret)
> + return ret;
> +
> + dev_set_drvdata(dev, sysc);
> + rz_sysc_regmap.max_register = data->max_register_offset;
> + regmap = devm_regmap_init(dev, NULL, sysc, &rz_sysc_regmap);
> + 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",
> + .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 at 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
> 000000000000..bb850310c931
> --- /dev/null
> +++ b/drivers/soc/renesas/rz-sysc.h
> @@ -0,0 +1,52 @@
> +/* 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/refcount.h>
> +#include <linux/types.h>
> +
> +/**
> + * struct rz_sysc_signal_init_data - RZ SYSC signals init data
> + * @name: signal name
> + * @offset: register offset controling this signal
> + * @mask: bitmask in register specific to this signal
> + * @refcnt_incr_val: increment refcnt when setting this value */
> +struct rz_sysc_signal_init_data {
> + const char *name;
> + u32 offset;
> + u32 mask;
> + u32 refcnt_incr_val;
> +};
> +
> +/**
> + * struct rz_sysc_signal - RZ SYSC signals
> + * @init_data: signals initialization data
> + * @refcnt: reference counter
> + */
> +struct rz_sysc_signal {
> + const struct rz_sysc_signal_init_data *init_data;
> + refcount_t refcnt;
> +};
> +
> +/**
> + * struct rz_sysc_init_data - RZ SYSC initialization data
> + * @signals_init_data: RZ SYSC signals initialization data
> + * @num_signals: number of SYSC signals
> + * @max_register_offset: Maximum SYSC register offset to be used by the
> +regmap config */ struct rz_sysc_init_data {
> + const struct rz_sysc_signal_init_data *signals_init_data;
> + u32 num_signals;
> + u32 max_register_offset;
> +};
> +
> +extern const struct rz_sysc_init_data rzg3s_sysc_init_data;
> +
> +#endif /* __SOC_RENESAS_RZ_SYSC_H__ */
> --
> 2.39.2
>
More information about the linux-phy
mailing list