Add SoC identification driver for Renesas SoC's. This allows to identify the SoC type and revision based on Product Register.
This can be checked where needed using soc_device_match(). Signed-off-by: Biju Das <biju.das...@bp.renesas.com> Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad...@bp.renesas.com> --- v2: New patch --- drivers/soc/Kconfig | 7 ++ drivers/soc/Makefile | 1 + drivers/soc/soc_renesas.c | 239 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 drivers/soc/soc_renesas.c diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index 864d00a885..475e94cd77 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -16,6 +16,13 @@ config SOC_DEVICE_TI_K3 This allows Texas Instruments Keystone 3 SoCs to identify specifics about the SoC in use. +config SOC_DEVICE_RENESAS + depends on SOC_DEVICE + bool "Enable SoC driver for Renesas SoCs" + help + This allows Renesas SoCs to identify specifics about the + SoC in use. + source "drivers/soc/ti/Kconfig" endmenu diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 9ef20ca506..b143eac5fd 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_SOC_TI) += ti/ obj-$(CONFIG_SOC_DEVICE) += soc-uclass.o +obj-$(CONFIG_$(SPL_)SOC_DEVICE_RENESAS) += soc_renesas.o obj-$(CONFIG_SOC_DEVICE_TI_K3) += soc_ti_k3.o obj-$(CONFIG_SANDBOX) += soc_sandbox.o diff --git a/drivers/soc/soc_renesas.c b/drivers/soc/soc_renesas.c new file mode 100644 index 0000000000..4543e0dbfd --- /dev/null +++ b/drivers/soc/soc_renesas.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Renesas Electronics Corp. + * + */ + +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <soc.h> + +#include <asm/io.h> + +struct soc_renesas_priv { + const char *family; + const char *soc_id; + char revision[6]; +}; + +struct renesas_family { + const char name[16]; + u32 reg; /* CCCR or PRR, if not in DT */ +}; + +static const struct renesas_family fam_rcar_gen3 __maybe_unused = { + .name = "R-Car Gen3", + .reg = 0xfff00044, /* PRR (Product Register) */ +}; + +static const struct renesas_family fam_rzg2 __maybe_unused = { + .name = "RZ/G2", + .reg = 0xfff00044, /* PRR (Product Register) */ +}; + +struct renesas_soc { + const struct renesas_family *family; + u8 id; +}; + +#ifdef CONFIG_R8A774A1 +static const struct renesas_soc soc_rz_g2m = { + .family = &fam_rzg2, + .id = 0x52, +}; +#endif + +#ifdef CONFIG_R8A774B1 +static const struct renesas_soc soc_rz_g2n = { + .family = &fam_rzg2, + .id = 0x55, +}; +#endif + +#ifdef CONFIG_R8A774C0 +static const struct renesas_soc soc_rz_g2e = { + .family = &fam_rzg2, + .id = 0x57, +}; +#endif + +#ifdef CONFIG_R8A774E1 +static const struct renesas_soc soc_rz_g2h = { + .family = &fam_rzg2, + .id = 0x4f, +}; +#endif + +#ifdef CONFIG_R8A7795 +static const struct renesas_soc soc_rcar_h3 = { + .family = &fam_rcar_gen3, + .id = 0x4f, +}; +#endif + +#ifdef CONFIG_R8A7796 +static const struct renesas_soc soc_rcar_m3_w = { + .family = &fam_rcar_gen3, + .id = 0x52, +}; +#endif + +#ifdef CONFIG_R8A77965 +static const struct renesas_soc soc_rcar_m3_n = { + .family = &fam_rcar_gen3, + .id = 0x55, +}; +#endif + +#ifdef CONFIG_R8A77970 +static const struct renesas_soc soc_rcar_v3m = { + .family = &fam_rcar_gen3, + .id = 0x54, +}; +#endif + +#ifdef CONFIG_R8A77980 +static const struct renesas_soc soc_rcar_v3h = { + .family = &fam_rcar_gen3, + .id = 0x56, +}; +#endif + +#ifdef CONFIG_R8A77990 +static const struct renesas_soc soc_rcar_e3 = { + .family = &fam_rcar_gen3, + .id = 0x57, +}; +#endif + +#ifdef CONFIG_R8A77995 +static const struct renesas_soc soc_rcar_d3 = { + .family = &fam_rcar_gen3, + .id = 0x58, +}; +#endif + +static int soc_renesas_get_family(struct udevice *dev, char *buf, int size) +{ + struct soc_renesas_priv *priv = dev_get_priv(dev); + + snprintf(buf, size, "%s", priv->family); + + return 0; +} + +static int soc_renesas_get_revision(struct udevice *dev, char *buf, int size) +{ + struct soc_renesas_priv *priv = dev_get_priv(dev); + + snprintf(buf, size, "%s", priv->revision); + + return 0; +} + +static int soc_renesas_get_soc_id(struct udevice *dev, char *buf, int size) +{ + struct soc_renesas_priv *priv = dev_get_priv(dev); + + snprintf(buf, size, "%s", priv->soc_id); + + return 0; +} + +static const struct udevice_id renesas_socs[] = { +#ifdef CONFIG_R8A774A1 + { .compatible = "renesas,r8a774a1", .data = (ulong)&soc_rz_g2m, }, +#endif +#ifdef CONFIG_R8A774B1 + { .compatible = "renesas,r8a774b1", .data = (ulong)&soc_rz_g2n, }, +#endif +#ifdef CONFIG_R8A774C0 + { .compatible = "renesas,r8a774c0", .data = (ulong)&soc_rz_g2e, }, +#endif +#ifdef CONFIG_R8A774E1 + { .compatible = "renesas,r8a774e1", .data = (ulong)&soc_rz_g2h, }, +#endif +#ifdef CONFIG_R8A7795 + { .compatible = "renesas,r8a7795", .data = (ulong)&soc_rcar_h3, }, +#endif +#ifdef CONFIG_R8A7796 + { .compatible = "renesas,r8a7796", .data = (ulong)&soc_rcar_m3_w, }, +#endif +#ifdef CONFIG_R8A77965 + { .compatible = "renesas,r8a77965", .data = (ulong)&soc_rcar_m3_n, }, +#endif +#ifdef CONFIG_R8A77970 + { .compatible = "renesas,r8a77970", .data = (ulong)&soc_rcar_v3m, }, +#endif +#ifdef CONFIG_R8A77980 + { .compatible = "renesas,r8a77980", .data = (ulong)&soc_rcar_v3h, }, +#endif +#ifdef CONFIG_R8A77990 + { .compatible = "renesas,r8a77990", .data = (ulong)&soc_rcar_e3, }, +#endif +#ifdef CONFIG_R8A77995 + { .compatible = "renesas,r8a77995", .data = (ulong)&soc_rcar_d3, }, +#endif + { /* sentinel */ } +}; + +static const struct udevice_id prr_ids[] = { + { .compatible = "renesas,prr" }, + { /* sentinel */ } +}; + +static const struct soc_ops soc_renesas_ops = { + .get_family = soc_renesas_get_family, + .get_revision = soc_renesas_get_revision, + .get_soc_id = soc_renesas_get_soc_id, +}; + +int soc_renesas_probe(struct udevice *dev) +{ + struct soc_renesas_priv *priv = dev_get_priv(dev); + const struct renesas_family *family; + const struct udevice_id *match; + struct renesas_soc *soc; + unsigned int product, eshi = 0, eslo; + ofnode root_node; + + root_node = ofnode_path("/"); + match = of_match_node(renesas_socs, root_node); + if (!match) { + printf("SoC entry is missing in DT\n"); + return 0; + } + + soc = (struct renesas_soc *)match->data; + family = soc->family; + + product = readl(0xFFF00044); + /* 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; + if (soc->id && ((product >> 8) & 0xff) != soc->id) { + printf("SoC mismatch (product = 0x%x)\n", product); + return 0; + } + eshi = ((product >> 4) & 0x0f) + 1; + eslo = product & 0xf; + + priv->family = family->name; + priv->soc_id = strchr(match->compatible, ',') + 1; + snprintf(priv->revision, sizeof(priv->revision), "ES%u.%u", eshi, eslo); + + return 0; +} + +U_BOOT_DRIVER(soc_renesas) = { + .name = "soc_renesas", + .id = UCLASS_SOC, + .ops = &soc_renesas_ops, + .of_match = prr_ids, + .probe = soc_renesas_probe, + .priv_auto_alloc_size = sizeof(struct soc_renesas_priv), +}; -- 2.17.1