From: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxx> --- 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