Message ID | 20241126092050.1825607-5-claudiu.beznea.uj@bp.renesas.com |
---|---|
State | New |
Headers | show |
Series | Add initial USB support for the Renesas RZ/G3S SoC | expand |
Hi Claudiu, Thanks for the patch. > -----Original Message----- > From: Claudiu <claudiu.beznea@tuxon.dev> > Sent: 26 November 2024 09:21 > Subject: [PATCH v2 04/15] soc: renesas: rz-sysc: Add SoC detection support > > From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> > > The RZ SYSC controller has registers that keep the SoC ID data. Add driver support to retrieve the SoC > ID and register a SoC driver. > > Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com> Cheers, Biju > --- > > Changes in v2: > - this was patch 05/16 in v1 > - changed patch title and description > - added SoC initialization code in its own function > - addressed the review comments > - introduced struct rz_sysc_soc_id_init_data and adjusted the code > accordingly > - dropped the RZ/G3S SoC detection code (it will be introduced in > a separate patch) > > drivers/soc/renesas/rz-sysc.c | 72 +++++++++++++++++++++++++++++++++-- > drivers/soc/renesas/rz-sysc.h | 18 +++++++++ > 2 files changed, 86 insertions(+), 4 deletions(-) > > diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c index > dc0edacd7170..d34d295831b8 100644 > --- a/drivers/soc/renesas/rz-sysc.c > +++ b/drivers/soc/renesas/rz-sysc.c > @@ -14,9 +14,12 @@ > #include <linux/refcount.h> > #include <linux/regmap.h> > #include <linux/seq_file.h> > +#include <linux/sys_soc.h> > > #include "rz-sysc.h" > > +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) > + > /** > * struct rz_sysc - RZ SYSC private data structure > * @base: SYSC base address > @@ -211,6 +214,59 @@ static int rz_sysc_signals_init(struct rz_sysc *sysc, > return 0; > } > > +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}; > + u8 size; > + > + if (!soc_data || !soc_data->family || !soc_data->offset || > + !soc_data->revision_mask) > + return -EINVAL; > + > + soc_id_start = strchr(match->compatible, ',') + 1; > + soc_id_end = strchr(match->compatible, '-'); > + size = soc_id_end - soc_id_start; > + if (size > 32) > + size = 32; > + 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 = soc_data->family; > + 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->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; > + } > + > + 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 struct regmap_config rz_sysc_regmap = { > .name = "rz_sysc_regs", > .reg_bits = 32, > @@ -235,14 +291,15 @@ 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 rz_sysc *sysc; > struct regmap *regmap; > + struct rz_sysc *sysc; > int ret; > > - data = device_get_match_data(dev); > - if (!data || !data->max_register_offset) > - return -EINVAL; > + match = of_match_node(rz_sysc_match, dev->of_node); > + if (!match || !match->data) > + return -ENODEV; > > sysc = devm_kzalloc(dev, sizeof(*sysc), GFP_KERNEL); > if (!sysc) > @@ -253,6 +310,13 @@ static int rz_sysc_probe(struct platform_device *pdev) > return PTR_ERR(sysc->base); > > sysc->dev = dev; > + ret = rz_sysc_soc_init(sysc, match); > + if (ret) > + return ret; > + > + data = match->data; > + if (!data->max_register_offset) > + return -EINVAL; > > ret = rz_sysc_signals_init(sysc, data->signals_init_data, data->num_signals); > if (ret) > diff --git a/drivers/soc/renesas/rz-sysc.h b/drivers/soc/renesas/rz-sysc.h index > bb850310c931..babca9c743c7 100644 > --- a/drivers/soc/renesas/rz-sysc.h > +++ b/drivers/soc/renesas/rz-sysc.h > @@ -35,13 +35,31 @@ struct rz_sysc_signal { > refcount_t refcnt; > }; > > +/** > + * struct rz_syc_soc_id_init_data - RZ SYSC SoC identification > +initialization data > + * @family: RZ SoC family > + * @id: RZ SoC expected ID > + * @offset: SYSC SoC ID register offset > + * @revision_mask: SYSC SoC ID revision mask > + * @specific_id_mask: SYSC SoC ID specific ID mask */ struct > +rz_sysc_soc_id_init_data { > + const char * const family; > + u32 id; > + u32 offset; > + u32 revision_mask; > + u32 specific_id_mask; > +}; > + > /** > * struct rz_sysc_init_data - RZ SYSC initialization data > + * @soc_id_init_data: RZ SYSC SoC ID 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_soc_id_init_data *soc_id_init_data; > const struct rz_sysc_signal_init_data *signals_init_data; > u32 num_signals; > u32 max_register_offset; > -- > 2.39.2 >
diff --git a/drivers/soc/renesas/rz-sysc.c b/drivers/soc/renesas/rz-sysc.c index dc0edacd7170..d34d295831b8 100644 --- a/drivers/soc/renesas/rz-sysc.c +++ b/drivers/soc/renesas/rz-sysc.c @@ -14,9 +14,12 @@ #include <linux/refcount.h> #include <linux/regmap.h> #include <linux/seq_file.h> +#include <linux/sys_soc.h> #include "rz-sysc.h" +#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) + /** * struct rz_sysc - RZ SYSC private data structure * @base: SYSC base address @@ -211,6 +214,59 @@ static int rz_sysc_signals_init(struct rz_sysc *sysc, return 0; } +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}; + u8 size; + + if (!soc_data || !soc_data->family || !soc_data->offset || + !soc_data->revision_mask) + return -EINVAL; + + soc_id_start = strchr(match->compatible, ',') + 1; + soc_id_end = strchr(match->compatible, '-'); + size = soc_id_end - soc_id_start; + if (size > 32) + size = 32; + 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 = soc_data->family; + 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->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; + } + + 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 struct regmap_config rz_sysc_regmap = { .name = "rz_sysc_regs", .reg_bits = 32, @@ -235,14 +291,15 @@ 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 rz_sysc *sysc; struct regmap *regmap; + struct rz_sysc *sysc; int ret; - data = device_get_match_data(dev); - if (!data || !data->max_register_offset) - return -EINVAL; + match = of_match_node(rz_sysc_match, dev->of_node); + if (!match || !match->data) + return -ENODEV; sysc = devm_kzalloc(dev, sizeof(*sysc), GFP_KERNEL); if (!sysc) @@ -253,6 +310,13 @@ static int rz_sysc_probe(struct platform_device *pdev) return PTR_ERR(sysc->base); sysc->dev = dev; + ret = rz_sysc_soc_init(sysc, match); + if (ret) + return ret; + + data = match->data; + if (!data->max_register_offset) + return -EINVAL; ret = rz_sysc_signals_init(sysc, data->signals_init_data, data->num_signals); if (ret) diff --git a/drivers/soc/renesas/rz-sysc.h b/drivers/soc/renesas/rz-sysc.h index bb850310c931..babca9c743c7 100644 --- a/drivers/soc/renesas/rz-sysc.h +++ b/drivers/soc/renesas/rz-sysc.h @@ -35,13 +35,31 @@ struct rz_sysc_signal { refcount_t refcnt; }; +/** + * struct rz_syc_soc_id_init_data - RZ SYSC SoC identification initialization data + * @family: RZ SoC family + * @id: RZ SoC expected ID + * @offset: SYSC SoC ID register offset + * @revision_mask: SYSC SoC ID revision mask + * @specific_id_mask: SYSC SoC ID specific ID mask + */ +struct rz_sysc_soc_id_init_data { + const char * const family; + u32 id; + u32 offset; + u32 revision_mask; + u32 specific_id_mask; +}; + /** * struct rz_sysc_init_data - RZ SYSC initialization data + * @soc_id_init_data: RZ SYSC SoC ID 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_soc_id_init_data *soc_id_init_data; const struct rz_sysc_signal_init_data *signals_init_data; u32 num_signals; u32 max_register_offset;