[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