From 44b5100f7b744b2cd2a29f8766eea5dbf741e0e5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 28 Aug 2019 13:36:12 +0200 Subject: soc: renesas: rcar-sysc: Prepare for fixing power request conflicts Recent R-Car Gen3 SoCs added an External Request Mask Register to the System Controller (SYSC). This register allows to mask external power requests for CPU or 3DG domains, to prevent conflicts between powering off CPU cores or the 3D Graphics Engine, and changing the state of another power domain through SYSC, which could lead to CPG state machine lock-ups. Add support for making use of this register. Take into account that the register is optional, and that its location and contents are SoC-specific. Note that the issue fixed by this cannot happen in the upstream kernel, as upstream has no support for graphics acceleration yet. SoCs lacking the External Request Mask Register may need a different mitigation in the future. Inspired by a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190828113618.6672-2-geert+renesas@glider.be --- drivers/soc/renesas/rcar-sysc.c | 16 ++++++++++++++++ drivers/soc/renesas/rcar-sysc.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 59b5e6b10272..176de145f423 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -63,6 +63,7 @@ struct rcar_sysc_ch { static void __iomem *rcar_sysc_base; static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ +static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val; static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) { @@ -105,6 +106,14 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) spin_lock_irqsave(&rcar_sysc_lock, flags); + /* + * Mask external power requests for CPU or 3DG domains + */ + if (rcar_sysc_extmask_val) { + iowrite32(rcar_sysc_extmask_val, + rcar_sysc_base + rcar_sysc_extmask_offs); + } + /* * The interrupt source needs to be enabled, but masked, to prevent the * CPU from receiving it. @@ -148,6 +157,9 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); out: + if (rcar_sysc_extmask_val) + iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs); + spin_unlock_irqrestore(&rcar_sysc_lock, flags); pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", @@ -360,6 +372,10 @@ static int __init rcar_sysc_pd_init(void) rcar_sysc_base = base; + /* Optional External Request Mask Register */ + rcar_sysc_extmask_offs = info->extmask_offs; + rcar_sysc_extmask_val = info->extmask_val; + domains = kzalloc(sizeof(*domains), GFP_KERNEL); if (!domains) { error = -ENOMEM; diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 485520a5b295..21ee3ff8620b 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -44,6 +44,9 @@ struct rcar_sysc_info { int (*init)(void); /* Optional */ const struct rcar_sysc_area *areas; unsigned int num_areas; + /* Optional External Request Mask Register */ + u32 extmask_offs; /* SYSCEXTMASK register offset */ + u32 extmask_val; /* SYSCEXTMASK register mask value */ }; extern const struct rcar_sysc_info r8a7743_sysc_info; -- cgit v1.2.3 From 0e0c4db2fa0982af0956c0eed0a94e0b412ef2f4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 28 Aug 2019 13:36:13 +0200 Subject: soc: renesas: r8a7795-sysc: Fix power request conflicts Describe the location and contents of the SYSCEXTMASK register on R-Car H3, to prevent conflicts between internal and external power requests. This register does not exist on R-Car H3 ES1.x and ES2.x. Based on a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190828113618.6672-3-geert+renesas@glider.be --- drivers/soc/renesas/r8a7795-sysc.c | 32 +++++++++++++++++++++++++++----- drivers/soc/renesas/rcar-sysc.h | 2 +- 2 files changed, 28 insertions(+), 6 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a7795-sysc.c b/drivers/soc/renesas/r8a7795-sysc.c index cda27a67de98..7e15cd09c4eb 100644 --- a/drivers/soc/renesas/r8a7795-sysc.c +++ b/drivers/soc/renesas/r8a7795-sysc.c @@ -5,6 +5,7 @@ * Copyright (C) 2016-2017 Glider bvba */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/kernel.h> #include <linux/sys_soc.h> @@ -51,25 +52,46 @@ static struct rcar_sysc_area r8a7795_areas[] __initdata = { /* - * Fixups for R-Car H3 revisions after ES1.x + * Fixups for R-Car H3 revisions */ -static const struct soc_device_attribute r8a7795es1[] __initconst = { - { .soc_id = "r8a7795", .revision = "ES1.*" }, +#define HAS_A2VC0 BIT(0) /* Power domain A2VC0 is present */ +#define NO_EXTMASK BIT(1) /* Missing SYSCEXTMASK register */ + +static const struct soc_device_attribute r8a7795_quirks_match[] __initconst = { + { + .soc_id = "r8a7795", .revision = "ES1.*", + .data = (void *)(HAS_A2VC0 | NO_EXTMASK), + }, { + .soc_id = "r8a7795", .revision = "ES2.*", + .data = (void *)(NO_EXTMASK), + }, { /* sentinel */ } }; static int __init r8a7795_sysc_init(void) { - if (!soc_device_match(r8a7795es1)) + const struct soc_device_attribute *attr; + u32 quirks = 0; + + attr = soc_device_match(r8a7795_quirks_match); + if (attr) + quirks = (uintptr_t)attr->data; + + if (!(quirks & HAS_A2VC0)) rcar_sysc_nullify(r8a7795_areas, ARRAY_SIZE(r8a7795_areas), R8A7795_PD_A2VC0); + if (quirks & NO_EXTMASK) + r8a7795_sysc_info.extmask_val = 0; + return 0; } -const struct rcar_sysc_info r8a7795_sysc_info __initconst = { +struct rcar_sysc_info r8a7795_sysc_info __initdata = { .init = r8a7795_sysc_init, .areas = r8a7795_areas, .num_areas = ARRAY_SIZE(r8a7795_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), }; diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 21ee3ff8620b..499797a9e18c 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -59,7 +59,7 @@ extern const struct rcar_sysc_info r8a7790_sysc_info; extern const struct rcar_sysc_info r8a7791_sysc_info; extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; -extern const struct rcar_sysc_info r8a7795_sysc_info; +extern struct rcar_sysc_info r8a7795_sysc_info; extern const struct rcar_sysc_info r8a7796_sysc_info; extern const struct rcar_sysc_info r8a77965_sysc_info; extern const struct rcar_sysc_info r8a77970_sysc_info; -- cgit v1.2.3 From 5a6cf826b37c5cd31304ba42117f632ee1f0552f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 28 Aug 2019 13:36:14 +0200 Subject: soc: renesas: r8a7796-sysc: Fix power request conflicts Describe the location and contents of the SYSCEXTMASK register on R-Car M3-W, to prevent conflicts between internal and external power requests. This register does not exist on R-Car M3-W ES1.x. Based on a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190828113618.6672-4-geert+renesas@glider.be --- drivers/soc/renesas/r8a7796-sysc.c | 22 +++++++++++++++++++++- drivers/soc/renesas/rcar-sysc.h | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a7796-sysc.c b/drivers/soc/renesas/r8a7796-sysc.c index 1b06f868b6e8..a4eaa8d1f5d0 100644 --- a/drivers/soc/renesas/r8a7796-sysc.c +++ b/drivers/soc/renesas/r8a7796-sysc.c @@ -5,8 +5,10 @@ * Copyright (C) 2016 Glider bvba */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/kernel.h> +#include <linux/sys_soc.h> #include <dt-bindings/power/r8a7796-sysc.h> @@ -39,7 +41,25 @@ static const struct rcar_sysc_area r8a7796_areas[] __initconst = { { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON }, }; -const struct rcar_sysc_info r8a7796_sysc_info __initconst = { + +/* Fixups for R-Car M3-W ES1.x revision */ +static const struct soc_device_attribute r8a7796es1[] __initconst = { + { .soc_id = "r8a7796", .revision = "ES1.*" }, + { /* sentinel */ } +}; + +static int __init r8a7796_sysc_init(void) +{ + if (soc_device_match(r8a7796es1)) + r8a7796_sysc_info.extmask_val = 0; + + return 0; +} + +struct rcar_sysc_info r8a7796_sysc_info __initdata = { + .init = r8a7796_sysc_init, .areas = r8a7796_areas, .num_areas = ARRAY_SIZE(r8a7796_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), }; diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 499797a9e18c..64c2a0fa7945 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -60,7 +60,7 @@ extern const struct rcar_sysc_info r8a7791_sysc_info; extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; extern struct rcar_sysc_info r8a7795_sysc_info; -extern const struct rcar_sysc_info r8a7796_sysc_info; +extern struct rcar_sysc_info r8a7796_sysc_info; extern const struct rcar_sysc_info r8a77965_sysc_info; extern const struct rcar_sysc_info r8a77970_sysc_info; extern const struct rcar_sysc_info r8a77980_sysc_info; -- cgit v1.2.3 From fd733519436fb56fc89265f42276bfac04a14cfc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 28 Aug 2019 13:36:15 +0200 Subject: soc: renesas: r8a77965-sysc: Fix power request conflicts Describe the location and contents of the SYSCEXTMASK register on R-Car M3-N, to prevent conflicts between internal and external power requests. Based on a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190828113618.6672-5-geert+renesas@glider.be --- drivers/soc/renesas/r8a77965-sysc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a77965-sysc.c b/drivers/soc/renesas/r8a77965-sysc.c index e0533beb50fd..b5d8230d4bbc 100644 --- a/drivers/soc/renesas/r8a77965-sysc.c +++ b/drivers/soc/renesas/r8a77965-sysc.c @@ -7,6 +7,7 @@ * Copyright (C) 2016 Glider bvba */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/kernel.h> @@ -33,4 +34,6 @@ static const struct rcar_sysc_area r8a77965_areas[] __initconst = { const struct rcar_sysc_info r8a77965_sysc_info __initconst = { .areas = r8a77965_areas, .num_areas = ARRAY_SIZE(r8a77965_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), }; -- cgit v1.2.3 From a228560c52e89aa5e31c5b8a636bf9ab681bda84 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 28 Aug 2019 13:36:16 +0200 Subject: soc: renesas: r8a77970-sysc: Fix power request conflicts Describe the location and contents of the SYSCEXTMASK register on R-Car V3M, to prevent conflicts between internal and external power requests. Based on a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190828113618.6672-6-geert+renesas@glider.be --- drivers/soc/renesas/r8a77970-sysc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c index 280c48b80f24..b3033e3f0fd0 100644 --- a/drivers/soc/renesas/r8a77970-sysc.c +++ b/drivers/soc/renesas/r8a77970-sysc.c @@ -5,6 +5,7 @@ * Copyright (C) 2017 Cogent Embedded Inc. */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/kernel.h> @@ -32,4 +33,6 @@ static const struct rcar_sysc_area r8a77970_areas[] __initconst = { const struct rcar_sysc_info r8a77970_sysc_info __initconst = { .areas = r8a77970_areas, .num_areas = ARRAY_SIZE(r8a77970_areas), + .extmask_offs = 0x1b0, + .extmask_val = BIT(0), }; -- cgit v1.2.3 From ee038b88c60a32908fd83d420fce445bd0138f6d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 28 Aug 2019 13:36:17 +0200 Subject: soc: renesas: r8a77980-sysc: Fix power request conflicts Describe the location and contents of the SYSCEXTMASK register on R-Car V3H, to prevent conflicts between internal and external power requests. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190828113618.6672-7-geert+renesas@glider.be --- drivers/soc/renesas/r8a77980-sysc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a77980-sysc.c b/drivers/soc/renesas/r8a77980-sysc.c index a8dbe55e8ba8..e3b5ee1b3091 100644 --- a/drivers/soc/renesas/r8a77980-sysc.c +++ b/drivers/soc/renesas/r8a77980-sysc.c @@ -6,6 +6,7 @@ * Copyright (C) 2018 Cogent Embedded, Inc. */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/kernel.h> @@ -49,4 +50,6 @@ static const struct rcar_sysc_area r8a77980_areas[] __initconst = { const struct rcar_sysc_info r8a77980_sysc_info __initconst = { .areas = r8a77980_areas, .num_areas = ARRAY_SIZE(r8a77980_areas), + .extmask_offs = 0x138, + .extmask_val = BIT(0), }; -- cgit v1.2.3 From b34095cbc2bb102c9f138afd8c99a8a205b0e142 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 28 Aug 2019 13:36:18 +0200 Subject: soc: renesas: r8a77990-sysc: Fix power request conflicts Describe the location and contents of the SYSCEXTMASK register on R-Car E3, to prevent conflicts between internal and external power requests. Based on a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190828113618.6672-8-geert+renesas@glider.be --- drivers/soc/renesas/r8a77990-sysc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a77990-sysc.c b/drivers/soc/renesas/r8a77990-sysc.c index 664b244eb1dd..1d2432020b7a 100644 --- a/drivers/soc/renesas/r8a77990-sysc.c +++ b/drivers/soc/renesas/r8a77990-sysc.c @@ -5,6 +5,7 @@ * Copyright (C) 2018 Renesas Electronics Corp. */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/kernel.h> #include <linux/sys_soc.h> @@ -50,4 +51,6 @@ const struct rcar_sysc_info r8a77990_sysc_info __initconst = { .init = r8a77990_sysc_init, .areas = r8a77990_areas, .num_areas = ARRAY_SIZE(r8a77990_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), }; -- cgit v1.2.3 From d634055c4b0f8c24269959f64b7d4a1a8d87d630 Mon Sep 17 00:00:00 2001 From: Biju Das <biju.das@bp.renesas.com> Date: Thu, 5 Sep 2019 10:30:43 +0100 Subject: soc: renesas: Add Renesas R8A774B1 config option Add configuration option for the RZ/G2N (R8A774B1) SoC. Signed-off-by: Biju Das <biju.das@bp.renesas.com> Link: https://lore.kernel.org/r/1567675844-19247-4-git-send-email-biju.das@bp.renesas.com Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> --- drivers/soc/renesas/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 3c5e017bacba..d6a7df3588c8 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -178,6 +178,12 @@ config ARCH_R8A774A1 help This enables support for the Renesas RZ/G2M SoC. +config ARCH_R8A774B1 + bool "Renesas RZ/G2N SoC Platform" + select ARCH_RCAR_GEN3 + help + This enables support for the Renesas RZ/G2N SoC. + config ARCH_R8A774C0 bool "Renesas RZ/G2E SoC Platform" select ARCH_RCAR_GEN3 -- cgit v1.2.3 From 574cb721729fccbc252b0caa5ee401393d06c49e Mon Sep 17 00:00:00 2001 From: Biju Das <biju.das@bp.renesas.com> Date: Thu, 5 Sep 2019 10:30:42 +0100 Subject: soc: renesas: Identify RZ/G2N This patch adds support for identifying the RZ/G2N (r8a774b1) SoC. Signed-off-by: Biju Das <biju.das@bp.renesas.com> Link: https://lore.kernel.org/r/1567675844-19247-3-git-send-email-biju.das@bp.renesas.com Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> --- drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 3299cf5365f3..45135bc88e27 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -116,6 +116,11 @@ static const struct renesas_soc soc_rz_g2m __initconst __maybe_unused = { .id = 0x52, }; +static const struct renesas_soc soc_rz_g2n __initconst __maybe_unused = { + .family = &fam_rzg2, + .id = 0x55, +}; + static const struct renesas_soc soc_rz_g2e __initconst __maybe_unused = { .family = &fam_rzg2, .id = 0x57, @@ -227,6 +232,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A774A1 { .compatible = "renesas,r8a774a1", .data = &soc_rz_g2m }, #endif +#ifdef CONFIG_ARCH_R8A774B1 + { .compatible = "renesas,r8a774b1", .data = &soc_rz_g2n }, +#endif #ifdef CONFIG_ARCH_R8A774C0 { .compatible = "renesas,r8a774c0", .data = &soc_rz_g2e }, #endif -- cgit v1.2.3 From 26405045e73b184900ad43206eeda7bee1cedee1 Mon Sep 17 00:00:00 2001 From: Biju Das <biju.das@bp.renesas.com> Date: Thu, 19 Sep 2019 09:17:12 +0100 Subject: soc: renesas: rcar-rst: Add support for RZ/G2N Add support for RZ/G2N (R8A774B1) to the R-Car RST driver. Signed-off-by: Biju Das <biju.das@bp.renesas.com> Link: https://lore.kernel.org/r/1568881036-4404-5-git-send-email-biju.das@bp.renesas.com Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> --- drivers/soc/renesas/rcar-rst.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index d183c381e8db..cd5592977cef 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -45,6 +45,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 }, /* RZ/G2 is handled like R-Car Gen3 */ { .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 }, /* R-Car Gen1 */ { .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 }, -- cgit v1.2.3 From 8c32c5ff873580a5bff1b743ac5e080b36fbd784 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Fri, 20 Sep 2019 16:35:23 +0200 Subject: soc: renesas: r8a774c0-sysc: Fix power request conflicts Describe the location and contents of the SYSCEXTMASK register on RZ/G2E, to prevent conflicts between internal and external power requests. Based on a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190920143523.23125-1-geert+renesas@glider.be --- drivers/soc/renesas/r8a774c0-sysc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a774c0-sysc.c b/drivers/soc/renesas/r8a774c0-sysc.c index 11050e17ea81..40d1558aab37 100644 --- a/drivers/soc/renesas/r8a774c0-sysc.c +++ b/drivers/soc/renesas/r8a774c0-sysc.c @@ -6,6 +6,7 @@ * Based on Renesas R-Car E3 System Controller */ +#include <linux/bits.h> #include <linux/bug.h> #include <linux/kernel.h> #include <linux/sys_soc.h> @@ -50,4 +51,6 @@ const struct rcar_sysc_info r8a774c0_sysc_info __initconst = { .init = r8a774c0_sysc_init, .areas = r8a774c0_areas, .num_areas = ARRAY_SIZE(r8a774c0_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), }; -- cgit v1.2.3 From 3b1600c515a50417fd08488b844199bd23919f28 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Fri, 20 Sep 2019 16:47:05 +0200 Subject: soc: renesas: rcar-sysc: Remove unneeded inclusion of <linux/bug.h> No R-Car or RZ/G SYSC driver uses any of the definitions provided by <linux/bug.h>, hence there is no need to include this header file. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Link: https://lore.kernel.org/r/20190920144705.27394-1-geert+renesas@glider.be --- drivers/soc/renesas/r8a7743-sysc.c | 1 - drivers/soc/renesas/r8a7745-sysc.c | 1 - drivers/soc/renesas/r8a77470-sysc.c | 1 - drivers/soc/renesas/r8a774a1-sysc.c | 1 - drivers/soc/renesas/r8a774c0-sysc.c | 1 - drivers/soc/renesas/r8a7779-sysc.c | 1 - drivers/soc/renesas/r8a7790-sysc.c | 1 - drivers/soc/renesas/r8a7791-sysc.c | 1 - drivers/soc/renesas/r8a7792-sysc.c | 1 - drivers/soc/renesas/r8a7794-sysc.c | 1 - drivers/soc/renesas/r8a7795-sysc.c | 1 - drivers/soc/renesas/r8a7796-sysc.c | 1 - drivers/soc/renesas/r8a77965-sysc.c | 1 - drivers/soc/renesas/r8a77970-sysc.c | 1 - drivers/soc/renesas/r8a77980-sysc.c | 1 - drivers/soc/renesas/r8a77990-sysc.c | 1 - drivers/soc/renesas/r8a77995-sysc.c | 1 - 17 files changed, 17 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/r8a7743-sysc.c b/drivers/soc/renesas/r8a7743-sysc.c index edf6436e879f..4e2c0ab951b3 100644 --- a/drivers/soc/renesas/r8a7743-sysc.c +++ b/drivers/soc/renesas/r8a7743-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2016 Cogent Embedded Inc. */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a7743-sysc.h> diff --git a/drivers/soc/renesas/r8a7745-sysc.c b/drivers/soc/renesas/r8a7745-sysc.c index 65dc6b09cc85..865821a2f0c6 100644 --- a/drivers/soc/renesas/r8a7745-sysc.c +++ b/drivers/soc/renesas/r8a7745-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2016 Cogent Embedded Inc. */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a7745-sysc.h> diff --git a/drivers/soc/renesas/r8a77470-sysc.c b/drivers/soc/renesas/r8a77470-sysc.c index cfa015e208ef..1eeb8018df50 100644 --- a/drivers/soc/renesas/r8a77470-sysc.c +++ b/drivers/soc/renesas/r8a77470-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2018 Renesas Electronics Corp. */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a77470-sysc.h> diff --git a/drivers/soc/renesas/r8a774a1-sysc.c b/drivers/soc/renesas/r8a774a1-sysc.c index 9db51ff6f5ed..38ac2c689ff0 100644 --- a/drivers/soc/renesas/r8a774a1-sysc.c +++ b/drivers/soc/renesas/r8a774a1-sysc.c @@ -7,7 +7,6 @@ * Copyright (C) 2016 Glider bvba */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a774a1-sysc.h> diff --git a/drivers/soc/renesas/r8a774c0-sysc.c b/drivers/soc/renesas/r8a774c0-sysc.c index 40d1558aab37..c1c216f7d073 100644 --- a/drivers/soc/renesas/r8a774c0-sysc.c +++ b/drivers/soc/renesas/r8a774c0-sysc.c @@ -7,7 +7,6 @@ */ #include <linux/bits.h> -#include <linux/bug.h> #include <linux/kernel.h> #include <linux/sys_soc.h> diff --git a/drivers/soc/renesas/r8a7779-sysc.c b/drivers/soc/renesas/r8a7779-sysc.c index 517aa40fa6e6..e24a7151d55f 100644 --- a/drivers/soc/renesas/r8a7779-sysc.c +++ b/drivers/soc/renesas/r8a7779-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2016 Glider bvba */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a7779-sysc.h> diff --git a/drivers/soc/renesas/r8a7790-sysc.c b/drivers/soc/renesas/r8a7790-sysc.c index 9b5a6bb62152..b9afe7f6245b 100644 --- a/drivers/soc/renesas/r8a7790-sysc.c +++ b/drivers/soc/renesas/r8a7790-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2016 Glider bvba */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a7790-sysc.h> diff --git a/drivers/soc/renesas/r8a7791-sysc.c b/drivers/soc/renesas/r8a7791-sysc.c index acf545cdebfb..f00fa24522a3 100644 --- a/drivers/soc/renesas/r8a7791-sysc.c +++ b/drivers/soc/renesas/r8a7791-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2016 Glider bvba */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a7791-sysc.h> diff --git a/drivers/soc/renesas/r8a7792-sysc.c b/drivers/soc/renesas/r8a7792-sysc.c index 05b78525cc43..60aae242c43f 100644 --- a/drivers/soc/renesas/r8a7792-sysc.c +++ b/drivers/soc/renesas/r8a7792-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2016 Cogent Embedded Inc. */ -#include <linux/bug.h> #include <linux/init.h> #include <linux/kernel.h> diff --git a/drivers/soc/renesas/r8a7794-sysc.c b/drivers/soc/renesas/r8a7794-sysc.c index 0d42637fa662..72ef4e85458f 100644 --- a/drivers/soc/renesas/r8a7794-sysc.c +++ b/drivers/soc/renesas/r8a7794-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2016 Glider bvba */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a7794-sysc.h> diff --git a/drivers/soc/renesas/r8a7795-sysc.c b/drivers/soc/renesas/r8a7795-sysc.c index 7e15cd09c4eb..91074411b8cf 100644 --- a/drivers/soc/renesas/r8a7795-sysc.c +++ b/drivers/soc/renesas/r8a7795-sysc.c @@ -6,7 +6,6 @@ */ #include <linux/bits.h> -#include <linux/bug.h> #include <linux/kernel.h> #include <linux/sys_soc.h> diff --git a/drivers/soc/renesas/r8a7796-sysc.c b/drivers/soc/renesas/r8a7796-sysc.c index a4eaa8d1f5d0..d374622a667b 100644 --- a/drivers/soc/renesas/r8a7796-sysc.c +++ b/drivers/soc/renesas/r8a7796-sysc.c @@ -6,7 +6,6 @@ */ #include <linux/bits.h> -#include <linux/bug.h> #include <linux/kernel.h> #include <linux/sys_soc.h> diff --git a/drivers/soc/renesas/r8a77965-sysc.c b/drivers/soc/renesas/r8a77965-sysc.c index b5d8230d4bbc..ff0b0d116992 100644 --- a/drivers/soc/renesas/r8a77965-sysc.c +++ b/drivers/soc/renesas/r8a77965-sysc.c @@ -8,7 +8,6 @@ */ #include <linux/bits.h> -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a77965-sysc.h> diff --git a/drivers/soc/renesas/r8a77970-sysc.c b/drivers/soc/renesas/r8a77970-sysc.c index b3033e3f0fd0..706258250600 100644 --- a/drivers/soc/renesas/r8a77970-sysc.c +++ b/drivers/soc/renesas/r8a77970-sysc.c @@ -6,7 +6,6 @@ */ #include <linux/bits.h> -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a77970-sysc.h> diff --git a/drivers/soc/renesas/r8a77980-sysc.c b/drivers/soc/renesas/r8a77980-sysc.c index e3b5ee1b3091..39ca84a67daa 100644 --- a/drivers/soc/renesas/r8a77980-sysc.c +++ b/drivers/soc/renesas/r8a77980-sysc.c @@ -7,7 +7,6 @@ */ #include <linux/bits.h> -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a77980-sysc.h> diff --git a/drivers/soc/renesas/r8a77990-sysc.c b/drivers/soc/renesas/r8a77990-sysc.c index 1d2432020b7a..9f92737dc352 100644 --- a/drivers/soc/renesas/r8a77990-sysc.c +++ b/drivers/soc/renesas/r8a77990-sysc.c @@ -6,7 +6,6 @@ */ #include <linux/bits.h> -#include <linux/bug.h> #include <linux/kernel.h> #include <linux/sys_soc.h> diff --git a/drivers/soc/renesas/r8a77995-sysc.c b/drivers/soc/renesas/r8a77995-sysc.c index 6243aaaf60fb..efcc67e3d76d 100644 --- a/drivers/soc/renesas/r8a77995-sysc.c +++ b/drivers/soc/renesas/r8a77995-sysc.c @@ -5,7 +5,6 @@ * Copyright (C) 2017 Glider bvba */ -#include <linux/bug.h> #include <linux/kernel.h> #include <dt-bindings/power/r8a77995-sysc.h> -- cgit v1.2.3 From 6655c568ced0789479f00b9399603c5d6ee48640 Mon Sep 17 00:00:00 2001 From: Biju Das <biju.das@bp.renesas.com> Date: Mon, 23 Sep 2019 08:29:40 +0100 Subject: soc: renesas: rcar-sysc: Add r8a774b1 support Add support for RZ/G2N (R8A774B1) SoC power areas to the R-Car SYSC driver. Signed-off-by: Biju Das <biju.das@bp.renesas.com> Link: https://lore.kernel.org/r/1569223780-54304-1-git-send-email-biju.das@bp.renesas.com Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> --- drivers/soc/renesas/Kconfig | 5 +++++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a774b1-sysc.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/soc/renesas/rcar-sysc.c | 3 +++ drivers/soc/renesas/rcar-sysc.h | 1 + 5 files changed, 47 insertions(+) create mode 100644 drivers/soc/renesas/r8a774b1-sysc.c (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index d6a7df3588c8..3bd0c218bf30 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -181,6 +181,7 @@ config ARCH_R8A774A1 config ARCH_R8A774B1 bool "Renesas RZ/G2N SoC Platform" select ARCH_RCAR_GEN3 + select SYSC_R8A774B1 help This enables support for the Renesas RZ/G2N SoC. @@ -259,6 +260,10 @@ config SYSC_R8A774A1 bool "RZ/G2M System Controller support" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A774B1 + bool "RZ/G2N System Controller support" if COMPILE_TEST + select SYSC_RCAR + config SYSC_R8A774C0 bool "RZ/G2E System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 00764d5a60b3..e99dc37ea120 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o +obj-$(CONFIG_SYSC_R8A774B1) += r8a774b1-sysc.o obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o diff --git a/drivers/soc/renesas/r8a774b1-sysc.c b/drivers/soc/renesas/r8a774b1-sysc.c new file mode 100644 index 000000000000..5f97ff26f3f8 --- /dev/null +++ b/drivers/soc/renesas/r8a774b1-sysc.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2N System Controller + * Copyright (C) 2019 Renesas Electronics Corp. + * + * Based on Renesas R-Car M3-W System Controller + * Copyright (C) 2016 Glider bvba + */ + +#include <linux/bits.h> +#include <linux/kernel.h> + +#include <dt-bindings/power/r8a774b1-sysc.h> + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a774b1_areas[] __initconst = { + { "always-on", 0, 0, R8A774B1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A774B1_PD_CA57_SCU, R8A774B1_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A774B1_PD_CA57_CPU0, R8A774B1_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A774B1_PD_CA57_CPU1, R8A774B1_PD_CA57_SCU, + PD_CPU_NOCR }, + { "a3vc", 0x380, 0, R8A774B1_PD_A3VC, R8A774B1_PD_ALWAYS_ON }, + { "a3vp", 0x340, 0, R8A774B1_PD_A3VP, R8A774B1_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A774B1_PD_A2VC1, R8A774B1_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A774B1_PD_3DG_A, R8A774B1_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A774B1_PD_3DG_B, R8A774B1_PD_3DG_A }, +}; + +const struct rcar_sysc_info r8a774b1_sysc_info __initconst = { + .areas = r8a774b1_areas, + .num_areas = ARRAY_SIZE(r8a774b1_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 176de145f423..d4f2ed52b2b3 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -287,6 +287,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A774A1 { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A774B1 + { .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A774C0 { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, #endif diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 64c2a0fa7945..e4c9854f5dc0 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -53,6 +53,7 @@ extern const struct rcar_sysc_info r8a7743_sysc_info; extern const struct rcar_sysc_info r8a7745_sysc_info; extern const struct rcar_sysc_info r8a77470_sysc_info; extern const struct rcar_sysc_info r8a774a1_sysc_info; +extern const struct rcar_sysc_info r8a774b1_sysc_info; extern const struct rcar_sysc_info r8a774c0_sysc_info; extern const struct rcar_sysc_info r8a7779_sysc_info; extern const struct rcar_sysc_info r8a7790_sysc_info; -- cgit v1.2.3 From 0a728e0bda7c14921723362af86b0ba30e5d6c79 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam <nkristam@nvidia.com> Date: Tue, 3 Sep 2019 16:26:52 +0530 Subject: soc/tegra: fuse: Add FUSE clock check in tegra_fuse_readl() tegra_fuse_readl() can be called from drivers at any time. If this API is called before tegra_fuse_probe(), we end up enabling the clock before it is registered. Add a check for the FUSE clock in tegra_fuse_readl() and propagate any errors. Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com> Acked-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/fuse/fuse-tegra.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 3eb44e65b326..58996c6ea767 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -186,9 +186,12 @@ u32 __init tegra_fuse_read_early(unsigned int offset) int tegra_fuse_readl(unsigned long offset, u32 *value) { - if (!fuse->read) + if (!fuse->read || !fuse->clk) return -EPROBE_DEFER; + if (IS_ERR(fuse->clk)) + return PTR_ERR(fuse->clk); + *value = fuse->read(fuse, offset); return 0; -- cgit v1.2.3 From c9e753767a9c75d2044fb7343950a6a992d34a16 Mon Sep 17 00:00:00 2001 From: Thierry Reding <treding@nvidia.com> Date: Tue, 1 Oct 2019 13:48:29 +0200 Subject: soc/tegra: pmc: Fix crashes for hierarchical interrupts Interrupts that don't have an associated wake event or GPIO wake events end up with an associate IRQ chip that is NULL and which causes IRQ code to crash. This is because we don't implicitly set the parent IRQ chip by allocating the interrupt at the parent. However, there really isn't a corresponding interrupt at the parent, so we need to work around this by setting the special no_irq_chip as the IRQ chip for these interrupts. Fixes: 19906e6b1667 ("soc/tegra: pmc: Add wake event support") Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/pmc.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 9f9c1c677cf4..0447afa970f5 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1899,6 +1899,20 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq, event->id, &pmc->irq, pmc); + /* + * GPIOs don't have an equivalent interrupt in the + * parent controller (GIC). However some code, such + * as the one in irq_get_irqchip_state(), require a + * valid IRQ chip to be set. Make sure that's the + * case by passing NULL here, which will install a + * dummy IRQ chip for the interrupt in the parent + * domain. + */ + if (domain->parent) + irq_domain_set_hwirq_and_chip(domain->parent, + virq, 0, NULL, + NULL); + break; } } @@ -1908,10 +1922,22 @@ static int tegra_pmc_irq_alloc(struct irq_domain *domain, unsigned int virq, * dummy hardware IRQ number. This is used in the ->irq_set_type() * and ->irq_set_wake() callbacks to return early for these IRQs. */ - if (i == soc->num_wake_events) + if (i == soc->num_wake_events) { err = irq_domain_set_hwirq_and_chip(domain, virq, ULONG_MAX, &pmc->irq, pmc); + /* + * Interrupts without a wake event don't have a corresponding + * interrupt in the parent controller (GIC). Pass NULL for the + * chip here, which causes a dummy IRQ chip to be installed + * for the interrupt in the parent domain, to make this + * explicit. + */ + if (domain->parent) + irq_domain_set_hwirq_and_chip(domain->parent, virq, 0, + NULL, NULL); + } + return err; } -- cgit v1.2.3 From a14b820316e84310b1bad3701a8d4c9159377633 Mon Sep 17 00:00:00 2001 From: Vivek Gautam <vivek.gautam@codeaurora.org> Date: Thu, 18 Jul 2019 18:32:36 +0530 Subject: soc: qcom: llcc cleanup to get rid of sdm845 specific driver file A single file should suffice the need to program the llcc for various platforms. Get rid of sdm845 specific driver file to make way for a more generic driver. Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/Kconfig | 14 ++---- drivers/soc/qcom/Makefile | 1 - drivers/soc/qcom/llcc-sdm845.c | 100 ----------------------------------------- drivers/soc/qcom/llcc-slice.c | 60 ++++++++++++++++++++++--- 4 files changed, 56 insertions(+), 119 deletions(-) delete mode 100644 drivers/soc/qcom/llcc-sdm845.c (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 661e47acc354..c6df8b43fa6d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -58,17 +58,9 @@ config QCOM_LLCC depends on ARCH_QCOM || COMPILE_TEST help Qualcomm Technologies, Inc. platform specific - Last Level Cache Controller(LLCC) driver. This provides interfaces - to clients that use the LLCC. Say yes here to enable LLCC slice - driver. - -config QCOM_SDM845_LLCC - tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver" - depends on QCOM_LLCC - help - Say yes here to enable the LLCC driver for SDM845. This provides - data required to configure LLCC so that clients can start using the - LLCC slices. + Last Level Cache Controller(LLCC) driver for platforms such as, + SDM845. This provides interfaces to clients that use the LLCC. + Say yes here to enable LLCC slice driver. config QCOM_MDT_LOADER tristate diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 162788701a77..28d45b2e87e8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -22,6 +22,5 @@ obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o -obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c deleted file mode 100644 index 86600d97c36d..000000000000 --- a/drivers/soc/qcom/llcc-sdm845.c +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/soc/qcom/llcc-qcom.h> - -/* - * SCT(System Cache Table) entry contains of the following members: - * usecase_id: Unique id for the client's use case - * slice_id: llcc slice id for each client - * max_cap: The maximum capacity of the cache slice provided in KB - * priority: Priority of the client used to select victim line for replacement - * fixed_size: Boolean indicating if the slice has a fixed capacity - * bonus_ways: Bonus ways are additional ways to be used for any slice, - * if client ends up using more than reserved cache ways. Bonus - * ways are allocated only if they are not reserved for some - * other client. - * res_ways: Reserved ways for the cache slice, the reserved ways cannot - * be used by any other client than the one its assigned to. - * cache_mode: Each slice operates as a cache, this controls the mode of the - * slice: normal or TCM(Tightly Coupled Memory) - * probe_target_ways: Determines what ways to probe for access hit. When - * configured to 1 only bonus and reserved ways are probed. - * When configured to 0 all ways in llcc are probed. - * dis_cap_alloc: Disable capacity based allocation for a client - * retain_on_pc: If this bit is set and client has maintained active vote - * then the ways assigned to this client are not flushed on power - * collapse. - * activate_on_init: Activate the slice immediately after the SCT is programmed - */ -#define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \ - { \ - .usecase_id = uid, \ - .slice_id = sid, \ - .max_cap = mc, \ - .priority = p, \ - .fixed_size = fs, \ - .bonus_ways = bway, \ - .res_ways = rway, \ - .cache_mode = cmod, \ - .probe_target_ways = ptw, \ - .dis_cap_alloc = dca, \ - .retain_on_pc = rp, \ - .activate_on_init = a, \ - } - -static struct llcc_slice_config sdm845_data[] = { - SCT_ENTRY(LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1), - SCT_ENTRY(LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0), - SCT_ENTRY(LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1), - SCT_ENTRY(LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), -}; - -static int sdm845_qcom_llcc_remove(struct platform_device *pdev) -{ - return qcom_llcc_remove(pdev); -} - -static int sdm845_qcom_llcc_probe(struct platform_device *pdev) -{ - return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); -} - -static const struct of_device_id sdm845_qcom_llcc_of_match[] = { - { .compatible = "qcom,sdm845-llcc", }, - { } -}; - -static struct platform_driver sdm845_qcom_llcc_driver = { - .driver = { - .name = "sdm845-llcc", - .of_match_table = sdm845_qcom_llcc_of_match, - }, - .probe = sdm845_qcom_llcc_probe, - .remove = sdm845_qcom_llcc_remove, -}; -module_platform_driver(sdm845_qcom_llcc_driver); - -MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c index 9090ea12eaf3..574bb5bf20bc 100644 --- a/drivers/soc/qcom/llcc-slice.c +++ b/drivers/soc/qcom/llcc-slice.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * */ @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/of.h> #include <linux/of_device.h> #include <linux/regmap.h> #include <linux/sizes.h> @@ -46,6 +47,27 @@ #define BANK_OFFSET_STRIDE 0x80000 +static struct llcc_slice_config sdm845_data[] = { + { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, + { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, + { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, + { LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 }, + { LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 }, + { LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 }, + { LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 }, + { LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 }, + { LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 }, + { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, +}; + static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; static const struct regmap_config llcc_regmap_config = { @@ -301,13 +323,12 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev) return ret; } -int qcom_llcc_remove(struct platform_device *pdev) +static int qcom_llcc_remove(struct platform_device *pdev) { /* Set the global pointer to a error code to avoid referencing it */ drv_data = ERR_PTR(-ENODEV); return 0; } -EXPORT_SYMBOL_GPL(qcom_llcc_remove); static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, const char *name) @@ -326,8 +347,8 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); } -int qcom_llcc_probe(struct platform_device *pdev, - const struct llcc_slice_config *llcc_cfg, u32 sz) +static int qcom_llcc_probe(struct platform_device *pdev, + const struct llcc_slice_config *llcc_cfg, u32 sz) { u32 num_banks; struct device *dev = &pdev->dev; @@ -407,6 +428,31 @@ err: drv_data = ERR_PTR(-ENODEV); return ret; } -EXPORT_SYMBOL_GPL(qcom_llcc_probe); + +static int sdm845_qcom_llcc_remove(struct platform_device *pdev) +{ + return qcom_llcc_remove(pdev); +} + +static int sdm845_qcom_llcc_probe(struct platform_device *pdev) +{ + return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); +} + +static const struct of_device_id sdm845_qcom_llcc_of_match[] = { + { .compatible = "qcom,sdm845-llcc", }, + { } +}; + +static struct platform_driver sdm845_qcom_llcc_driver = { + .driver = { + .name = "sdm845-llcc", + .of_match_table = sdm845_qcom_llcc_of_match, + }, + .probe = sdm845_qcom_llcc_probe, + .remove = sdm845_qcom_llcc_remove, +}; +module_platform_driver(sdm845_qcom_llcc_driver); + +MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller"); -- cgit v1.2.3 From a0e72a5ba48ae9c6449a32130d74506a854b79d2 Mon Sep 17 00:00:00 2001 From: Vivek Gautam <vivek.gautam@codeaurora.org> Date: Thu, 18 Jul 2019 18:32:37 +0530 Subject: soc: qcom: Rename llcc-slice to llcc-qcom The cleaning up was done without changing the driver file name to ensure a cleaner bisect. Change the file name now to facilitate making the driver generic in subsequent patch. Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/Makefile | 2 +- drivers/soc/qcom/llcc-qcom.c | 458 ++++++++++++++++++++++++++++++++++++++++++ drivers/soc/qcom/llcc-slice.c | 458 ------------------------------------------ 3 files changed, 459 insertions(+), 459 deletions(-) create mode 100644 drivers/soc/qcom/llcc-qcom.c delete mode 100644 drivers/soc/qcom/llcc-slice.c (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 28d45b2e87e8..2559fe948ce0 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -21,6 +21,6 @@ obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o -obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o +obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c new file mode 100644 index 000000000000..574bb5bf20bc --- /dev/null +++ b/drivers/soc/qcom/llcc-qcom.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * + */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/soc/qcom/llcc-qcom.h> + +#define ACTIVATE BIT(0) +#define DEACTIVATE BIT(1) +#define ACT_CTRL_OPCODE_ACTIVATE BIT(0) +#define ACT_CTRL_OPCODE_DEACTIVATE BIT(1) +#define ACT_CTRL_ACT_TRIG BIT(0) +#define ACT_CTRL_OPCODE_SHIFT 0x01 +#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x02 +#define ATTR1_FIXED_SIZE_SHIFT 0x03 +#define ATTR1_PRIORITY_SHIFT 0x04 +#define ATTR1_MAX_CAP_SHIFT 0x10 +#define ATTR0_RES_WAYS_MASK GENMASK(11, 0) +#define ATTR0_BONUS_WAYS_MASK GENMASK(27, 16) +#define ATTR0_BONUS_WAYS_SHIFT 0x10 +#define LLCC_STATUS_READ_DELAY 100 + +#define CACHE_LINE_SIZE_SHIFT 6 + +#define LLCC_COMMON_STATUS0 0x0003000c +#define LLCC_LB_CNT_MASK GENMASK(31, 28) +#define LLCC_LB_CNT_SHIFT 28 + +#define MAX_CAP_TO_BYTES(n) (n * SZ_1K) +#define LLCC_TRP_ACT_CTRLn(n) (n * SZ_4K) +#define LLCC_TRP_STATUSn(n) (4 + n * SZ_4K) +#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + SZ_8 * n) +#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + SZ_8 * n) + +#define BANK_OFFSET_STRIDE 0x80000 + +static struct llcc_slice_config sdm845_data[] = { + { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, + { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, + { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, + { LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 }, + { LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 }, + { LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 }, + { LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 }, + { LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 }, + { LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 }, + { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, +}; + +static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; + +static const struct regmap_config llcc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +/** + * llcc_slice_getd - get llcc slice descriptor + * @uid: usecase_id for the client + * + * A pointer to llcc slice descriptor will be returned on success and + * and error pointer is returned on failure + */ +struct llcc_slice_desc *llcc_slice_getd(u32 uid) +{ + const struct llcc_slice_config *cfg; + struct llcc_slice_desc *desc; + u32 sz, count; + + if (IS_ERR(drv_data)) + return ERR_CAST(drv_data); + + cfg = drv_data->cfg; + sz = drv_data->cfg_size; + + for (count = 0; cfg && count < sz; count++, cfg++) + if (cfg->usecase_id == uid) + break; + + if (count == sz || !cfg) + return ERR_PTR(-ENODEV); + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) + return ERR_PTR(-ENOMEM); + + desc->slice_id = cfg->slice_id; + desc->slice_size = cfg->max_cap; + + return desc; +} +EXPORT_SYMBOL_GPL(llcc_slice_getd); + +/** + * llcc_slice_putd - llcc slice descritpor + * @desc: Pointer to llcc slice descriptor + */ +void llcc_slice_putd(struct llcc_slice_desc *desc) +{ + if (!IS_ERR_OR_NULL(desc)) + kfree(desc); +} +EXPORT_SYMBOL_GPL(llcc_slice_putd); + +static int llcc_update_act_ctrl(u32 sid, + u32 act_ctrl_reg_val, u32 status) +{ + u32 act_ctrl_reg; + u32 status_reg; + u32 slice_status; + int ret; + + if (IS_ERR(drv_data)) + return PTR_ERR(drv_data); + + act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid); + status_reg = LLCC_TRP_STATUSn(sid); + + /* Set the ACTIVE trigger */ + act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG; + ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg, + act_ctrl_reg_val); + if (ret) + return ret; + + /* Clear the ACTIVE trigger */ + act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG; + ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg, + act_ctrl_reg_val); + if (ret) + return ret; + + ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg, + slice_status, !(slice_status & status), + 0, LLCC_STATUS_READ_DELAY); + return ret; +} + +/** + * llcc_slice_activate - Activate the llcc slice + * @desc: Pointer to llcc slice descriptor + * + * A value of zero will be returned on success and a negative errno will + * be returned in error cases + */ +int llcc_slice_activate(struct llcc_slice_desc *desc) +{ + int ret; + u32 act_ctrl_val; + + if (IS_ERR(drv_data)) + return PTR_ERR(drv_data); + + if (IS_ERR_OR_NULL(desc)) + return -EINVAL; + + mutex_lock(&drv_data->lock); + if (test_bit(desc->slice_id, drv_data->bitmap)) { + mutex_unlock(&drv_data->lock); + return 0; + } + + act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT; + + ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, + DEACTIVATE); + if (ret) { + mutex_unlock(&drv_data->lock); + return ret; + } + + __set_bit(desc->slice_id, drv_data->bitmap); + mutex_unlock(&drv_data->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(llcc_slice_activate); + +/** + * llcc_slice_deactivate - Deactivate the llcc slice + * @desc: Pointer to llcc slice descriptor + * + * A value of zero will be returned on success and a negative errno will + * be returned in error cases + */ +int llcc_slice_deactivate(struct llcc_slice_desc *desc) +{ + u32 act_ctrl_val; + int ret; + + if (IS_ERR(drv_data)) + return PTR_ERR(drv_data); + + if (IS_ERR_OR_NULL(desc)) + return -EINVAL; + + mutex_lock(&drv_data->lock); + if (!test_bit(desc->slice_id, drv_data->bitmap)) { + mutex_unlock(&drv_data->lock); + return 0; + } + act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT; + + ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, + ACTIVATE); + if (ret) { + mutex_unlock(&drv_data->lock); + return ret; + } + + __clear_bit(desc->slice_id, drv_data->bitmap); + mutex_unlock(&drv_data->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(llcc_slice_deactivate); + +/** + * llcc_get_slice_id - return the slice id + * @desc: Pointer to llcc slice descriptor + */ +int llcc_get_slice_id(struct llcc_slice_desc *desc) +{ + if (IS_ERR_OR_NULL(desc)) + return -EINVAL; + + return desc->slice_id; +} +EXPORT_SYMBOL_GPL(llcc_get_slice_id); + +/** + * llcc_get_slice_size - return the slice id + * @desc: Pointer to llcc slice descriptor + */ +size_t llcc_get_slice_size(struct llcc_slice_desc *desc) +{ + if (IS_ERR_OR_NULL(desc)) + return 0; + + return desc->slice_size; +} +EXPORT_SYMBOL_GPL(llcc_get_slice_size); + +static int qcom_llcc_cfg_program(struct platform_device *pdev) +{ + int i; + u32 attr1_cfg; + u32 attr0_cfg; + u32 attr1_val; + u32 attr0_val; + u32 max_cap_cacheline; + u32 sz; + int ret = 0; + const struct llcc_slice_config *llcc_table; + struct llcc_slice_desc desc; + + sz = drv_data->cfg_size; + llcc_table = drv_data->cfg; + + for (i = 0; i < sz; i++) { + attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id); + attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id); + + attr1_val = llcc_table[i].cache_mode; + attr1_val |= llcc_table[i].probe_target_ways << + ATTR1_PROBE_TARGET_WAYS_SHIFT; + attr1_val |= llcc_table[i].fixed_size << + ATTR1_FIXED_SIZE_SHIFT; + attr1_val |= llcc_table[i].priority << + ATTR1_PRIORITY_SHIFT; + + max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap); + + /* LLCC instances can vary for each target. + * The SW writes to broadcast register which gets propagated + * to each llcc instace (llcc0,.. llccN). + * Since the size of the memory is divided equally amongst the + * llcc instances, we need to configure the max cap accordingly. + */ + max_cap_cacheline = max_cap_cacheline / drv_data->num_banks; + max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT; + attr1_val |= max_cap_cacheline << ATTR1_MAX_CAP_SHIFT; + + attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK; + attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT; + + ret = regmap_write(drv_data->bcast_regmap, attr1_cfg, + attr1_val); + if (ret) + return ret; + ret = regmap_write(drv_data->bcast_regmap, attr0_cfg, + attr0_val); + if (ret) + return ret; + if (llcc_table[i].activate_on_init) { + desc.slice_id = llcc_table[i].slice_id; + ret = llcc_slice_activate(&desc); + } + } + return ret; +} + +static int qcom_llcc_remove(struct platform_device *pdev) +{ + /* Set the global pointer to a error code to avoid referencing it */ + drv_data = ERR_PTR(-ENODEV); + return 0; +} + +static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, + const char *name) +{ + struct resource *res; + void __iomem *base; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) + return ERR_PTR(-ENODEV); + + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return ERR_CAST(base); + + return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); +} + +static int qcom_llcc_probe(struct platform_device *pdev, + const struct llcc_slice_config *llcc_cfg, u32 sz) +{ + u32 num_banks; + struct device *dev = &pdev->dev; + int ret, i; + struct platform_device *llcc_edac; + + drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) { + ret = -ENOMEM; + goto err; + } + + drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base"); + if (IS_ERR(drv_data->regmap)) { + ret = PTR_ERR(drv_data->regmap); + goto err; + } + + drv_data->bcast_regmap = + qcom_llcc_init_mmio(pdev, "llcc_broadcast_base"); + if (IS_ERR(drv_data->bcast_regmap)) { + ret = PTR_ERR(drv_data->bcast_regmap); + goto err; + } + + ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0, + &num_banks); + if (ret) + goto err; + + num_banks &= LLCC_LB_CNT_MASK; + num_banks >>= LLCC_LB_CNT_SHIFT; + drv_data->num_banks = num_banks; + + for (i = 0; i < sz; i++) + if (llcc_cfg[i].slice_id > drv_data->max_slices) + drv_data->max_slices = llcc_cfg[i].slice_id; + + drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), + GFP_KERNEL); + if (!drv_data->offsets) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < num_banks; i++) + drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; + + drv_data->bitmap = devm_kcalloc(dev, + BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long), + GFP_KERNEL); + if (!drv_data->bitmap) { + ret = -ENOMEM; + goto err; + } + + drv_data->cfg = llcc_cfg; + drv_data->cfg_size = sz; + mutex_init(&drv_data->lock); + platform_set_drvdata(pdev, drv_data); + + ret = qcom_llcc_cfg_program(pdev); + if (ret) + goto err; + + drv_data->ecc_irq = platform_get_irq(pdev, 0); + if (drv_data->ecc_irq >= 0) { + llcc_edac = platform_device_register_data(&pdev->dev, + "qcom_llcc_edac", -1, drv_data, + sizeof(*drv_data)); + if (IS_ERR(llcc_edac)) + dev_err(dev, "Failed to register llcc edac driver\n"); + } + + return 0; +err: + drv_data = ERR_PTR(-ENODEV); + return ret; +} + +static int sdm845_qcom_llcc_remove(struct platform_device *pdev) +{ + return qcom_llcc_remove(pdev); +} + +static int sdm845_qcom_llcc_probe(struct platform_device *pdev) +{ + return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); +} + +static const struct of_device_id sdm845_qcom_llcc_of_match[] = { + { .compatible = "qcom,sdm845-llcc", }, + { } +}; + +static struct platform_driver sdm845_qcom_llcc_driver = { + .driver = { + .name = "sdm845-llcc", + .of_match_table = sdm845_qcom_llcc_of_match, + }, + .probe = sdm845_qcom_llcc_probe, + .remove = sdm845_qcom_llcc_remove, +}; +module_platform_driver(sdm845_qcom_llcc_driver); + +MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c deleted file mode 100644 index 574bb5bf20bc..000000000000 --- a/drivers/soc/qcom/llcc-slice.c +++ /dev/null @@ -1,458 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. - * - */ - -#include <linux/bitmap.h> -#include <linux/bitops.h> -#include <linux/device.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/regmap.h> -#include <linux/sizes.h> -#include <linux/slab.h> -#include <linux/soc/qcom/llcc-qcom.h> - -#define ACTIVATE BIT(0) -#define DEACTIVATE BIT(1) -#define ACT_CTRL_OPCODE_ACTIVATE BIT(0) -#define ACT_CTRL_OPCODE_DEACTIVATE BIT(1) -#define ACT_CTRL_ACT_TRIG BIT(0) -#define ACT_CTRL_OPCODE_SHIFT 0x01 -#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x02 -#define ATTR1_FIXED_SIZE_SHIFT 0x03 -#define ATTR1_PRIORITY_SHIFT 0x04 -#define ATTR1_MAX_CAP_SHIFT 0x10 -#define ATTR0_RES_WAYS_MASK GENMASK(11, 0) -#define ATTR0_BONUS_WAYS_MASK GENMASK(27, 16) -#define ATTR0_BONUS_WAYS_SHIFT 0x10 -#define LLCC_STATUS_READ_DELAY 100 - -#define CACHE_LINE_SIZE_SHIFT 6 - -#define LLCC_COMMON_STATUS0 0x0003000c -#define LLCC_LB_CNT_MASK GENMASK(31, 28) -#define LLCC_LB_CNT_SHIFT 28 - -#define MAX_CAP_TO_BYTES(n) (n * SZ_1K) -#define LLCC_TRP_ACT_CTRLn(n) (n * SZ_4K) -#define LLCC_TRP_STATUSn(n) (4 + n * SZ_4K) -#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + SZ_8 * n) -#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + SZ_8 * n) - -#define BANK_OFFSET_STRIDE 0x80000 - -static struct llcc_slice_config sdm845_data[] = { - { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, - { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, - { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, - { LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 }, - { LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 }, - { LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 }, - { LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 }, - { LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, - { LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 }, - { LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 }, - { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, -}; - -static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; - -static const struct regmap_config llcc_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .fast_io = true, -}; - -/** - * llcc_slice_getd - get llcc slice descriptor - * @uid: usecase_id for the client - * - * A pointer to llcc slice descriptor will be returned on success and - * and error pointer is returned on failure - */ -struct llcc_slice_desc *llcc_slice_getd(u32 uid) -{ - const struct llcc_slice_config *cfg; - struct llcc_slice_desc *desc; - u32 sz, count; - - if (IS_ERR(drv_data)) - return ERR_CAST(drv_data); - - cfg = drv_data->cfg; - sz = drv_data->cfg_size; - - for (count = 0; cfg && count < sz; count++, cfg++) - if (cfg->usecase_id == uid) - break; - - if (count == sz || !cfg) - return ERR_PTR(-ENODEV); - - desc = kzalloc(sizeof(*desc), GFP_KERNEL); - if (!desc) - return ERR_PTR(-ENOMEM); - - desc->slice_id = cfg->slice_id; - desc->slice_size = cfg->max_cap; - - return desc; -} -EXPORT_SYMBOL_GPL(llcc_slice_getd); - -/** - * llcc_slice_putd - llcc slice descritpor - * @desc: Pointer to llcc slice descriptor - */ -void llcc_slice_putd(struct llcc_slice_desc *desc) -{ - if (!IS_ERR_OR_NULL(desc)) - kfree(desc); -} -EXPORT_SYMBOL_GPL(llcc_slice_putd); - -static int llcc_update_act_ctrl(u32 sid, - u32 act_ctrl_reg_val, u32 status) -{ - u32 act_ctrl_reg; - u32 status_reg; - u32 slice_status; - int ret; - - if (IS_ERR(drv_data)) - return PTR_ERR(drv_data); - - act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid); - status_reg = LLCC_TRP_STATUSn(sid); - - /* Set the ACTIVE trigger */ - act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG; - ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg, - act_ctrl_reg_val); - if (ret) - return ret; - - /* Clear the ACTIVE trigger */ - act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG; - ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg, - act_ctrl_reg_val); - if (ret) - return ret; - - ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg, - slice_status, !(slice_status & status), - 0, LLCC_STATUS_READ_DELAY); - return ret; -} - -/** - * llcc_slice_activate - Activate the llcc slice - * @desc: Pointer to llcc slice descriptor - * - * A value of zero will be returned on success and a negative errno will - * be returned in error cases - */ -int llcc_slice_activate(struct llcc_slice_desc *desc) -{ - int ret; - u32 act_ctrl_val; - - if (IS_ERR(drv_data)) - return PTR_ERR(drv_data); - - if (IS_ERR_OR_NULL(desc)) - return -EINVAL; - - mutex_lock(&drv_data->lock); - if (test_bit(desc->slice_id, drv_data->bitmap)) { - mutex_unlock(&drv_data->lock); - return 0; - } - - act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT; - - ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, - DEACTIVATE); - if (ret) { - mutex_unlock(&drv_data->lock); - return ret; - } - - __set_bit(desc->slice_id, drv_data->bitmap); - mutex_unlock(&drv_data->lock); - - return ret; -} -EXPORT_SYMBOL_GPL(llcc_slice_activate); - -/** - * llcc_slice_deactivate - Deactivate the llcc slice - * @desc: Pointer to llcc slice descriptor - * - * A value of zero will be returned on success and a negative errno will - * be returned in error cases - */ -int llcc_slice_deactivate(struct llcc_slice_desc *desc) -{ - u32 act_ctrl_val; - int ret; - - if (IS_ERR(drv_data)) - return PTR_ERR(drv_data); - - if (IS_ERR_OR_NULL(desc)) - return -EINVAL; - - mutex_lock(&drv_data->lock); - if (!test_bit(desc->slice_id, drv_data->bitmap)) { - mutex_unlock(&drv_data->lock); - return 0; - } - act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT; - - ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, - ACTIVATE); - if (ret) { - mutex_unlock(&drv_data->lock); - return ret; - } - - __clear_bit(desc->slice_id, drv_data->bitmap); - mutex_unlock(&drv_data->lock); - - return ret; -} -EXPORT_SYMBOL_GPL(llcc_slice_deactivate); - -/** - * llcc_get_slice_id - return the slice id - * @desc: Pointer to llcc slice descriptor - */ -int llcc_get_slice_id(struct llcc_slice_desc *desc) -{ - if (IS_ERR_OR_NULL(desc)) - return -EINVAL; - - return desc->slice_id; -} -EXPORT_SYMBOL_GPL(llcc_get_slice_id); - -/** - * llcc_get_slice_size - return the slice id - * @desc: Pointer to llcc slice descriptor - */ -size_t llcc_get_slice_size(struct llcc_slice_desc *desc) -{ - if (IS_ERR_OR_NULL(desc)) - return 0; - - return desc->slice_size; -} -EXPORT_SYMBOL_GPL(llcc_get_slice_size); - -static int qcom_llcc_cfg_program(struct platform_device *pdev) -{ - int i; - u32 attr1_cfg; - u32 attr0_cfg; - u32 attr1_val; - u32 attr0_val; - u32 max_cap_cacheline; - u32 sz; - int ret = 0; - const struct llcc_slice_config *llcc_table; - struct llcc_slice_desc desc; - - sz = drv_data->cfg_size; - llcc_table = drv_data->cfg; - - for (i = 0; i < sz; i++) { - attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id); - attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id); - - attr1_val = llcc_table[i].cache_mode; - attr1_val |= llcc_table[i].probe_target_ways << - ATTR1_PROBE_TARGET_WAYS_SHIFT; - attr1_val |= llcc_table[i].fixed_size << - ATTR1_FIXED_SIZE_SHIFT; - attr1_val |= llcc_table[i].priority << - ATTR1_PRIORITY_SHIFT; - - max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap); - - /* LLCC instances can vary for each target. - * The SW writes to broadcast register which gets propagated - * to each llcc instace (llcc0,.. llccN). - * Since the size of the memory is divided equally amongst the - * llcc instances, we need to configure the max cap accordingly. - */ - max_cap_cacheline = max_cap_cacheline / drv_data->num_banks; - max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT; - attr1_val |= max_cap_cacheline << ATTR1_MAX_CAP_SHIFT; - - attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK; - attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT; - - ret = regmap_write(drv_data->bcast_regmap, attr1_cfg, - attr1_val); - if (ret) - return ret; - ret = regmap_write(drv_data->bcast_regmap, attr0_cfg, - attr0_val); - if (ret) - return ret; - if (llcc_table[i].activate_on_init) { - desc.slice_id = llcc_table[i].slice_id; - ret = llcc_slice_activate(&desc); - } - } - return ret; -} - -static int qcom_llcc_remove(struct platform_device *pdev) -{ - /* Set the global pointer to a error code to avoid referencing it */ - drv_data = ERR_PTR(-ENODEV); - return 0; -} - -static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, - const char *name) -{ - struct resource *res; - void __iomem *base; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); - if (!res) - return ERR_PTR(-ENODEV); - - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return ERR_CAST(base); - - return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); -} - -static int qcom_llcc_probe(struct platform_device *pdev, - const struct llcc_slice_config *llcc_cfg, u32 sz) -{ - u32 num_banks; - struct device *dev = &pdev->dev; - int ret, i; - struct platform_device *llcc_edac; - - drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); - if (!drv_data) { - ret = -ENOMEM; - goto err; - } - - drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base"); - if (IS_ERR(drv_data->regmap)) { - ret = PTR_ERR(drv_data->regmap); - goto err; - } - - drv_data->bcast_regmap = - qcom_llcc_init_mmio(pdev, "llcc_broadcast_base"); - if (IS_ERR(drv_data->bcast_regmap)) { - ret = PTR_ERR(drv_data->bcast_regmap); - goto err; - } - - ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0, - &num_banks); - if (ret) - goto err; - - num_banks &= LLCC_LB_CNT_MASK; - num_banks >>= LLCC_LB_CNT_SHIFT; - drv_data->num_banks = num_banks; - - for (i = 0; i < sz; i++) - if (llcc_cfg[i].slice_id > drv_data->max_slices) - drv_data->max_slices = llcc_cfg[i].slice_id; - - drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), - GFP_KERNEL); - if (!drv_data->offsets) { - ret = -ENOMEM; - goto err; - } - - for (i = 0; i < num_banks; i++) - drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; - - drv_data->bitmap = devm_kcalloc(dev, - BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long), - GFP_KERNEL); - if (!drv_data->bitmap) { - ret = -ENOMEM; - goto err; - } - - drv_data->cfg = llcc_cfg; - drv_data->cfg_size = sz; - mutex_init(&drv_data->lock); - platform_set_drvdata(pdev, drv_data); - - ret = qcom_llcc_cfg_program(pdev); - if (ret) - goto err; - - drv_data->ecc_irq = platform_get_irq(pdev, 0); - if (drv_data->ecc_irq >= 0) { - llcc_edac = platform_device_register_data(&pdev->dev, - "qcom_llcc_edac", -1, drv_data, - sizeof(*drv_data)); - if (IS_ERR(llcc_edac)) - dev_err(dev, "Failed to register llcc edac driver\n"); - } - - return 0; -err: - drv_data = ERR_PTR(-ENODEV); - return ret; -} - -static int sdm845_qcom_llcc_remove(struct platform_device *pdev) -{ - return qcom_llcc_remove(pdev); -} - -static int sdm845_qcom_llcc_probe(struct platform_device *pdev) -{ - return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); -} - -static const struct of_device_id sdm845_qcom_llcc_of_match[] = { - { .compatible = "qcom,sdm845-llcc", }, - { } -}; - -static struct platform_driver sdm845_qcom_llcc_driver = { - .driver = { - .name = "sdm845-llcc", - .of_match_table = sdm845_qcom_llcc_of_match, - }, - .probe = sdm845_qcom_llcc_probe, - .remove = sdm845_qcom_llcc_remove, -}; -module_platform_driver(sdm845_qcom_llcc_driver); - -MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 99356b03b431f9589bbaec2bc5bacceccb3dd99a Mon Sep 17 00:00:00 2001 From: Vivek Gautam <vivek.gautam@codeaurora.org> Date: Thu, 18 Jul 2019 18:32:38 +0530 Subject: soc: qcom: Make llcc-qcom a generic driver This makes way for adding future llcc versions. Also pull out the llcc-qcom specific definitions from includes. Includes path now contains the only definitions that are to be exposed to other subsystems. Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/llcc-qcom.c | 137 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 116 insertions(+), 21 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 574bb5bf20bc..98563ef0ac6b 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -47,6 +47,100 @@ #define BANK_OFFSET_STRIDE 0x80000 +/** + * llcc_slice_config - Data associated with the llcc slice + * @usecase_id: Unique id for the client's use case + * @slice_id: llcc slice id for each client + * @max_cap: The maximum capacity of the cache slice provided in KB + * @priority: Priority of the client used to select victim line for replacement + * @fixed_size: Boolean indicating if the slice has a fixed capacity + * @bonus_ways: Bonus ways are additional ways to be used for any slice, + * if client ends up using more than reserved cache ways. Bonus + * ways are allocated only if they are not reserved for some + * other client. + * @res_ways: Reserved ways for the cache slice, the reserved ways cannot + * be used by any other client than the one its assigned to. + * @cache_mode: Each slice operates as a cache, this controls the mode of the + * slice: normal or TCM(Tightly Coupled Memory) + * @probe_target_ways: Determines what ways to probe for access hit. When + * configured to 1 only bonus and reserved ways are probed. + * When configured to 0 all ways in llcc are probed. + * @dis_cap_alloc: Disable capacity based allocation for a client + * @retain_on_pc: If this bit is set and client has maintained active vote + * then the ways assigned to this client are not flushed on power + * collapse. + * @activate_on_init: Activate the slice immediately after it is programmed + */ +struct llcc_slice_config { + u32 usecase_id; + u32 slice_id; + u32 max_cap; + u32 priority; + bool fixed_size; + u32 bonus_ways; + u32 res_ways; + u32 cache_mode; + u32 probe_target_ways; + bool dis_cap_alloc; + bool retain_on_pc; + bool activate_on_init; +}; + +/** + * llcc_drv_data - Data associated with the llcc driver + * @regmap: regmap associated with the llcc device + * @bcast_regmap: regmap associated with llcc broadcast offset + * @cfg: pointer to the data structure for slice configuration + * @lock: mutex associated with each slice + * @cfg_size: size of the config data table + * @max_slices: max slices as read from device tree + * @num_banks: Number of llcc banks + * @bitmap: Bit map to track the active slice ids + * @offsets: Pointer to the bank offsets array + * @ecc_irq: interrupt for llcc cache error detection and reporting + */ +struct llcc_drv_data { + struct regmap *regmap; + struct regmap *bcast_regmap; + const struct llcc_slice_config *cfg; + struct mutex lock; + u32 cfg_size; + u32 max_slices; + u32 num_banks; + unsigned long *bitmap; + u32 *offsets; + int ecc_irq; +}; + +/** + * llcc_edac_reg_data - llcc edac registers data for each error type + * @name: Name of the error + * @synd_reg: Syndrome register address + * @count_status_reg: Status register address to read the error count + * @ways_status_reg: Status register address to read the error ways + * @reg_cnt: Number of registers + * @count_mask: Mask value to get the error count + * @ways_mask: Mask value to get the error ways + * @count_shift: Shift value to get the error count + * @ways_shift: Shift value to get the error ways + */ +struct llcc_edac_reg_data { + char *name; + u64 synd_reg; + u64 count_status_reg; + u64 ways_status_reg; + u32 reg_cnt; + u32 count_mask; + u32 ways_mask; + u8 count_shift; + u8 ways_shift; +}; + +struct qcom_llcc_config { + const struct llcc_slice_config *sct_data; + int size; +}; + static struct llcc_slice_config sdm845_data[] = { { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, @@ -68,6 +162,11 @@ static struct llcc_slice_config sdm845_data[] = { { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, }; +static const struct qcom_llcc_config sdm845_cfg = { + .sct_data = sdm845_data, + .size = ARRAY_SIZE(sdm845_data), +}; + static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; static const struct regmap_config llcc_regmap_config = { @@ -347,13 +446,15 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); } -static int qcom_llcc_probe(struct platform_device *pdev, - const struct llcc_slice_config *llcc_cfg, u32 sz) +static int qcom_llcc_probe(struct platform_device *pdev) { u32 num_banks; struct device *dev = &pdev->dev; int ret, i; struct platform_device *llcc_edac; + const struct qcom_llcc_config *cfg; + const struct llcc_slice_config *llcc_cfg; + u32 sz; drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) { @@ -383,6 +484,10 @@ static int qcom_llcc_probe(struct platform_device *pdev, num_banks >>= LLCC_LB_CNT_SHIFT; drv_data->num_banks = num_banks; + cfg = of_device_get_match_data(&pdev->dev); + llcc_cfg = cfg->sct_data; + sz = cfg->size; + for (i = 0; i < sz; i++) if (llcc_cfg[i].slice_id > drv_data->max_slices) drv_data->max_slices = llcc_cfg[i].slice_id; @@ -429,30 +534,20 @@ err: return ret; } -static int sdm845_qcom_llcc_remove(struct platform_device *pdev) -{ - return qcom_llcc_remove(pdev); -} - -static int sdm845_qcom_llcc_probe(struct platform_device *pdev) -{ - return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); -} - -static const struct of_device_id sdm845_qcom_llcc_of_match[] = { - { .compatible = "qcom,sdm845-llcc", }, +static const struct of_device_id qcom_llcc_of_match[] = { + { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, { } }; -static struct platform_driver sdm845_qcom_llcc_driver = { +static struct platform_driver qcom_llcc_driver = { .driver = { - .name = "sdm845-llcc", - .of_match_table = sdm845_qcom_llcc_of_match, + .name = "qcom-llcc", + .of_match_table = qcom_llcc_of_match, }, - .probe = sdm845_qcom_llcc_probe, - .remove = sdm845_qcom_llcc_remove, + .probe = qcom_llcc_probe, + .remove = qcom_llcc_remove, }; -module_platform_driver(sdm845_qcom_llcc_driver); +module_platform_driver(qcom_llcc_driver); -MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); +MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 66e6a633910a8e046ffefa7753210c237868419f Mon Sep 17 00:00:00 2001 From: Georgi Djakov <georgi.djakov@linaro.org> Date: Tue, 23 Jul 2019 17:23:36 +0300 Subject: soc: qcom: smd-rpm: Create RPM interconnect proxy child device Register a platform device to handle the communication of bus bandwidth requests with the remote processor. The interconnect proxy device is part of this remote processor (RPM) hardware. Let's create a icc-smd-rpm proxy child device to represent the bus throughput functionality that is provided by the RPM. Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Georgi Djakov <georgi.djakov@linaro.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/smd-rpm.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index fa9dd12b5e39..34cdd638a6c1 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -19,12 +19,14 @@ /** * struct qcom_smd_rpm - state of the rpm device driver * @rpm_channel: reference to the smd channel + * @icc: interconnect proxy device * @ack: completion for acks * @lock: mutual exclusion around the send/complete pair * @ack_status: result of the rpm request */ struct qcom_smd_rpm { struct rpmsg_endpoint *rpm_channel; + struct platform_device *icc; struct device *dev; struct completion ack; @@ -193,6 +195,7 @@ static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) { struct qcom_smd_rpm *rpm; + int ret; rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); if (!rpm) @@ -205,11 +208,23 @@ static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) rpm->rpm_channel = rpdev->ept; dev_set_drvdata(&rpdev->dev, rpm); - return of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); + rpm->icc = platform_device_register_data(&rpdev->dev, "icc_smd_rpm", -1, + NULL, 0); + if (IS_ERR(rpm->icc)) + return PTR_ERR(rpm->icc); + + ret = of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); + if (ret) + platform_device_unregister(rpm->icc); + + return ret; } static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) { + struct qcom_smd_rpm *rpm = dev_get_drvdata(&rpdev->dev); + + platform_device_unregister(rpm->icc); of_platform_depopulate(&rpdev->dev); } -- cgit v1.2.3 From 69d2d2531119338b8507d83eb4d7bf4c90f34636 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Date: Thu, 12 Sep 2019 10:10:56 +0100 Subject: soc: qcom: socinfo: add sdm845 and sda845 soc ids This patch adds missing soc ids for sdm845 and sda845 Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/socinfo.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index a39ea5061dc5..7864b75ce569 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -198,6 +198,8 @@ static const struct soc_id soc_id[] = { { 310, "MSM8996AU" }, { 311, "APQ8096AU" }, { 312, "APQ8096SG" }, + { 321, "SDM845" }, + { 341, "SDA845" }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) -- cgit v1.2.3 From 83a81c1b8690310b98f0804aef134c4318234866 Mon Sep 17 00:00:00 2001 From: "Angelo G. Del Regno" <kholk11@gmail.com> Date: Sat, 21 Sep 2019 12:12:05 +0200 Subject: soc: qcom: smd-rpm: Add MSM8976 compatible Add a compatible for the RPM on the Qualcomm MSM8976 platform: this is also valid for MSM8956 and their APQ variants. Signed-off-by: Angelo G. Del Regno <kholk11@gmail.com> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/smd-rpm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index 34cdd638a6c1..005dd30c58fa 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -232,6 +232,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8084" }, { .compatible = "qcom,rpm-msm8916" }, { .compatible = "qcom,rpm-msm8974" }, + { .compatible = "qcom,rpm-msm8976" }, { .compatible = "qcom,rpm-msm8996" }, { .compatible = "qcom,rpm-msm8998" }, { .compatible = "qcom,rpm-sdm660" }, -- cgit v1.2.3 From e386b228cad2e3dbba532a891dc084af8e7e702c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki <s.nawrocki@samsung.com> Date: Wed, 4 Sep 2019 10:49:51 +0200 Subject: soc: samsung: chipid: Make exynos_chipid_early_init() static Add missing static qualifier to the chipid initcall function. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> --- drivers/soc/samsung/exynos-chipid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index c55a47cfe617..25562dd0b206 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -45,7 +45,7 @@ static const char * __init product_id_to_soc_id(unsigned int product_id) return NULL; } -int __init exynos_chipid_early_init(void) +static int __init exynos_chipid_early_init(void) { struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; -- cgit v1.2.3 From b1d522443b4b000974e48f27d4ee77dbfc67962d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno <kholk11@gmail.com> Date: Sat, 5 Oct 2019 13:07:58 +0200 Subject: soc: qcom: rpmpd: Add rpm power domains for msm8976 The MSM8956/76 SoCs have two main voltage-level power domains, VDD_CX and VDD_MX, which also have their own voltage-floor-level (VFL) corner. Signed-off-by: AngeloGioacchino Del Regno <kholk11@gmail.com> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/rpmpd.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 3c1a55cf25d6..2b1834c5609a 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -115,6 +115,28 @@ struct rpmpd_desc { static DEFINE_MUTEX(rpmpd_lock); +/* msm8976 RPM Power Domains */ +DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2); +DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6); + +DEFINE_RPMPD_VFL(msm8976, vddcx_vfl, RWSC, 2); +DEFINE_RPMPD_VFL(msm8976, vddmx_vfl, RWSM, 6); + +static struct rpmpd *msm8976_rpmpds[] = { + [MSM8976_VDDCX] = &msm8976_vddcx, + [MSM8976_VDDCX_AO] = &msm8976_vddcx_ao, + [MSM8976_VDDCX_VFL] = &msm8976_vddcx_vfl, + [MSM8976_VDDMX] = &msm8976_vddmx, + [MSM8976_VDDMX_AO] = &msm8976_vddmx_ao, + [MSM8976_VDDMX_VFL] = &msm8976_vddmx_vfl, +}; + +static const struct rpmpd_desc msm8976_desc = { + .rpmpds = msm8976_rpmpds, + .num_pds = ARRAY_SIZE(msm8976_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_HIGH, +}; + /* msm8996 RPM Power domains */ DEFINE_RPMPD_PAIR(msm8996, vddcx, vddcx_ao, SMPA, CORNER, 1); DEFINE_RPMPD_PAIR(msm8996, vddmx, vddmx_ao, SMPA, CORNER, 2); @@ -198,6 +220,7 @@ static const struct rpmpd_desc qcs404_desc = { }; static const struct of_device_id rpmpd_match_table[] = { + { .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, -- cgit v1.2.3 From 240051cb833b5d9006ff0852ac54b2d7a657404b Mon Sep 17 00:00:00 2001 From: Jianxin Pan <jianxin.pan@amlogic.com> Date: Thu, 12 Sep 2019 04:19:27 -0400 Subject: soc: amlogic: meson-gx-socinfo: Add A1 and A113L IDs Add the SoC IDs for the A113L Amlogic A1 SoC. Signed-off-by: Jianxin Pan <jianxin.pan@amlogic.com> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com> Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Reviewed-by: Kevin Hilman <khilman@baylibre.com> Signed-off-by: Kevin Hilman <khilman@baylibre.com> --- drivers/soc/amlogic/meson-gx-socinfo.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 6d0d04f163cb..3c86d8dccdcc 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -40,6 +40,7 @@ static const struct meson_gx_soc_id { { "G12A", 0x28 }, { "G12B", 0x29 }, { "SM1", 0x2b }, + { "A1", 0x2c }, }; static const struct meson_gx_package_id { @@ -68,6 +69,7 @@ static const struct meson_gx_package_id { { "S922X", 0x29, 0x40, 0xf0 }, { "A311D", 0x29, 0x10, 0xf0 }, { "S905X3", 0x2b, 0x5, 0xf }, + { "A113L", 0x2c, 0x0, 0xf8 }, }; static inline unsigned int socinfo_to_major(u32 socinfo) -- cgit v1.2.3 From 1d7c541b8a5b80a3252ca96a0ed22e894bcafb5d Mon Sep 17 00:00:00 2001 From: Christian Hewitt <christianshewitt@gmail.com> Date: Mon, 7 Oct 2019 09:23:59 +0400 Subject: soc: amlogic: meson-gx-socinfo: Add S905X3 ID for VIM3L VIM3L appears to use a different ID: [ 0.086470] soc soc0: Amlogic Meson SM1 (S905X3) Revision 2b:c (b0:2) Detected Signed-off-by: Christian Hewitt <christianshewitt@gmail.com> Signed-off-by: Kevin Hilman <khilman@baylibre.com> --- drivers/soc/amlogic/meson-gx-socinfo.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 3c86d8dccdcc..87ed558fa1c2 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -69,6 +69,7 @@ static const struct meson_gx_package_id { { "S922X", 0x29, 0x40, 0xf0 }, { "A311D", 0x29, 0x10, 0xf0 }, { "S905X3", 0x2b, 0x5, 0xf }, + { "S905X3", 0x2b, 0xb0, 0xf2 }, { "A113L", 0x2c, 0x0, 0xf8 }, }; -- cgit v1.2.3 From 89da2ba947b1080199f4a6413686569a75fc2e7d Mon Sep 17 00:00:00 2001 From: YueHaibing <yuehaibing@huawei.com> Date: Tue, 8 Oct 2019 15:16:14 +0800 Subject: soc: qcom: Fix llcc-qcom definitions to include commit 99356b03b431 ("soc: qcom: Make llcc-qcom a generic driver") move these out of llcc-qcom.h, make the building fails: drivers/edac/qcom_edac.c:86:40: error: array type has incomplete element type struct llcc_edac_reg_data static const struct llcc_edac_reg_data edac_reg_data[] = { ^~~~~~~~~~~~~ drivers/edac/qcom_edac.c:87:3: error: array index in non-array initializer [LLCC_DRAM_CE] = { ^~~~~~~~~~~~ drivers/edac/qcom_edac.c:87:3: note: (near initialization for edac_reg_data) drivers/edac/qcom_edac.c:88:3: error: field name not in record or union initializer .name = "DRAM Single-bit", ... drivers/edac/qcom_edac.c:169:51: warning: struct llcc_drv_data declared inside parameter list will not be visible outside of this definition or declaration qcom_llcc_clear_error_status(int err_type, struct llcc_drv_data *drv) ^~~~~~~~~~~~~ This patch move the needed definitions back to include. Reported-by: Hulk Robot <hulkci@huawei.com> Fixes: 99356b03b431 ("soc: qcom: Make llcc-qcom a generic driver") Signed-off-by: YueHaibing <yuehaibing@huawei.com> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/llcc-qcom.c | 50 -------------------------------------------- 1 file changed, 50 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 98563ef0ac6b..436067367db4 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -86,56 +86,6 @@ struct llcc_slice_config { bool activate_on_init; }; -/** - * llcc_drv_data - Data associated with the llcc driver - * @regmap: regmap associated with the llcc device - * @bcast_regmap: regmap associated with llcc broadcast offset - * @cfg: pointer to the data structure for slice configuration - * @lock: mutex associated with each slice - * @cfg_size: size of the config data table - * @max_slices: max slices as read from device tree - * @num_banks: Number of llcc banks - * @bitmap: Bit map to track the active slice ids - * @offsets: Pointer to the bank offsets array - * @ecc_irq: interrupt for llcc cache error detection and reporting - */ -struct llcc_drv_data { - struct regmap *regmap; - struct regmap *bcast_regmap; - const struct llcc_slice_config *cfg; - struct mutex lock; - u32 cfg_size; - u32 max_slices; - u32 num_banks; - unsigned long *bitmap; - u32 *offsets; - int ecc_irq; -}; - -/** - * llcc_edac_reg_data - llcc edac registers data for each error type - * @name: Name of the error - * @synd_reg: Syndrome register address - * @count_status_reg: Status register address to read the error count - * @ways_status_reg: Status register address to read the error ways - * @reg_cnt: Number of registers - * @count_mask: Mask value to get the error count - * @ways_mask: Mask value to get the error ways - * @count_shift: Shift value to get the error count - * @ways_shift: Shift value to get the error ways - */ -struct llcc_edac_reg_data { - char *name; - u64 synd_reg; - u64 count_status_reg; - u64 ways_status_reg; - u32 reg_cnt; - u32 count_mask; - u32 ways_mask; - u8 count_shift; - u8 ways_shift; -}; - struct qcom_llcc_config { const struct llcc_slice_config *sct_data; int size; -- cgit v1.2.3 From 3e99cb214f03b8206aad80644809aea947910372 Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:36 -0700 Subject: soc: ti: add initial PRM driver with reset control support Add initial PRM (Power and Reset Management) driver for TI OMAP class SoCs. Initially this driver only supports reset control, but can be extended to support rest of the functionality, like powerdomain control, PRCM irq support etc. Signed-off-by: Tero Kristo <t-kristo@ti.com> Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de> Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/Makefile | 1 + drivers/soc/ti/omap_prm.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 drivers/soc/ti/omap_prm.c (limited to 'drivers/soc') diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile index b3868d392d4f..788b5cd1e180 100644 --- a/drivers/soc/ti/Makefile +++ b/drivers/soc/ti/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS) += knav_qmss.o knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA) += knav_dma.o obj-$(CONFIG_AMX3_PM) += pm33xx.o +obj-$(CONFIG_ARCH_OMAP2PLUS) += omap_prm.o obj-$(CONFIG_WKUP_M3_IPC) += wkup_m3_ipc.o obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN) += ti_sci_inta_msi.o diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c new file mode 100644 index 000000000000..d171e96e4ef1 --- /dev/null +++ b/drivers/soc/ti/omap_prm.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP2+ PRM driver + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Tero Kristo <t-kristo@ti.com> + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/delay.h> + +struct omap_rst_map { + s8 rst; + s8 st; +}; + +struct omap_prm_data { + u32 base; + const char *name; + u16 rstctrl; + u16 rstst; + const struct omap_rst_map *rstmap; + u8 flags; +}; + +struct omap_prm { + const struct omap_prm_data *data; + void __iomem *base; +}; + +struct omap_reset_data { + struct reset_controller_dev rcdev; + struct omap_prm *prm; + u32 mask; + spinlock_t lock; +}; + +#define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev) + +#define OMAP_MAX_RESETS 8 +#define OMAP_RESET_MAX_WAIT 10000 + +#define OMAP_PRM_HAS_RSTCTRL BIT(0) +#define OMAP_PRM_HAS_RSTST BIT(1) + +#define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) + +static const struct of_device_id omap_prm_id_table[] = { + { }, +}; + +static bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id) +{ + if (reset->mask & BIT(id)) + return true; + + return false; +} + +static int omap_reset_get_st_bit(struct omap_reset_data *reset, + unsigned long id) +{ + const struct omap_rst_map *map = reset->prm->data->rstmap; + + while (map->rst >= 0) { + if (map->rst == id) + return map->st; + + map++; + } + + return id; +} + +static int omap_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + u32 v; + int st_bit = omap_reset_get_st_bit(reset, id); + bool has_rstst = reset->prm->data->rstst || + (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); + + /* Check if we have rstst */ + if (!has_rstst) + return -ENOTSUPP; + + /* Check if hw reset line is asserted */ + v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); + if (v & BIT(id)) + return 1; + + /* + * Check reset status, high value means reset sequence has been + * completed successfully so we can return 0 here (reset deasserted) + */ + v = readl_relaxed(reset->prm->base + reset->prm->data->rstst); + v >>= st_bit; + v &= 1; + + return !v; +} + +static int omap_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + u32 v; + unsigned long flags; + + /* assert the reset control line */ + spin_lock_irqsave(&reset->lock, flags); + v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); + v |= 1 << id; + writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); + spin_unlock_irqrestore(&reset->lock, flags); + + return 0; +} + +static int omap_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + u32 v; + int st_bit; + bool has_rstst; + unsigned long flags; + + has_rstst = reset->prm->data->rstst || + (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); + + if (has_rstst) { + st_bit = omap_reset_get_st_bit(reset, id); + + /* Clear the reset status by writing 1 to the status bit */ + v = 1 << st_bit; + writel_relaxed(v, reset->prm->base + reset->prm->data->rstst); + } + + /* de-assert the reset control line */ + spin_lock_irqsave(&reset->lock, flags); + v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); + v &= ~(1 << id); + writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); + spin_unlock_irqrestore(&reset->lock, flags); + + return 0; +} + +static const struct reset_control_ops omap_reset_ops = { + .assert = omap_reset_assert, + .deassert = omap_reset_deassert, + .status = omap_reset_status, +}; + +static int omap_prm_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + + if (!_is_valid_reset(reset, reset_spec->args[0])) + return -EINVAL; + + return reset_spec->args[0]; +} + +static int omap_prm_reset_init(struct platform_device *pdev, + struct omap_prm *prm) +{ + struct omap_reset_data *reset; + const struct omap_rst_map *map; + + /* + * Check if we have controllable resets. If either rstctrl is non-zero + * or OMAP_PRM_HAS_RSTCTRL flag is set, we have reset control register + * for the domain. + */ + if (!prm->data->rstctrl && !(prm->data->flags & OMAP_PRM_HAS_RSTCTRL)) + return 0; + + map = prm->data->rstmap; + if (!map) + return -EINVAL; + + reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); + if (!reset) + return -ENOMEM; + + reset->rcdev.owner = THIS_MODULE; + reset->rcdev.ops = &omap_reset_ops; + reset->rcdev.of_node = pdev->dev.of_node; + reset->rcdev.nr_resets = OMAP_MAX_RESETS; + reset->rcdev.of_xlate = omap_prm_reset_xlate; + reset->rcdev.of_reset_n_cells = 1; + spin_lock_init(&reset->lock); + + reset->prm = prm; + + while (map->rst >= 0) { + reset->mask |= BIT(map->rst); + map++; + } + + return devm_reset_controller_register(&pdev->dev, &reset->rcdev); +} + +static int omap_prm_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct omap_prm_data *data; + struct omap_prm *prm; + const struct of_device_id *match; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + match = of_match_device(omap_prm_id_table, &pdev->dev); + if (!match) + return -ENOTSUPP; + + prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL); + if (!prm) + return -ENOMEM; + + data = match->data; + + while (data->base != res->start) { + if (!data->base) + return -EINVAL; + data++; + } + + prm->data = data; + + prm->base = devm_ioremap_resource(&pdev->dev, res); + if (!prm->base) + return -ENOMEM; + + return omap_prm_reset_init(pdev, prm); +} + +static struct platform_driver omap_prm_driver = { + .probe = omap_prm_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = omap_prm_id_table, + }, +}; +builtin_platform_driver(omap_prm_driver); -- cgit v1.2.3 From c5117a78dd88e5b673897baec0a6eed9ee599e02 Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:37 -0700 Subject: soc: ti: omap-prm: poll for reset complete during de-assert Poll for reset completion status during de-assertion of reset, otherwise the IP in question might be accessed before it has left reset properly. Signed-off-by: Tero Kristo <t-kristo@ti.com> Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de> Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index d171e96e4ef1..a3ffb19e402f 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -152,6 +152,18 @@ static int omap_reset_deassert(struct reset_controller_dev *rcdev, writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); spin_unlock_irqrestore(&reset->lock, flags); + if (!has_rstst) + return 0; + + /* wait for the status to be set */ + ret = readl_relaxed_poll_timeout(reset->prm->base + + reset->prm->data->rstst, + v, v & BIT(st_bit), 1, + OMAP_RESET_MAX_WAIT); + if (ret) + pr_err("%s: timedout waiting for %s:%lu\n", __func__, + reset->prm->data->name, id); + return 0; } -- cgit v1.2.3 From d30cd83f68535ca21412b1abe8684438690c1c2b Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:38 -0700 Subject: soc: ti: omap-prm: add support for denying idle for reset clockdomain TI SoCs hardware reset signals require the parent clockdomain to be in force wakeup mode while de-asserting the reset, otherwise it may never complete. To support this, add pdata hooks to control the clockdomain directly. Signed-off-by: Tero Kristo <t-kristo@ti.com> Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index a3ffb19e402f..09985da8f6d0 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -16,6 +16,8 @@ #include <linux/reset-controller.h> #include <linux/delay.h> +#include <linux/platform_data/ti-prm.h> + struct omap_rst_map { s8 rst; s8 st; @@ -24,6 +26,7 @@ struct omap_rst_map { struct omap_prm_data { u32 base; const char *name; + const char *clkdm_name; u16 rstctrl; u16 rstst; const struct omap_rst_map *rstmap; @@ -40,6 +43,8 @@ struct omap_reset_data { struct omap_prm *prm; u32 mask; spinlock_t lock; + struct clockdomain *clkdm; + struct device *dev; }; #define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev) @@ -49,6 +54,7 @@ struct omap_reset_data { #define OMAP_PRM_HAS_RSTCTRL BIT(0) #define OMAP_PRM_HAS_RSTST BIT(1) +#define OMAP_PRM_HAS_NO_CLKDM BIT(2) #define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) @@ -133,6 +139,8 @@ static int omap_reset_deassert(struct reset_controller_dev *rcdev, int st_bit; bool has_rstst; unsigned long flags; + struct ti_prm_platform_data *pdata = dev_get_platdata(reset->dev); + int ret = 0; has_rstst = reset->prm->data->rstst || (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); @@ -145,6 +153,9 @@ static int omap_reset_deassert(struct reset_controller_dev *rcdev, writel_relaxed(v, reset->prm->base + reset->prm->data->rstst); } + if (reset->clkdm) + pdata->clkdm_deny_idle(reset->clkdm); + /* de-assert the reset control line */ spin_lock_irqsave(&reset->lock, flags); v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); @@ -153,7 +164,7 @@ static int omap_reset_deassert(struct reset_controller_dev *rcdev, spin_unlock_irqrestore(&reset->lock, flags); if (!has_rstst) - return 0; + goto exit; /* wait for the status to be set */ ret = readl_relaxed_poll_timeout(reset->prm->base + @@ -164,7 +175,11 @@ static int omap_reset_deassert(struct reset_controller_dev *rcdev, pr_err("%s: timedout waiting for %s:%lu\n", __func__, reset->prm->data->name, id); - return 0; +exit: + if (reset->clkdm) + pdata->clkdm_allow_idle(reset->clkdm); + + return ret; } static const struct reset_control_ops omap_reset_ops = { @@ -189,6 +204,8 @@ static int omap_prm_reset_init(struct platform_device *pdev, { struct omap_reset_data *reset; const struct omap_rst_map *map; + struct ti_prm_platform_data *pdata = dev_get_platdata(&pdev->dev); + char buf[32]; /* * Check if we have controllable resets. If either rstctrl is non-zero @@ -198,6 +215,11 @@ static int omap_prm_reset_init(struct platform_device *pdev, if (!prm->data->rstctrl && !(prm->data->flags & OMAP_PRM_HAS_RSTCTRL)) return 0; + /* Check if we have the pdata callbacks in place */ + if (!pdata || !pdata->clkdm_lookup || !pdata->clkdm_deny_idle || + !pdata->clkdm_allow_idle) + return -EINVAL; + map = prm->data->rstmap; if (!map) return -EINVAL; @@ -212,10 +234,20 @@ static int omap_prm_reset_init(struct platform_device *pdev, reset->rcdev.nr_resets = OMAP_MAX_RESETS; reset->rcdev.of_xlate = omap_prm_reset_xlate; reset->rcdev.of_reset_n_cells = 1; + reset->dev = &pdev->dev; spin_lock_init(&reset->lock); reset->prm = prm; + sprintf(buf, "%s_clkdm", prm->data->clkdm_name ? prm->data->clkdm_name : + prm->data->name); + + if (!(prm->data->flags & OMAP_PRM_HAS_NO_CLKDM)) { + reset->clkdm = pdata->clkdm_lookup(buf); + if (!reset->clkdm) + return -EINVAL; + } + while (map->rst >= 0) { reset->mask |= BIT(map->rst); map++; -- cgit v1.2.3 From 0f0faaf4d7ff373def6c5218172c45f55a5113f6 Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:38 -0700 Subject: soc: ti: omap-prm: add omap4 PRM data Add PRM data for omap4 family of SoCs. Initially this is just used to provide reset support. Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Tero Kristo <t-kristo@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index 09985da8f6d0..f845b8a415cc 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -58,7 +58,29 @@ struct omap_reset_data { #define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) +static const struct omap_rst_map rst_map_01[] = { + { .rst = 0, .st = 0 }, + { .rst = 1, .st = 1 }, + { .rst = -1 }, +}; + +static const struct omap_rst_map rst_map_012[] = { + { .rst = 0, .st = 0 }, + { .rst = 1, .st = 1 }, + { .rst = 2, .st = 2 }, + { .rst = -1 }, +}; + +static const struct omap_prm_data omap4_prm_data[] = { + { .name = "tesla", .base = 0x4a306400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { .name = "core", .base = 0x4a306700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ducati", .rstmap = rst_map_012 }, + { .name = "ivahd", .base = 0x4a306f00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, + { .name = "device", .base = 0x4a307b00, .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, + { }, +}; + static const struct of_device_id omap_prm_id_table[] = { + { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, { }, }; -- cgit v1.2.3 From 8aa35504a0b9f71cec84434369273a3678982b4b Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:39 -0700 Subject: soc: ti: omap-prm: add data for am33xx Add PRM instance data for AM33xx SoC. Includes some basic register definitions and reset data for now. Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Tero Kristo <t-kristo@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index f845b8a415cc..38c26fd16c6d 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -58,6 +58,11 @@ struct omap_reset_data { #define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) +static const struct omap_rst_map rst_map_0[] = { + { .rst = 0, .st = 0 }, + { .rst = -1 }, +}; + static const struct omap_rst_map rst_map_01[] = { { .rst = 0, .st = 0 }, { .rst = 1, .st = 1 }, @@ -79,8 +84,27 @@ static const struct omap_prm_data omap4_prm_data[] = { { }, }; +static const struct omap_rst_map am3_per_rst_map[] = { + { .rst = 1 }, + { .rst = -1 }, +}; + +static const struct omap_rst_map am3_wkup_rst_map[] = { + { .rst = 3, .st = 5 }, + { .rst = -1 }, +}; + +static const struct omap_prm_data am3_prm_data[] = { + { .name = "per", .base = 0x44e00c00, .rstctrl = 0x0, .rstmap = am3_per_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp" }, + { .name = "wkup", .base = 0x44e00d00, .rstctrl = 0x0, .rstst = 0xc, .rstmap = am3_wkup_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, + { .name = "device", .base = 0x44e00f00, .rstctrl = 0x0, .rstst = 0x8, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, + { .name = "gfx", .base = 0x44e01100, .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3" }, + { }, +}; + static const struct of_device_id omap_prm_id_table[] = { { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, + { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, { }, }; -- cgit v1.2.3 From 59de827750f2ac001cb150101e600df3324e7656 Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:39 -0700 Subject: soc: ti: omap-prm: add dra7 PRM data Add PRM instance data for dra7 family of SoCs. Initially this is just used to provide reset support. Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Tero Kristo <t-kristo@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index 38c26fd16c6d..ad411395ad30 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -84,6 +84,19 @@ static const struct omap_prm_data omap4_prm_data[] = { { }, }; +static const struct omap_prm_data dra7_prm_data[] = { + { .name = "dsp1", .base = 0x4ae06400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { .name = "ipu", .base = 0x4ae06500, .rstctrl = 0x10, .rstst = 0x14, .clkdm_name = "ipu1", .rstmap = rst_map_012 }, + { .name = "core", .base = 0x4ae06700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu2", .rstmap = rst_map_012 }, + { .name = "iva", .base = 0x4ae06f00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, + { .name = "dsp2", .base = 0x4ae07b00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { .name = "eve1", .base = 0x4ae07b40, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { .name = "eve2", .base = 0x4ae07b80, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { .name = "eve3", .base = 0x4ae07bc0, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { .name = "eve4", .base = 0x4ae07c00, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { }, +}; + static const struct omap_rst_map am3_per_rst_map[] = { { .rst = 1 }, { .rst = -1 }, @@ -104,6 +117,7 @@ static const struct omap_prm_data am3_prm_data[] = { static const struct of_device_id omap_prm_id_table[] = { { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, + { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data }, { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, { }, }; -- cgit v1.2.3 From 01f5069efa628845afa2aad4b4aec752215f2179 Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:40 -0700 Subject: soc: ti: omap-prm: add am4 PRM data Add PRM instance data for am4 family of SoCs. Initially this is just used to provide reset support. Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Tero Kristo <t-kristo@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index ad411395ad30..bd3a0afa412a 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -115,10 +115,30 @@ static const struct omap_prm_data am3_prm_data[] = { { }, }; +static const struct omap_rst_map am4_per_rst_map[] = { + { .rst = 1, .st = 0 }, + { .rst = -1 }, +}; + +static const struct omap_rst_map am4_device_rst_map[] = { + { .rst = 0, .st = 1 }, + { .rst = 1, .st = 0 }, + { .rst = -1 }, +}; + +static const struct omap_prm_data am4_prm_data[] = { + { .name = "gfx", .base = 0x44df0400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3" }, + { .name = "per", .base = 0x44df0800, .rstctrl = 0x10, .rstst = 0x14, .rstmap = am4_per_rst_map, .clkdm_name = "pruss_ocp" }, + { .name = "wkup", .base = 0x44df2000, .rstctrl = 0x10, .rstst = 0x14, .rstmap = am3_wkup_rst_map, .flags = OMAP_PRM_HAS_NO_CLKDM }, + { .name = "device", .base = 0x44df4000, .rstctrl = 0x0, .rstst = 0x4, .rstmap = am4_device_rst_map, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, + { }, +}; + static const struct of_device_id omap_prm_id_table[] = { { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data }, { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, + { .compatible = "ti,am4-prm-inst", .data = am4_prm_data }, { }, }; -- cgit v1.2.3 From 5478f912d225a1745a4b3d5f564daa0004d35a42 Mon Sep 17 00:00:00 2001 From: Tero Kristo <t-kristo@ti.com> Date: Wed, 9 Oct 2019 08:55:40 -0700 Subject: soc: ti: omap-prm: add omap5 PRM data Add PRM instance data for omap5 family of SoCs. Initially this is just used to provide reset support. Reviewed-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Tero Kristo <t-kristo@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index bd3a0afa412a..db47a8bceb87 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -84,6 +84,14 @@ static const struct omap_prm_data omap4_prm_data[] = { { }, }; +static const struct omap_prm_data omap5_prm_data[] = { + { .name = "dsp", .base = 0x4ae06400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, + { .name = "core", .base = 0x4ae06700, .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu", .rstmap = rst_map_012 }, + { .name = "iva", .base = 0x4ae07200, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 }, + { .name = "device", .base = 0x4ae07c00, .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM }, + { }, +}; + static const struct omap_prm_data dra7_prm_data[] = { { .name = "dsp1", .base = 0x4ae06400, .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 }, { .name = "ipu", .base = 0x4ae06500, .rstctrl = 0x10, .rstst = 0x14, .clkdm_name = "ipu1", .rstmap = rst_map_012 }, @@ -136,6 +144,7 @@ static const struct omap_prm_data am4_prm_data[] = { static const struct of_device_id omap_prm_id_table[] = { { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, + { .compatible = "ti,omap5-prm-inst", .data = omap5_prm_data }, { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data }, { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, { .compatible = "ti,am4-prm-inst", .data = am4_prm_data }, -- cgit v1.2.3 From 2bfd3e7651addcaf48f12d4f11ea9d8fca6c3aa8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd <swboyd@chromium.org> Date: Tue, 8 Oct 2019 16:45:04 -0700 Subject: soc: qcom: llcc: Name regmaps to avoid collisions We'll end up with debugfs collisions if we don't give names to the regmaps created by this driver. Change the name of the config before registering it so we don't collide in debugfs. Fixes: 7f9c136216c7 ("soc: qcom: Add broadcast base for Last Level Cache Controller (LLCC)") Cc: Venkata Narendra Kumar Gutta <vnkgutta@codeaurora.org> Reviewed-by: Evan Green <evgreen@chromium.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/llcc-qcom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 436067367db4..3550eb7f7568 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -119,7 +119,7 @@ static const struct qcom_llcc_config sdm845_cfg = { static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; -static const struct regmap_config llcc_regmap_config = { +static struct regmap_config llcc_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, @@ -393,6 +393,7 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, if (IS_ERR(base)) return ERR_CAST(base); + llcc_regmap_config.name = name; return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); } -- cgit v1.2.3 From acbf73bfa02881ba9e0532ba1a5f5beec573af9f Mon Sep 17 00:00:00 2001 From: Stephen Boyd <swboyd@chromium.org> Date: Tue, 8 Oct 2019 16:45:05 -0700 Subject: soc: qcom: llcc: Move regmap config to local variable This is now a global variable that we're modifying to fix the name. That isn't terribly thread safe and it's not necessary to be a global so let's just move this to a local variable instead. This saves space in the symtab and actually reduces kernel image size because the regmap config is large and we can replace the initialization of that structure with a memset and a few member assignments. Cc: Venkata Narendra Kumar Gutta <vnkgutta@codeaurora.org> Reviewed-by: Evan Green <evgreen@chromium.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/llcc-qcom.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 3550eb7f7568..4bd982a294ce 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -119,13 +119,6 @@ static const struct qcom_llcc_config sdm845_cfg = { static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; -static struct regmap_config llcc_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .fast_io = true, -}; - /** * llcc_slice_getd - get llcc slice descriptor * @uid: usecase_id for the client @@ -384,6 +377,12 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, { struct resource *res; void __iomem *base; + struct regmap_config llcc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + }; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); if (!res) -- cgit v1.2.3 From e502ff8606b32df4f9f2435ab00278312db125b3 Mon Sep 17 00:00:00 2001 From: Tejas Patel <tejas.patel@xilinx.com> Date: Mon, 26 Aug 2019 13:30:44 -0700 Subject: soc: xilinx: Set CAP_UNUSABLE requirement for versal while powering down domain For "0" requirement which is used to inform firmware that device is not required currently by master, Versal PLM (Platform Loader and Manager) which runs on Platform Management Controller and is responsible platform management of devices that disables clock, power it down and reset the device. genpd_power_off() is being called during runtime suspend also. So, if any device goes to runtime suspend state during resumes it needs to be re-initialized again. It is possible that drivers do not reinitialize device upon resume from runtime suspend every time ans so dont want it to be powered down or get reset during runtime suspend. In Versal PLM new PM_CAP_UNUSABLE capability is added, which disables clock only and avoids power down and reset during runtime suspend. Power and reset will be gated with core suspend.So, this patch sets CAPABILITY_UNUSABLE requirement during gpd_power_off() if platform is other than zynqmp. Signed-off-by: Tejas Patel <tejas.patel@xilinx.com> Signed-off-by: Jolly Shah <jolly.shah@xilinx.com> Signed-off-by: Michal Simek <michal.simek@xilinx.com> --- drivers/soc/xilinx/zynqmp_pm_domains.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c index 600f57cf0c2e..23d90cb12ba9 100644 --- a/drivers/soc/xilinx/zynqmp_pm_domains.c +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c @@ -2,7 +2,7 @@ /* * ZynqMP Generic PM domain support * - * Copyright (C) 2015-2018 Xilinx, Inc. + * Copyright (C) 2015-2019 Xilinx, Inc. * * Davorin Mista <davorin.mista@aggios.com> * Jolly Shah <jollys@xilinx.com> @@ -25,6 +25,8 @@ static const struct zynqmp_eemi_ops *eemi_ops; +static int min_capability; + /** * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain * @gpd: Generic power domain @@ -106,7 +108,7 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) int ret; struct pm_domain_data *pdd, *tmp; struct zynqmp_pm_domain *pd; - u32 capabilities = 0; + u32 capabilities = min_capability; bool may_wakeup; if (!eemi_ops->set_requirement) @@ -283,6 +285,10 @@ static int zynqmp_gpd_probe(struct platform_device *pdev) if (!domains) return -ENOMEM; + if (!of_device_is_compatible(dev->parent->of_node, + "xlnx,zynqmp-firmware")) + min_capability = ZYNQMP_PM_CAPABILITY_UNUSABLE; + for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) { pd->node_id = 0; pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i); -- cgit v1.2.3 From 9f1022b8bd14ce937ca56174a97a1ce475c07693 Mon Sep 17 00:00:00 2001 From: Thierry Reding <treding@nvidia.com> Date: Tue, 20 Aug 2019 15:53:44 +0200 Subject: soc/tegra: fuse: Restore base on sysfs failure Make sure to also restore the register base address on sysfs registration failure. Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/fuse/fuse-tegra.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 58996c6ea767..db516a2a3807 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -146,20 +146,24 @@ static int tegra_fuse_probe(struct platform_device *pdev) if (fuse->soc->probe) { err = fuse->soc->probe(fuse); - if (err < 0) { - fuse->base = base; - return err; - } + if (err < 0) + goto restore; } if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size, - fuse->soc->info)) - return -ENODEV; + fuse->soc->info)) { + err = -ENODEV; + goto restore; + } /* release the early I/O memory mapping */ iounmap(base); return 0; + +restore: + fuse->base = base; + return err; } static struct platform_driver tegra_fuse_driver = { -- cgit v1.2.3 From 96ee12b2a203167ffda7ac4a444418ca53df056d Mon Sep 17 00:00:00 2001 From: Thierry Reding <treding@nvidia.com> Date: Tue, 20 Aug 2019 15:57:16 +0200 Subject: soc/tegra: fuse: Implement nvmem device The nvmem framework provides a generic infrastructure and API to access the type of information stored in fuses such as the Tegra FUSE block. Implement an nvmem device that can be used to access the information in a more generic way to decouple consumers from the custom Tegra API and to add a more formal way of creating the dependency between the FUSE device and the consumers. Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/fuse/fuse-tegra.c | 82 ++++++++++++++++--------------------- drivers/soc/tegra/fuse/fuse.h | 3 ++ 2 files changed, 38 insertions(+), 47 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index db516a2a3807..430a47963a57 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -8,6 +8,8 @@ #include <linux/kobject.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -31,50 +33,6 @@ static const char *tegra_revision_name[TEGRA_REVISION_MAX] = { [TEGRA_REVISION_A04] = "A04", }; -static u8 fuse_readb(struct tegra_fuse *fuse, unsigned int offset) -{ - u32 val; - - val = fuse->read(fuse, round_down(offset, 4)); - val >>= (offset % 4) * 8; - val &= 0xff; - - return val; -} - -static ssize_t fuse_read(struct file *fd, struct kobject *kobj, - struct bin_attribute *attr, char *buf, - loff_t pos, size_t size) -{ - struct device *dev = kobj_to_dev(kobj); - struct tegra_fuse *fuse = dev_get_drvdata(dev); - int i; - - if (pos < 0 || pos >= attr->size) - return 0; - - if (size > attr->size - pos) - size = attr->size - pos; - - for (i = 0; i < size; i++) - buf[i] = fuse_readb(fuse, pos + i); - - return i; -} - -static struct bin_attribute fuse_bin_attr = { - .attr = { .name = "fuse", .mode = S_IRUGO, }, - .read = fuse_read, -}; - -static int tegra_fuse_create_sysfs(struct device *dev, unsigned int size, - const struct tegra_fuse_info *info) -{ - fuse_bin_attr.size = size; - - return device_create_bin_file(dev, &fuse_bin_attr); -} - static const struct of_device_id car_match[] __initconst = { { .compatible = "nvidia,tegra20-car", }, { .compatible = "nvidia,tegra30-car", }, @@ -115,9 +73,23 @@ static const struct of_device_id tegra_fuse_match[] = { { /* sentinel */ } }; +static int tegra_fuse_read(void *priv, unsigned int offset, void *value, + size_t bytes) +{ + unsigned int count = bytes / 4, i; + struct tegra_fuse *fuse = priv; + u32 *buffer = value; + + for (i = 0; i < count; i++) + buffer[i] = fuse->read(fuse, offset + i * 4); + + return 0; +} + static int tegra_fuse_probe(struct platform_device *pdev) { void __iomem *base = fuse->base; + struct nvmem_config nvmem; struct resource *res; int err; @@ -150,9 +122,25 @@ static int tegra_fuse_probe(struct platform_device *pdev) goto restore; } - if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size, - fuse->soc->info)) { - err = -ENODEV; + memset(&nvmem, 0, sizeof(nvmem)); + nvmem.dev = &pdev->dev; + nvmem.name = "fuse"; + nvmem.id = -1; + nvmem.owner = THIS_MODULE; + nvmem.type = NVMEM_TYPE_OTP; + nvmem.read_only = true; + nvmem.root_only = true; + nvmem.reg_read = tegra_fuse_read; + nvmem.size = fuse->soc->info->size; + nvmem.word_size = 4; + nvmem.stride = 4; + nvmem.priv = fuse; + + fuse->nvmem = devm_nvmem_register(&pdev->dev, &nvmem); + if (IS_ERR(fuse->nvmem)) { + err = PTR_ERR(fuse->nvmem); + dev_err(&pdev->dev, "failed to register NVMEM device: %d\n", + err); goto restore; } diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index 7230cb330503..32bf6c070ae7 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -13,6 +13,7 @@ #include <linux/dmaengine.h> #include <linux/types.h> +struct nvmem_device; struct tegra_fuse; struct tegra_fuse_info { @@ -48,6 +49,8 @@ struct tegra_fuse { dma_addr_t phys; u32 *virt; } apbdma; + + struct nvmem_device *nvmem; }; void tegra_init_revision(void); -- cgit v1.2.3 From f4619c7f68ba02f8357a9d42340a6dc8a229268d Mon Sep 17 00:00:00 2001 From: Thierry Reding <treding@nvidia.com> Date: Tue, 20 Aug 2019 15:59:49 +0200 Subject: soc/tegra: fuse: Add cell information Create nvmem cells for all the fuses currently used by consumers. Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/fuse/fuse-tegra.c | 90 +++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 430a47963a57..cbe3d6f19074 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -86,6 +86,94 @@ static int tegra_fuse_read(void *priv, unsigned int offset, void *value, return 0; } +static const struct nvmem_cell_info tegra_fuse_cells[] = { + { + .name = "tsensor-cpu1", + .offset = 0x084, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-cpu2", + .offset = 0x088, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-cpu0", + .offset = 0x098, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "xusb-pad-calibration", + .offset = 0x0f0, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-cpu3", + .offset = 0x12c, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "sata-calibration", + .offset = 0x124, + .bytes = 1, + .bit_offset = 0, + .nbits = 2, + }, { + .name = "tsensor-gpu", + .offset = 0x154, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-mem0", + .offset = 0x158, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-mem1", + .offset = 0x15c, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-pllx", + .offset = 0x160, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-common", + .offset = 0x180, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "tsensor-realignment", + .offset = 0x1fc, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "gpu-calibration", + .offset = 0x204, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, { + .name = "xusb-pad-calibration-ext", + .offset = 0x250, + .bytes = 4, + .bit_offset = 0, + .nbits = 32, + }, +}; + static int tegra_fuse_probe(struct platform_device *pdev) { void __iomem *base = fuse->base; @@ -127,6 +215,8 @@ static int tegra_fuse_probe(struct platform_device *pdev) nvmem.name = "fuse"; nvmem.id = -1; nvmem.owner = THIS_MODULE; + nvmem.cells = tegra_fuse_cells; + nvmem.ncells = ARRAY_SIZE(tegra_fuse_cells); nvmem.type = NVMEM_TYPE_OTP; nvmem.read_only = true; nvmem.root_only = true; -- cgit v1.2.3 From 9f94fadd75d34acec19c164ffb1b60c66d72f898 Mon Sep 17 00:00:00 2001 From: Thierry Reding <treding@nvidia.com> Date: Tue, 20 Aug 2019 16:01:04 +0200 Subject: soc/tegra: fuse: Register cell lookups for compatibility Typically nvmem cells would be stored in device tree. However, for compatibility with device trees that don't contain nvmem cell definitions, register lookups for cells currently used by consumers. This allows the consumers to use the same API to query cells from the device tree or using the legacy mechanism. Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/fuse/fuse-tegra.c | 9 ++ drivers/soc/tegra/fuse/fuse-tegra30.c | 154 ++++++++++++++++++++++++++++++++++ drivers/soc/tegra/fuse/fuse.h | 5 ++ 3 files changed, 168 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index cbe3d6f19074..4d719d4b8d5a 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -423,6 +423,15 @@ static int __init tegra_init_fuse(void) pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); + if (fuse->soc->lookups) { + size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups; + + fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL); + if (!fuse->lookups) + return -ENOMEM; + + nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); + } return 0; } diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index be9424a87173..b8daaf5b7291 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -8,6 +8,7 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/nvmem-consumer.h> #include <linux/of_device.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -127,6 +128,70 @@ const struct tegra_fuse_soc tegra114_fuse_soc = { #endif #if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) +static const struct nvmem_cell_lookup tegra124_fuse_lookups[] = { + { + .nvmem_name = "fuse", + .cell_name = "xusb-pad-calibration", + .dev_id = "7009f000.padctl", + .con_id = "calibration", + }, { + .nvmem_name = "fuse", + .cell_name = "sata-calibration", + .dev_id = "70020000.sata", + .con_id = "calibration", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-common", + .dev_id = "700e2000.thermal-sensor", + .con_id = "common", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-realignment", + .dev_id = "700e2000.thermal-sensor", + .con_id = "realignment", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu0", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu0", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu1", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu1", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu2", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu2", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu3", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu3", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-mem0", + .dev_id = "700e2000.thermal-sensor", + .con_id = "mem0", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-mem1", + .dev_id = "700e2000.thermal-sensor", + .con_id = "mem1", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-gpu", + .dev_id = "700e2000.thermal-sensor", + .con_id = "gpu", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-pllx", + .dev_id = "700e2000.thermal-sensor", + .con_id = "pllx", + }, +}; + static const struct tegra_fuse_info tegra124_fuse_info = { .read = tegra30_fuse_read, .size = 0x300, @@ -137,10 +202,81 @@ const struct tegra_fuse_soc tegra124_fuse_soc = { .init = tegra30_fuse_init, .speedo_init = tegra124_init_speedo_data, .info = &tegra124_fuse_info, + .lookups = tegra124_fuse_lookups, + .num_lookups = ARRAY_SIZE(tegra124_fuse_lookups), }; #endif #if defined(CONFIG_ARCH_TEGRA_210_SOC) +static const struct nvmem_cell_lookup tegra210_fuse_lookups[] = { + { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu1", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu1", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu2", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu2", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu0", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu0", + }, { + .nvmem_name = "fuse", + .cell_name = "xusb-pad-calibration", + .dev_id = "7009f000.padctl", + .con_id = "calibration", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-cpu3", + .dev_id = "700e2000.thermal-sensor", + .con_id = "cpu3", + }, { + .nvmem_name = "fuse", + .cell_name = "sata-calibration", + .dev_id = "70020000.sata", + .con_id = "calibration", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-gpu", + .dev_id = "700e2000.thermal-sensor", + .con_id = "gpu", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-mem0", + .dev_id = "700e2000.thermal-sensor", + .con_id = "mem0", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-mem1", + .dev_id = "700e2000.thermal-sensor", + .con_id = "mem1", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-pllx", + .dev_id = "700e2000.thermal-sensor", + .con_id = "pllx", + }, { + .nvmem_name = "fuse", + .cell_name = "tsensor-common", + .dev_id = "700e2000.thermal-sensor", + .con_id = "common", + }, { + .nvmem_name = "fuse", + .cell_name = "gpu-calibration", + .dev_id = "57000000.gpu", + .con_id = "calibration", + }, { + .nvmem_name = "fuse", + .cell_name = "xusb-pad-calibration-ext", + .dev_id = "7009f000.padctl", + .con_id = "calibration-ext", + }, +}; + static const struct tegra_fuse_info tegra210_fuse_info = { .read = tegra30_fuse_read, .size = 0x300, @@ -151,10 +287,26 @@ const struct tegra_fuse_soc tegra210_fuse_soc = { .init = tegra30_fuse_init, .speedo_init = tegra210_init_speedo_data, .info = &tegra210_fuse_info, + .lookups = tegra210_fuse_lookups, + .num_lookups = ARRAY_SIZE(tegra210_fuse_lookups), }; #endif #if defined(CONFIG_ARCH_TEGRA_186_SOC) +static const struct nvmem_cell_lookup tegra186_fuse_lookups[] = { + { + .nvmem_name = "fuse", + .cell_name = "xusb-pad-calibration", + .dev_id = "3520000.padctl", + .con_id = "calibration", + }, { + .nvmem_name = "fuse", + .cell_name = "xusb-pad-calibration-ext", + .dev_id = "3520000.padctl", + .con_id = "calibration-ext", + }, +}; + static const struct tegra_fuse_info tegra186_fuse_info = { .read = tegra30_fuse_read, .size = 0x300, @@ -164,5 +316,7 @@ static const struct tegra_fuse_info tegra186_fuse_info = { const struct tegra_fuse_soc tegra186_fuse_soc = { .init = tegra30_fuse_init, .info = &tegra186_fuse_info, + .lookups = tegra186_fuse_lookups, + .num_lookups = ARRAY_SIZE(tegra186_fuse_lookups), }; #endif diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index 32bf6c070ae7..0f74c2c34af0 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -13,6 +13,7 @@ #include <linux/dmaengine.h> #include <linux/types.h> +struct nvmem_cell_lookup; struct nvmem_device; struct tegra_fuse; @@ -28,6 +29,9 @@ struct tegra_fuse_soc { int (*probe)(struct tegra_fuse *fuse); const struct tegra_fuse_info *info; + + const struct nvmem_cell_lookup *lookups; + unsigned int num_lookups; }; struct tegra_fuse { @@ -51,6 +55,7 @@ struct tegra_fuse { } apbdma; struct nvmem_device *nvmem; + struct nvmem_cell_lookup *lookups; }; void tegra_init_revision(void); -- cgit v1.2.3 From 669f78802b015f4f038a33f653f4fd1474539764 Mon Sep 17 00:00:00 2001 From: Vivek Gautam <vivek.gautam@codeaurora.org> Date: Sat, 19 Oct 2019 17:07:11 +0530 Subject: soc: qcom: llcc: Add configuration data for SC7180 Add LLCC configuration data for SC7180 SoC which controls LLCC behaviour. Reviewed-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org> Signed-off-by: Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> --- drivers/soc/qcom/llcc-qcom.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 4bd982a294ce..429b5a60a1ba 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -91,7 +91,14 @@ struct qcom_llcc_config { int size; }; -static struct llcc_slice_config sdm845_data[] = { +static const struct llcc_slice_config sc7180_data[] = { + { LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 }, + { LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPUHTW, 11, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPU, 12, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, +}; + +static const struct llcc_slice_config sdm845_data[] = { { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, @@ -112,6 +119,11 @@ static struct llcc_slice_config sdm845_data[] = { { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, }; +static const struct qcom_llcc_config sc7180_cfg = { + .sct_data = sc7180_data, + .size = ARRAY_SIZE(sc7180_data), +}; + static const struct qcom_llcc_config sdm845_cfg = { .sct_data = sdm845_data, .size = ARRAY_SIZE(sdm845_data), @@ -485,6 +497,7 @@ err: } static const struct of_device_id qcom_llcc_of_match[] = { + { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, { } }; -- cgit v1.2.3 From a0708f559e4fa2239b84e927e661d26e6864e185 Mon Sep 17 00:00:00 2001 From: Anson Huang <Anson.Huang@nxp.com> Date: Fri, 25 Oct 2019 14:56:22 +0800 Subject: soc: imx8: Using existing serial_number instead of UID The soc_device_attribute structure already contains a serial_number attribute to show SoC's unique ID, just use it to show SoC's unique ID instead of creating a new file called soc_uid. Signed-off-by: Anson Huang <Anson.Huang@nxp.com> Signed-off-by: Shawn Guo <shawnguo@kernel.org> --- drivers/soc/imx/soc-imx8.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index b9831576dd25..fcbf745a9971 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -29,14 +29,6 @@ struct imx8_soc_data { static u64 soc_uid; -static ssize_t soc_uid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%016llX\n", soc_uid); -} - -static DEVICE_ATTR_RO(soc_uid); - static u32 __init imx8mq_soc_revision(void) { struct device_node *np; @@ -174,22 +166,25 @@ static int __init imx8_soc_init(void) goto free_soc; } + soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid); + if (!soc_dev_attr->serial_number) { + ret = -ENOMEM; + goto free_rev; + } + soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { ret = PTR_ERR(soc_dev); - goto free_rev; + goto free_serial_number; } - ret = device_create_file(soc_device_to_device(soc_dev), - &dev_attr_soc_uid); - if (ret) - goto free_rev; - if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); return 0; +free_serial_number: + kfree(soc_dev_attr->serial_number); free_rev: if (strcmp(soc_dev_attr->revision, "unknown")) kfree(soc_dev_attr->revision); -- cgit v1.2.3 From 7ae399b7d009c7348b9451ac41cd49671b31cf3a Mon Sep 17 00:00:00 2001 From: Anson Huang <Anson.Huang@nxp.com> Date: Fri, 25 Oct 2019 14:56:23 +0800 Subject: soc: imx-scu: Using existing serial_number instead of UID The soc_device_attribute structure already contains a serial_number attribute to show SoC's unique ID, just use it to show SoC's unique ID instead of creating a new file called soc_uid. Signed-off-by: Anson Huang <Anson.Huang@nxp.com> Signed-off-by: Shawn Guo <shawnguo@kernel.org> --- drivers/soc/imx/soc-imx-scu.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/soc-imx-scu.c b/drivers/soc/imx/soc-imx-scu.c index 50831ebf126a..f2eace5b50fe 100644 --- a/drivers/soc/imx/soc-imx-scu.c +++ b/drivers/soc/imx/soc-imx-scu.c @@ -33,12 +33,10 @@ struct imx_sc_msg_misc_get_soc_uid { u32 uid_high; } __packed; -static ssize_t soc_uid_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int imx_scu_soc_uid(u64 *soc_uid) { struct imx_sc_msg_misc_get_soc_uid msg; struct imx_sc_rpc_msg *hdr = &msg.hdr; - u64 soc_uid; int ret; hdr->ver = IMX_SC_RPC_VERSION; @@ -52,15 +50,13 @@ static ssize_t soc_uid_show(struct device *dev, return ret; } - soc_uid = msg.uid_high; - soc_uid <<= 32; - soc_uid |= msg.uid_low; + *soc_uid = msg.uid_high; + *soc_uid <<= 32; + *soc_uid |= msg.uid_low; - return sprintf(buf, "%016llX\n", soc_uid); + return 0; } -static DEVICE_ATTR_RO(soc_uid); - static int imx_scu_soc_id(void) { struct imx_sc_msg_misc_get_soc_id msg; @@ -89,6 +85,7 @@ static int imx_scu_soc_probe(struct platform_device *pdev) struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; int id, ret; + u64 uid = 0; u32 val; ret = imx_scu_get_handle(&soc_ipc_handle); @@ -112,6 +109,10 @@ static int imx_scu_soc_probe(struct platform_device *pdev) if (id < 0) return -EINVAL; + ret = imx_scu_soc_uid(&uid); + if (ret < 0) + return -EINVAL; + /* format soc_id value passed from SCU firmware */ val = id & 0x1f; soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x", val); @@ -130,19 +131,22 @@ static int imx_scu_soc_probe(struct platform_device *pdev) goto free_soc_id; } + soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", uid); + if (!soc_dev_attr->serial_number) { + ret = -ENOMEM; + goto free_revision; + } + soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { ret = PTR_ERR(soc_dev); - goto free_revision; + goto free_serial_number; } - ret = device_create_file(soc_device_to_device(soc_dev), - &dev_attr_soc_uid); - if (ret) - goto free_revision; - return 0; +free_serial_number: + kfree(soc_dev_attr->serial_number); free_revision: kfree(soc_dev_attr->revision); free_soc_id: -- cgit v1.2.3 From 5ea428595cc53677a0a5bacd950307463c40411f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki <s.nawrocki@samsung.com> Date: Mon, 28 Oct 2019 16:15:33 +0100 Subject: soc: samsung: Add Exynos Adaptive Supply Voltage driver The Adaptive Supply Voltage (ASV) driver adjusts CPU cluster operating points depending on exact revision of an SoC retrieved from the CHIPID block or the OTP memory. This allows for some power saving as for some CPU clock frequencies we can lower CPU cluster's supply voltage comparing to safe values common to all the SoC revisions. This patch adds support for Exynos5422/5800 SoC, it is partially based on code from https://github.com/hardkernel/linux repository, branch odroidxu4-4.14.y, files: arch/arm/mach-exynos/exynos5422-asv.[ch]. Tested on Odroid XU3, XU4, XU3 Lite. Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> --- drivers/soc/samsung/Kconfig | 10 + drivers/soc/samsung/Makefile | 3 + drivers/soc/samsung/exynos-asv.c | 177 ++++++++++++ drivers/soc/samsung/exynos-asv.h | 71 +++++ drivers/soc/samsung/exynos5422-asv.c | 505 +++++++++++++++++++++++++++++++++++ drivers/soc/samsung/exynos5422-asv.h | 31 +++ 6 files changed, 797 insertions(+) create mode 100644 drivers/soc/samsung/exynos-asv.c create mode 100644 drivers/soc/samsung/exynos-asv.h create mode 100644 drivers/soc/samsung/exynos5422-asv.c create mode 100644 drivers/soc/samsung/exynos5422-asv.h (limited to 'drivers/soc') diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 33ad0de2de3c..27fc59bbb520 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -7,6 +7,16 @@ menuconfig SOC_SAMSUNG if SOC_SAMSUNG +config EXYNOS_ASV + bool "Exynos Adaptive Supply Voltage support" if COMPILE_TEST + depends on (ARCH_EXYNOS && EXYNOS_CHIPID) || COMPILE_TEST + select EXYNOS_ASV_ARM if ARM && ARCH_EXYNOS + +# There is no need to enable these drivers for ARMv8 +config EXYNOS_ASV_ARM + bool "Exynos ASV ARMv7-specific driver extensions" if COMPILE_TEST + depends on EXYNOS_ASV + config EXYNOS_CHIPID bool "Exynos Chipid controller driver" if COMPILE_TEST depends on ARCH_EXYNOS || COMPILE_TEST diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile index 3b6a8797416c..edd1d6ea064d 100644 --- a/drivers/soc/samsung/Makefile +++ b/drivers/soc/samsung/Makefile @@ -1,5 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_EXYNOS_ASV) += exynos-asv.o +obj-$(CONFIG_EXYNOS_ASV_ARM) += exynos5422-asv.o + obj-$(CONFIG_EXYNOS_CHIPID) += exynos-chipid.o obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o diff --git a/drivers/soc/samsung/exynos-asv.c b/drivers/soc/samsung/exynos-asv.c new file mode 100644 index 000000000000..8abf4dfaa5c5 --- /dev/null +++ b/drivers/soc/samsung/exynos-asv.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * Samsung Exynos SoC Adaptive Supply Voltage support + */ + +#include <linux/cpu.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_opp.h> +#include <linux/regmap.h> +#include <linux/soc/samsung/exynos-chipid.h> + +#include "exynos-asv.h" +#include "exynos5422-asv.h" + +#define MHZ 1000000U + +static int exynos_asv_update_cpu_opps(struct exynos_asv *asv, + struct device *cpu) +{ + struct exynos_asv_subsys *subsys = NULL; + struct dev_pm_opp *opp; + unsigned int opp_freq; + int i; + + for (i = 0; i < ARRAY_SIZE(asv->subsys); i++) { + if (of_device_is_compatible(cpu->of_node, + asv->subsys[i].cpu_dt_compat)) { + subsys = &asv->subsys[i]; + break; + } + } + if (!subsys) + return -EINVAL; + + for (i = 0; i < subsys->table.num_rows; i++) { + unsigned int new_volt, volt; + int ret; + + opp_freq = exynos_asv_opp_get_frequency(subsys, i); + + opp = dev_pm_opp_find_freq_exact(cpu, opp_freq * MHZ, true); + if (IS_ERR(opp)) { + dev_info(asv->dev, "cpu%d opp%d, freq: %u missing\n", + cpu->id, i, opp_freq); + + continue; + } + + volt = dev_pm_opp_get_voltage(opp); + new_volt = asv->opp_get_voltage(subsys, i, volt); + dev_pm_opp_put(opp); + + if (new_volt == volt) + continue; + + ret = dev_pm_opp_adjust_voltage(cpu, opp_freq * MHZ, + new_volt, new_volt, new_volt); + if (ret < 0) + dev_err(asv->dev, + "Failed to adjust OPP %u Hz/%u uV for cpu%d\n", + opp_freq, new_volt, cpu->id); + else + dev_dbg(asv->dev, + "Adjusted OPP %u Hz/%u -> %u uV, cpu%d\n", + opp_freq, volt, new_volt, cpu->id); + } + + return 0; +} + +static int exynos_asv_update_opps(struct exynos_asv *asv) +{ + struct opp_table *last_opp_table = NULL; + struct device *cpu; + int ret, cpuid; + + for_each_possible_cpu(cpuid) { + struct opp_table *opp_table; + + cpu = get_cpu_device(cpuid); + if (!cpu) + continue; + + opp_table = dev_pm_opp_get_opp_table(cpu); + if (IS_ERR(opp_table)) + continue; + + if (!last_opp_table || opp_table != last_opp_table) { + last_opp_table = opp_table; + + ret = exynos_asv_update_cpu_opps(asv, cpu); + if (ret < 0) + dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n", + cpuid); + } + + dev_pm_opp_put_opp_table(opp_table); + } + + return 0; +} + +static int exynos_asv_probe(struct platform_device *pdev) +{ + int (*probe_func)(struct exynos_asv *asv); + struct exynos_asv *asv; + struct device *cpu_dev; + u32 product_id = 0; + int ret, i; + + cpu_dev = get_cpu_device(0); + ret = dev_pm_opp_get_opp_count(cpu_dev); + if (ret < 0) + return -EPROBE_DEFER; + + asv = devm_kzalloc(&pdev->dev, sizeof(*asv), GFP_KERNEL); + if (!asv) + return -ENOMEM; + + asv->chipid_regmap = device_node_to_regmap(pdev->dev.of_node); + if (IS_ERR(asv->chipid_regmap)) { + dev_err(&pdev->dev, "Could not find syscon regmap\n"); + return PTR_ERR(asv->chipid_regmap); + } + + regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id); + + switch (product_id & EXYNOS_MASK) { + case 0xE5422000: + probe_func = exynos5422_asv_init; + break; + default: + return -ENODEV; + } + + ret = of_property_read_u32(pdev->dev.of_node, "samsung,asv-bin", + &asv->of_bin); + if (ret < 0) + asv->of_bin = -EINVAL; + + asv->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, asv); + + for (i = 0; i < ARRAY_SIZE(asv->subsys); i++) + asv->subsys[i].asv = asv; + + ret = probe_func(asv); + if (ret < 0) + return ret; + + return exynos_asv_update_opps(asv); +} + +static const struct of_device_id exynos_asv_of_device_ids[] = { + { .compatible = "samsung,exynos4210-chipid" }, + {} +}; + +static struct platform_driver exynos_asv_driver = { + .driver = { + .name = "exynos-asv", + .of_match_table = exynos_asv_of_device_ids, + }, + .probe = exynos_asv_probe, +}; +module_platform_driver(exynos_asv_driver); diff --git a/drivers/soc/samsung/exynos-asv.h b/drivers/soc/samsung/exynos-asv.h new file mode 100644 index 000000000000..3fd1f2acd999 --- /dev/null +++ b/drivers/soc/samsung/exynos-asv.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * Samsung Exynos SoC Adaptive Supply Voltage support + */ +#ifndef __LINUX_SOC_EXYNOS_ASV_H +#define __LINUX_SOC_EXYNOS_ASV_H + +struct regmap; + +/* HPM, IDS values to select target group */ +struct asv_limit_entry { + unsigned int hpm; + unsigned int ids; +}; + +struct exynos_asv_table { + unsigned int num_rows; + unsigned int num_cols; + u32 *buf; +}; + +struct exynos_asv_subsys { + struct exynos_asv *asv; + const char *cpu_dt_compat; + int id; + struct exynos_asv_table table; + + unsigned int base_volt; + unsigned int offset_volt_h; + unsigned int offset_volt_l; +}; + +struct exynos_asv { + struct device *dev; + struct regmap *chipid_regmap; + struct exynos_asv_subsys subsys[2]; + + int (*opp_get_voltage)(const struct exynos_asv_subsys *subs, + int level, unsigned int voltage); + unsigned int group; + unsigned int table; + + /* True if SG fields from PKG_ID register should be used */ + bool use_sg; + /* ASV bin read from DT */ + int of_bin; +}; + +static inline u32 __asv_get_table_entry(const struct exynos_asv_table *table, + unsigned int row, unsigned int col) +{ + return table->buf[row * (table->num_cols) + col]; +} + +static inline u32 exynos_asv_opp_get_voltage(const struct exynos_asv_subsys *subsys, + unsigned int level, unsigned int group) +{ + return __asv_get_table_entry(&subsys->table, level, group + 1); +} + +static inline u32 exynos_asv_opp_get_frequency(const struct exynos_asv_subsys *subsys, + unsigned int level) +{ + return __asv_get_table_entry(&subsys->table, level, 0); +} + +#endif /* __LINUX_SOC_EXYNOS_ASV_H */ diff --git a/drivers/soc/samsung/exynos5422-asv.c b/drivers/soc/samsung/exynos5422-asv.c new file mode 100644 index 000000000000..01bb3050d678 --- /dev/null +++ b/drivers/soc/samsung/exynos5422-asv.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Samsung Exynos 5422 SoC Adaptive Supply Voltage support + */ + +#include <linux/bitrev.h> +#include <linux/errno.h> +#include <linux/regmap.h> +#include <linux/soc/samsung/exynos-chipid.h> +#include <linux/slab.h> + +#include "exynos-asv.h" +#include "exynos5422-asv.h" + +#define ASV_GROUPS_NUM 14 +#define ASV_ARM_DVFS_NUM 20 +#define ASV_ARM_BIN2_DVFS_NUM 17 +#define ASV_KFC_DVFS_NUM 14 +#define ASV_KFC_BIN2_DVFS_NUM 12 + +/* + * This array is a set of 4 ASV data tables, first column of each ASV table + * contains frequency value in MHz and subsequent columns contain the CPU + * cluster's supply voltage values in uV. + * In order to create a set of OPPs for specific SoC revision one of the voltage + * columns (1...14) from one of the tables (0...3) is selected during + * initialization. There are separate ASV tables for the big (ARM) and little + * (KFC) CPU cluster. Only OPPs which are already defined in devicetree + * will be updated. + */ + +static const u32 asv_arm_table[][ASV_ARM_DVFS_NUM][ASV_GROUPS_NUM + 1] = { +{ + /* ARM 0, 1 */ + { 2100, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, + 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 }, + { 2000, 1312500, 1312500, 1300000, 1287500, 1275000, 1262500, 1250000, + 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 }, + { 1900, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, + 1162500, 1150000, 1162500, 1150000, 1137500, 1125000, 1112500 }, + { 1800, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, + 1112500, 1100000, 1112500, 1100000, 1087500, 1075000, 1062500 }, + { 1700, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, + 1075000, 1062500, 1075000, 1062500, 1050000, 1037500, 1025000 }, + { 1600, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, + 1037500, 1025000, 1037500, 1025000, 1012500, 1000000, 987500 }, + { 1500, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, + 1000000, 987500, 1000000, 987500, 975000, 962500, 950000 }, + { 1400, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, + 975000, 962500, 975000, 962500, 950000, 937500, 925000 }, + { 1300, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, + 962500, 950000, 962500, 950000, 937500, 925000, 912500 }, + { 1200, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, + 937500, 925000, 937500, 925000, 912500, 900000, 900000 }, + { 1100, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, + 912500, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 1000, 975000, 962500, 950000, 937500, 925000, 912500, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 900, 950000, 937500, 925000, 912500, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 800, 925000, 912500, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 700, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +}, { + /* ARM 2 */ + { 2100, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, + 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 }, + { 2000, 1312500, 1312500, 1312500, 1300000, 1275000, 1262500, 1250000, + 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 }, + { 1900, 1262500, 1250000, 1250000, 1237500, 1212500, 1200000, 1187500, + 1175000, 1162500, 1175000, 1162500, 1150000, 1137500, 1125000 }, + { 1800, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, + 1125000, 1112500, 1125000, 1112500, 1100000, 1087500, 1075000 }, + { 1700, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, + 1087500, 1075000, 1087500, 1075000, 1062500, 1050000, 1037500 }, + { 1600, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, + 1050000, 1037500, 1050000, 1037500, 1025000, 1012500, 1000000 }, + { 1500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, + 1012500, 1000000, 1012500, 1000000, 987500, 975000, 962500 }, + { 1400, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, + 987500, 975000, 987500, 975000, 962500, 950000, 937500 }, + { 1300, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, + 962500, 950000, 962500, 950000, 937500, 925000, 912500 }, + { 1200, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, + 937500, 925000, 937500, 925000, 912500, 900000, 900000 }, + { 1100, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, + 912500, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 1000, 975000, 962500, 950000, 937500, 925000, 912500, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 900, 950000, 937500, 925000, 912500, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 800, 925000, 912500, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 700, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +}, { + /* ARM 3 */ + { 2100, 1362500, 1362500, 1350000, 1337500, 1325000, 1312500, 1300000, + 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000 }, + { 2000, 1312500, 1312500, 1300000, 1287500, 1275000, 1262500, 1250000, + 1237500, 1225000, 1237500, 1225000, 1212500, 1200000, 1187500 }, + { 1900, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, + 1175000, 1162500, 1175000, 1162500, 1150000, 1137500, 1125000 }, + { 1800, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, + 1125000, 1112500, 1125000, 1112500, 1100000, 1087500, 1075000 }, + { 1700, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, + 1087500, 1075000, 1087500, 1075000, 1062500, 1050000, 1037500 }, + { 1600, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, + 1050000, 1037500, 1050000, 1037500, 1025000, 1012500, 1000000 }, + { 1500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, + 1012500, 1000000, 1012500, 1000000, 987500, 975000, 962500 }, + { 1400, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, + 987500, 975000, 987500, 975000, 962500, 950000, 937500 }, + { 1300, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, + 962500, 950000, 962500, 950000, 937500, 925000, 912500 }, + { 1200, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, + 937500, 925000, 937500, 925000, 912500, 900000, 900000 }, + { 1100, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, + 912500, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 1000, 975000, 962500, 950000, 937500, 925000, 912500, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 900, 950000, 937500, 925000, 912500, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 800, 925000, 912500, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 700, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +}, { + /* ARM bin 2 */ + { 1800, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, + 1150000, 1137500, 1150000, 1137500, 1125000, 1112500, 1100000 }, + { 1700, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, + 1112500, 1100000, 1112500, 1100000, 1087500, 1075000, 1062500 }, + { 1600, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, + 1075000, 1062500, 1075000, 1062500, 1050000, 1037500, 1025000 }, + { 1500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, + 1037500, 1025000, 1037500, 1025000, 1012500, 1000000, 987500 }, + { 1400, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, + 1012500, 1000000, 1012500, 1000000, 987500, 975000, 962500 }, + { 1300, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, + 1000000, 987500, 1000000, 987500, 975000, 962500, 950000 }, + { 1200, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, + 975000, 962500, 975000, 962500, 950000, 937500, 925000 }, + { 1100, 1037500, 1025000, 1012500, 1000000, 987500, 975000, 962500, + 950000, 937500, 950000, 937500, 925000, 912500, 900000 }, + { 1000, 1012500, 1000000, 987500, 975000, 962500, 950000, 937500, + 925000, 912500, 925000, 912500, 900000, 900000, 900000 }, + { 900, 987500, 975000, 962500, 950000, 937500, 925000, 912500, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 800, 962500, 950000, 937500, 925000, 912500, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 700, 937500, 925000, 912500, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 600, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +} +}; + +static const u32 asv_kfc_table[][ASV_KFC_DVFS_NUM][ASV_GROUPS_NUM + 1] = { +{ + /* KFC 0, 1 */ + { 1500000, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, + 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 }, + { 1400000, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, + 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 }, + { 1300000, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, + 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, + { 1200000, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, + 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 }, + { 1100000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, + 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000 }, + { 1000000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, + 1012500, 1000000, 987500, 975000, 962500, 950000, 937500 }, + { 900000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, + 975000, 962500, 950000, 937500, 925000, 912500, 900000 }, + { 800000, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, + 937500, 925000, 912500, 900000, 900000, 900000, 900000 }, + { 700000, 987500, 975000, 962500, 950000, 937500, 925000, 912500, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 600000, 950000, 937500, 925000, 912500, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500000, 912500, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400000, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300000, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200000, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +}, { + /* KFC 2 */ + { 1500, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, + 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 }, + { 1400, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, + 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 }, + { 1300, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, + 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, + { 1200, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, + 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 }, + { 1100, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, + 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000 }, + { 1000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, + 1012500, 1000000, 987500, 975000, 962500, 950000, 937500 }, + { 900, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, + 975000, 962500, 950000, 937500, 925000, 912500, 900000 }, + { 800, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, + 937500, 925000, 912500, 900000, 900000, 900000, 900000 }, + { 700, 987500, 975000, 962500, 950000, 937500, 925000, 912500, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 600, 950000, 937500, 925000, 912500, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500, 912500, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +}, { + /* KFC 3 */ + { 1500, 1300000, 1300000, 1300000, 1287500, 1287500, 1287500, 1275000, + 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500 }, + { 1400, 1275000, 1262500, 1250000, 1237500, 1225000, 1212500, 1200000, + 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500 }, + { 1300, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, + 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, + { 1200, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, + 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500 }, + { 1100, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, + 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000 }, + { 1000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, + 1012500, 1000000, 987500, 975000, 962500, 950000, 937500 }, + { 900, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, + 975000, 962500, 950000, 937500, 925000, 912500, 900000 }, + { 800, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, + 937500, 925000, 912500, 900000, 900000, 900000, 900000 }, + { 700, 987500, 975000, 962500, 950000, 937500, 925000, 912500, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 600, 950000, 937500, 925000, 912500, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500, 912500, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +}, { + /* KFC bin 2 */ + { 1300, 1250000, 1237500, 1225000, 1212500, 1200000, 1187500, 1175000, + 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500 }, + { 1200, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, + 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500 }, + { 1100, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, + 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000 }, + { 1000, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, + 1037500, 1025000, 1012500, 1000000, 987500, 975000, 962500 }, + { 900, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, + 1000000, 987500, 975000, 962500, 950000, 937500, 925000 }, + { 800, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, + 962500, 950000, 937500, 925000, 912500, 900000, 900000 }, + { 700, 1012500, 1000000, 987500, 975000, 962500, 950000, 937500, + 925000, 912500, 900000, 900000, 900000, 900000, 900000 }, + { 600, 975000, 962500, 950000, 937500, 925000, 912500, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 500, 937500, 925000, 912500, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 400, 925000, 912500, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 300, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, + { 200, 900000, 900000, 900000, 900000, 900000, 900000, 900000, + 900000, 900000, 900000, 900000, 900000, 900000, 900000 }, +} +}; + +static const struct asv_limit_entry __asv_limits[ASV_GROUPS_NUM] = { + { 13, 55 }, + { 21, 65 }, + { 25, 69 }, + { 30, 72 }, + { 36, 74 }, + { 43, 76 }, + { 51, 78 }, + { 65, 80 }, + { 81, 82 }, + { 98, 84 }, + { 119, 87 }, + { 135, 89 }, + { 150, 92 }, + { 999, 999 }, +}; + +static int exynos5422_asv_get_group(struct exynos_asv *asv) +{ + unsigned int pkgid_reg, auxi_reg; + int hpm, ids, i; + + regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PKG_ID, &pkgid_reg); + regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_AUX_INFO, &auxi_reg); + + if (asv->use_sg) { + u32 sga = (pkgid_reg >> EXYNOS5422_SG_A_OFFSET) & + EXYNOS5422_SG_A_MASK; + + u32 sgb = (pkgid_reg >> EXYNOS5422_SG_B_OFFSET) & + EXYNOS5422_SG_B_MASK; + + if ((pkgid_reg >> EXYNOS5422_SG_BSIGN_OFFSET) & + EXYNOS5422_SG_BSIGN_MASK) + return sga + sgb; + else + return sga - sgb; + } + + hpm = (auxi_reg >> EXYNOS5422_TMCB_OFFSET) & EXYNOS5422_TMCB_MASK; + ids = (pkgid_reg >> EXYNOS5422_IDS_OFFSET) & EXYNOS5422_IDS_MASK; + + for (i = 0; i < ASV_GROUPS_NUM; i++) { + if (ids <= __asv_limits[i].ids) + break; + if (hpm <= __asv_limits[i].hpm) + break; + } + if (i < ASV_GROUPS_NUM) + return i; + + return 0; +} + +static int __asv_offset_voltage(unsigned int index) +{ + switch (index) { + case 1: + return 12500; + case 2: + return 50000; + case 3: + return 25000; + default: + return 0; + }; +} + +static void exynos5422_asv_offset_voltage_setup(struct exynos_asv *asv) +{ + struct exynos_asv_subsys *subsys; + unsigned int reg, value; + + regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_AUX_INFO, ®); + + /* ARM offset voltage setup */ + subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_ARM]; + + subsys->base_volt = 1000000; + + value = (reg >> EXYNOS5422_ARM_UP_OFFSET) & EXYNOS5422_ARM_UP_MASK; + subsys->offset_volt_h = __asv_offset_voltage(value); + + value = (reg >> EXYNOS5422_ARM_DN_OFFSET) & EXYNOS5422_ARM_DN_MASK; + subsys->offset_volt_l = __asv_offset_voltage(value); + + /* KFC offset voltage setup */ + subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_KFC]; + + subsys->base_volt = 1000000; + + value = (reg >> EXYNOS5422_KFC_UP_OFFSET) & EXYNOS5422_KFC_UP_MASK; + subsys->offset_volt_h = __asv_offset_voltage(value); + + value = (reg >> EXYNOS5422_KFC_DN_OFFSET) & EXYNOS5422_KFC_DN_MASK; + subsys->offset_volt_l = __asv_offset_voltage(value); +} + +static int exynos5422_asv_opp_get_voltage(const struct exynos_asv_subsys *subsys, + int level, unsigned int volt) +{ + unsigned int asv_volt; + + if (level >= subsys->table.num_rows) + return volt; + + asv_volt = exynos_asv_opp_get_voltage(subsys, level, + subsys->asv->group); + + if (volt > subsys->base_volt) + asv_volt += subsys->offset_volt_h; + else + asv_volt += subsys->offset_volt_l; + + return asv_volt; +} + +static unsigned int exynos5422_asv_parse_table(unsigned int pkg_id) +{ + return (pkg_id >> EXYNOS5422_TABLE_OFFSET) & EXYNOS5422_TABLE_MASK; +} + +static bool exynos5422_asv_parse_bin2(unsigned int pkg_id) +{ + return (pkg_id >> EXYNOS5422_BIN2_OFFSET) & EXYNOS5422_BIN2_MASK; +} + +static bool exynos5422_asv_parse_sg(unsigned int pkg_id) +{ + return (pkg_id >> EXYNOS5422_USESG_OFFSET) & EXYNOS5422_USESG_MASK; +} + +int exynos5422_asv_init(struct exynos_asv *asv) +{ + struct exynos_asv_subsys *subsys; + unsigned int table_index; + unsigned int pkg_id; + bool bin2; + + regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PKG_ID, &pkg_id); + + if (asv->of_bin == 2) { + bin2 = true; + asv->use_sg = false; + } else { + asv->use_sg = exynos5422_asv_parse_sg(pkg_id); + bin2 = exynos5422_asv_parse_bin2(pkg_id); + } + + asv->group = exynos5422_asv_get_group(asv); + asv->table = exynos5422_asv_parse_table(pkg_id); + + exynos5422_asv_offset_voltage_setup(asv); + + if (bin2) { + table_index = 3; + } else { + if (asv->table == 2 || asv->table == 3) + table_index = asv->table - 1; + else + table_index = 0; + } + + subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_ARM]; + subsys->cpu_dt_compat = "arm,cortex-a15"; + if (bin2) + subsys->table.num_rows = ASV_ARM_BIN2_DVFS_NUM; + else + subsys->table.num_rows = ASV_ARM_DVFS_NUM; + subsys->table.num_cols = ASV_GROUPS_NUM + 1; + subsys->table.buf = (u32 *)asv_arm_table[table_index]; + + subsys = &asv->subsys[EXYNOS_ASV_SUBSYS_ID_KFC]; + subsys->cpu_dt_compat = "arm,cortex-a7"; + if (bin2) + subsys->table.num_rows = ASV_KFC_BIN2_DVFS_NUM; + else + subsys->table.num_rows = ASV_KFC_DVFS_NUM; + subsys->table.num_cols = ASV_GROUPS_NUM + 1; + subsys->table.buf = (u32 *)asv_kfc_table[table_index]; + + asv->opp_get_voltage = exynos5422_asv_opp_get_voltage; + + return 0; +} diff --git a/drivers/soc/samsung/exynos5422-asv.h b/drivers/soc/samsung/exynos5422-asv.h new file mode 100644 index 000000000000..95a5fb1a7508 --- /dev/null +++ b/drivers/soc/samsung/exynos5422-asv.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Samsung Exynos 5422 SoC Adaptive Supply Voltage support + */ + +#ifndef __LINUX_SOC_EXYNOS5422_ASV_H +#define __LINUX_SOC_EXYNOS5422_ASV_H + +#include <linux/errno.h> + +enum { + EXYNOS_ASV_SUBSYS_ID_ARM, + EXYNOS_ASV_SUBSYS_ID_KFC, + EXYNOS_ASV_SUBSYS_ID_MAX +}; + +struct exynos_asv; + +#ifdef CONFIG_EXYNOS_ASV_ARM +int exynos5422_asv_init(struct exynos_asv *asv); +#else +static inline int exynos5422_asv_init(struct exynos_asv *asv) +{ + return -ENOTSUPP; +} +#endif + +#endif /* __LINUX_SOC_EXYNOS5422_ASV_H */ -- cgit v1.2.3 From 02fb29882d5ccfad352a310cc6fb9ad9d6daad70 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki <s.nawrocki@samsung.com> Date: Mon, 28 Oct 2019 16:20:48 +0100 Subject: soc: samsung: chipid: Drop "syscon" compatible requirement As we dropped the requirement of "syscon" compatible in the chipid nodes rework code acquiring the regmap to use device_node_to_regmap() rather than syscon_node_to_regmap(). Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> --- drivers/soc/samsung/exynos-chipid.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 25562dd0b206..b89c26a71c6e 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -50,12 +50,20 @@ static int __init exynos_chipid_early_init(void) struct soc_device_attribute *soc_dev_attr; struct soc_device *soc_dev; struct device_node *root; + struct device_node *syscon; struct regmap *regmap; u32 product_id; u32 revision; int ret; - regmap = syscon_regmap_lookup_by_compatible("samsung,exynos4210-chipid"); + syscon = of_find_compatible_node(NULL, NULL, + "samsung,exynos4210-chipid"); + if (!syscon) + return ENODEV; + + regmap = device_node_to_regmap(syscon); + of_node_put(syscon); + if (IS_ERR(regmap)) return PTR_ERR(regmap); -- cgit v1.2.3 From aba19827fced3f32bd17885db59d27538b0bd223 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni <skomatineni@nvidia.com> Date: Fri, 16 Aug 2019 12:42:01 -0700 Subject: soc/tegra: pmc: Support wake events on more Tegra SoCs This patch allows to create separate irq_set_wake and irq_set_type implementations for different Tegra designs PMC that has different wake models which require difference wake registers and different programming sequence. AOWAKE model support is available for Tegra186 and Tegra194 only and it resides within PMC and supports tiered wake architecture. Tegra210 and prior Tegra designs uses PMC directly to receive wake events and coordinate the wake sequence. Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/pmc.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 0447afa970f5..63e26bbaf576 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -226,6 +226,8 @@ struct tegra_pmc_soc { void (*setup_irq_polarity)(struct tegra_pmc *pmc, struct device_node *np, bool invert); + int (*irq_set_wake)(struct irq_data *data, unsigned int on); + int (*irq_set_type)(struct irq_data *data, unsigned int type); const char * const *reset_sources; unsigned int num_reset_sources; @@ -1946,7 +1948,7 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = { .alloc = tegra_pmc_irq_alloc, }; -static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on) +static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on) { struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); unsigned int offset, bit; @@ -1978,7 +1980,7 @@ static int tegra_pmc_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } -static int tegra_pmc_irq_set_type(struct irq_data *data, unsigned int type) +static int tegra186_pmc_irq_set_type(struct irq_data *data, unsigned int type) { struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); u32 value; @@ -2032,8 +2034,8 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc) pmc->irq.irq_unmask = irq_chip_unmask_parent; pmc->irq.irq_eoi = irq_chip_eoi_parent; pmc->irq.irq_set_affinity = irq_chip_set_affinity_parent; - pmc->irq.irq_set_type = tegra_pmc_irq_set_type; - pmc->irq.irq_set_wake = tegra_pmc_irq_set_wake; + pmc->irq.irq_set_type = pmc->soc->irq_set_type; + pmc->irq.irq_set_wake = pmc->soc->irq_set_wake; pmc->domain = irq_domain_add_hierarchy(parent, 0, 96, pmc->dev->of_node, &tegra_pmc_irq_domain_ops, pmc); @@ -2706,6 +2708,8 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .regs = &tegra186_pmc_regs, .init = NULL, .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, + .irq_set_wake = tegra186_pmc_irq_set_wake, + .irq_set_type = tegra186_pmc_irq_set_type, .reset_sources = tegra186_reset_sources, .num_reset_sources = ARRAY_SIZE(tegra186_reset_sources), .reset_levels = tegra186_reset_levels, -- cgit v1.2.3 From 7e9ae849eb1ea4617a9c7229a78c622a214283f2 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni <skomatineni@nvidia.com> Date: Fri, 16 Aug 2019 12:42:02 -0700 Subject: soc/tegra: pmc: Add wake event support on Tegra210 This patch implements PMC wakeup sequence for Tegra210 and defines the commonly used RTC alarm wake event. Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/pmc.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 63e26bbaf576..e1f14098bd2d 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -58,6 +58,11 @@ #define PMC_CNTRL_SYSCLK_POLARITY BIT(10) /* sys clk polarity */ #define PMC_CNTRL_MAIN_RST BIT(4) +#define PMC_WAKE_MASK 0x0c +#define PMC_WAKE_LEVEL 0x10 +#define PMC_WAKE_STATUS 0x14 +#define PMC_SW_WAKE_STATUS 0x18 + #define DPD_SAMPLE 0x020 #define DPD_SAMPLE_ENABLE BIT(0) #define DPD_SAMPLE_DISABLE (0 << 0) @@ -87,6 +92,11 @@ #define PMC_SCRATCH41 0x140 +#define PMC_WAKE2_MASK 0x160 +#define PMC_WAKE2_LEVEL 0x164 +#define PMC_WAKE2_STATUS 0x168 +#define PMC_SW_WAKE2_STATUS 0x16c + #define PMC_SENSOR_CTRL 0x1b0 #define PMC_SENSOR_CTRL_SCRATCH_WRITE BIT(2) #define PMC_SENSOR_CTRL_ENABLE_RST BIT(1) @@ -1948,6 +1958,86 @@ static const struct irq_domain_ops tegra_pmc_irq_domain_ops = { .alloc = tegra_pmc_irq_alloc, }; +static int tegra210_pmc_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); + unsigned int offset, bit; + u32 value; + + if (data->hwirq == ULONG_MAX) + return 0; + + offset = data->hwirq / 32; + bit = data->hwirq % 32; + + /* clear wake status */ + tegra_pmc_writel(pmc, 0, PMC_SW_WAKE_STATUS); + tegra_pmc_writel(pmc, 0, PMC_SW_WAKE2_STATUS); + + tegra_pmc_writel(pmc, 0, PMC_WAKE_STATUS); + tegra_pmc_writel(pmc, 0, PMC_WAKE2_STATUS); + + /* enable PMC wake */ + if (data->hwirq >= 32) + offset = PMC_WAKE2_MASK; + else + offset = PMC_WAKE_MASK; + + value = tegra_pmc_readl(pmc, offset); + + if (on) + value |= BIT(bit); + else + value &= ~BIT(bit); + + tegra_pmc_writel(pmc, value, offset); + + return 0; +} + +static int tegra210_pmc_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); + unsigned int offset, bit; + u32 value; + + if (data->hwirq == ULONG_MAX) + return 0; + + offset = data->hwirq / 32; + bit = data->hwirq % 32; + + if (data->hwirq >= 32) + offset = PMC_WAKE2_LEVEL; + else + offset = PMC_WAKE_LEVEL; + + value = tegra_pmc_readl(pmc, offset); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_LEVEL_HIGH: + value |= BIT(bit); + break; + + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_LEVEL_LOW: + value &= ~BIT(bit); + break; + + case IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING: + value ^= BIT(bit); + break; + + default: + return -EINVAL; + } + + tegra_pmc_writel(pmc, value, offset); + + return 0; +} + static int tegra186_pmc_irq_set_wake(struct irq_data *data, unsigned int on) { struct tegra_pmc *pmc = irq_data_get_irq_chip_data(data); @@ -2566,6 +2656,10 @@ static const struct pinctrl_pin_desc tegra210_pin_descs[] = { TEGRA210_IO_PAD_TABLE(TEGRA_IO_PIN_DESC) }; +static const struct tegra_wake_event tegra210_wake_events[] = { + TEGRA_WAKE_IRQ("rtc", 16, 2), +}; + static const struct tegra_pmc_soc tegra210_pmc_soc = { .num_powergates = ARRAY_SIZE(tegra210_powergates), .powergates = tegra210_powergates, @@ -2583,10 +2677,14 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, + .irq_set_wake = tegra210_pmc_irq_set_wake, + .irq_set_type = tegra210_pmc_irq_set_type, .reset_sources = tegra210_reset_sources, .num_reset_sources = ARRAY_SIZE(tegra210_reset_sources), .reset_levels = NULL, .num_reset_levels = 0, + .num_wake_events = ARRAY_SIZE(tegra210_wake_events), + .wake_events = tegra210_wake_events, }; #define TEGRA186_IO_PAD_TABLE(_pad) \ -- cgit v1.2.3 From 455271d9dc5f4cce3d35c5819f8f01c723bca94c Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni <skomatineni@nvidia.com> Date: Fri, 16 Aug 2019 12:42:04 -0700 Subject: soc/tegra: pmc: Configure core power request polarity This patch configures polarity of the core power request signal in PMC control register based on the device tree property. PMC asserts and de-asserts power request signal based on it polarity when it need to power-up and power-down the core rail during SC7. Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/pmc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index e1f14098bd2d..41e974cd91fe 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -56,6 +56,7 @@ #define PMC_CNTRL_SIDE_EFFECT_LP0 BIT(14) /* LP0 when CPU pwr gated */ #define PMC_CNTRL_SYSCLK_OE BIT(11) /* system clock enable */ #define PMC_CNTRL_SYSCLK_POLARITY BIT(10) /* sys clk polarity */ +#define PMC_CNTRL_PWRREQ_POLARITY BIT(8) #define PMC_CNTRL_MAIN_RST BIT(4) #define PMC_WAKE_MASK 0x0c @@ -2316,6 +2317,11 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc) else value |= PMC_CNTRL_SYSCLK_POLARITY; + if (pmc->corereq_high) + value &= ~PMC_CNTRL_PWRREQ_POLARITY; + else + value |= PMC_CNTRL_PWRREQ_POLARITY; + /* configure the output polarity while the request is tristated */ tegra_pmc_writel(pmc, value, PMC_CNTRL); -- cgit v1.2.3 From c7ccfccabb0f819eae1a191ccd94269f577e4523 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni <skomatineni@nvidia.com> Date: Fri, 16 Aug 2019 12:42:05 -0700 Subject: soc/tegra: pmc: Configure deep sleep control settings Tegra210 and prior Tegra chips have deep sleep entry and wakeup related timings which are platform specific that should be configured before entering into deep sleep. Below are the timing specific configurations for deep sleep entry and wakeup. - Core rail power-on stabilization timer - OSC clock stabilization timer after SOC rail power is stabilized. - Core power off time is the minimum wake delay to keep the system in deep sleep state irrespective of any quick wake event. These values depends on the discharge time of regulators and turn OFF time of the PMIC to allow the complete system to finish entering into deep sleep state. These values vary based on the platform design and are specified through the device tree. This patch has implementation to configure these timings which are must to have for proper deep sleep and wakeup operations. Signed-off-by: Sowjanya Komatineni <skomatineni@nvidia.com> Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/pmc.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 41e974cd91fe..c9b1baae5d8a 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -88,6 +88,8 @@ #define PMC_CPUPWRGOOD_TIMER 0xc8 #define PMC_CPUPWROFF_TIMER 0xcc +#define PMC_COREPWRGOOD_TIMER 0x3c +#define PMC_COREPWROFF_TIMER 0xe0 #define PMC_PWR_DET_VALUE 0xe4 @@ -2303,7 +2305,7 @@ static const struct tegra_pmc_regs tegra20_pmc_regs = { static void tegra20_pmc_init(struct tegra_pmc *pmc) { - u32 value; + u32 value, osc, pmu, off; /* Always enable CPU power request */ value = tegra_pmc_readl(pmc, PMC_CNTRL); @@ -2329,6 +2331,16 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc) value = tegra_pmc_readl(pmc, PMC_CNTRL); value |= PMC_CNTRL_SYSCLK_OE; tegra_pmc_writel(pmc, value, PMC_CNTRL); + + /* program core timings which are applicable only for suspend state */ + if (pmc->suspend_mode != TEGRA_SUSPEND_NONE) { + osc = DIV_ROUND_UP(pmc->core_osc_time * 8192, 1000000); + pmu = DIV_ROUND_UP(pmc->core_pmu_time * 32768, 1000000); + off = DIV_ROUND_UP(pmc->core_off_time * 32768, 1000000); + tegra_pmc_writel(pmc, ((osc << 8) & 0xff00) | (pmu & 0xff), + PMC_COREPWRGOOD_TIMER); + tegra_pmc_writel(pmc, off, PMC_COREPWROFF_TIMER); + } } static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc, -- cgit v1.2.3 From 496747e7d907b01bd2507d61bdd6874b987c9629 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko <digetx@gmail.com> Date: Thu, 25 Jul 2019 18:18:31 +0300 Subject: soc/tegra: regulators: Add regulators coupler for Tegra20 Add regulators coupler for Tegra20 SoCs that performs voltage balancing of a coupled regulators and thus provides voltage scaling functionality. There are 3 coupled regulators on all Tegra20 SoCs: CORE, RTC and CPU. The CORE and RTC voltages shall be in range of 170mV from each other and they both shall be higher than the CPU voltage by at least 120mV. This sounds like it could be handle by a generic voltage balancer, but the CORE voltage scaling isn't implemented in any of the upstream drivers yet. It will take quite some time and effort to hook up voltage scaling for all of the drivers, hence we will use a custom coupler that will manage the CPU voltage scaling for the starter. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Acked-By: Peter De Schrijver <pdeschrijver@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/Kconfig | 5 + drivers/soc/tegra/Makefile | 1 + drivers/soc/tegra/regulators-tegra20.c | 365 +++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 drivers/soc/tegra/regulators-tegra20.c (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index c8ef05d6b8c7..437466aa57e1 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -15,6 +15,7 @@ config ARCH_TEGRA_2x_SOC select PL310_ERRATA_769419 if CACHE_L2X0 select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC + select SOC_TEGRA20_VOLTAGE_COUPLER select TEGRA_TIMER help Support for NVIDIA Tegra AP20 and T20 processors, based on the @@ -135,3 +136,7 @@ config SOC_TEGRA_POWERGATE_BPMP def_bool y depends on PM_GENERIC_DOMAINS depends on TEGRA_BPMP + +config SOC_TEGRA20_VOLTAGE_COUPLER + bool "Voltage scaling support for Tegra20 SoCs" + depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index 902759fe5f4d..9f0bdd53bef8 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -5,3 +5,4 @@ obj-y += common.o obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o +obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o diff --git a/drivers/soc/tegra/regulators-tegra20.c b/drivers/soc/tegra/regulators-tegra20.c new file mode 100644 index 000000000000..ea0eede48802 --- /dev/null +++ b/drivers/soc/tegra/regulators-tegra20.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Voltage regulators coupler for NVIDIA Tegra20 + * Copyright (C) 2019 GRATE-DRIVER project + * + * Voltage constraints borrowed from downstream kernel sources + * Copyright (C) 2010-2011 NVIDIA Corporation + */ + +#define pr_fmt(fmt) "tegra voltage-coupler: " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/regulator/coupler.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +struct tegra_regulator_coupler { + struct regulator_coupler coupler; + struct regulator_dev *core_rdev; + struct regulator_dev *cpu_rdev; + struct regulator_dev *rtc_rdev; + int core_min_uV; +}; + +static inline struct tegra_regulator_coupler * +to_tegra_coupler(struct regulator_coupler *coupler) +{ + return container_of(coupler, struct tegra_regulator_coupler, coupler); +} + +static int tegra20_core_limit(struct tegra_regulator_coupler *tegra, + struct regulator_dev *core_rdev) +{ + int core_min_uV = 0; + int core_max_uV; + int core_cur_uV; + int err; + + if (tegra->core_min_uV > 0) + return tegra->core_min_uV; + + core_cur_uV = regulator_get_voltage_rdev(core_rdev); + if (core_cur_uV < 0) + return core_cur_uV; + + core_max_uV = max(core_cur_uV, 1200000); + + err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); + if (err) + return err; + + /* + * Limit minimum CORE voltage to a value left from bootloader or, + * if it's unreasonably low value, to the most common 1.2v or to + * whatever maximum value defined via board's device-tree. + */ + tegra->core_min_uV = core_max_uV; + + pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV); + + return tegra->core_min_uV; +} + +static int tegra20_core_rtc_max_spread(struct regulator_dev *core_rdev, + struct regulator_dev *rtc_rdev) +{ + struct coupling_desc *c_desc = &core_rdev->coupling_desc; + struct regulator_dev *rdev; + int max_spread; + unsigned int i; + + for (i = 1; i < c_desc->n_coupled; i++) { + max_spread = core_rdev->constraints->max_spread[i - 1]; + rdev = c_desc->coupled_rdevs[i]; + + if (rdev == rtc_rdev && max_spread) + return max_spread; + } + + pr_err_once("rtc-core max-spread is undefined in device-tree\n"); + + return 150000; +} + +static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra, + struct regulator_dev *core_rdev, + struct regulator_dev *rtc_rdev, + int cpu_uV, int cpu_min_uV) +{ + int core_min_uV, core_max_uV = INT_MAX; + int rtc_min_uV, rtc_max_uV = INT_MAX; + int core_target_uV; + int rtc_target_uV; + int max_spread; + int core_uV; + int rtc_uV; + int err; + + /* + * RTC and CORE voltages should be no more than 170mV from each other, + * CPU should be below RTC and CORE by at least 120mV. This applies + * to all Tegra20 SoC's. + */ + max_spread = tegra20_core_rtc_max_spread(core_rdev, rtc_rdev); + + /* + * The core voltage scaling is currently not hooked up in drivers, + * hence we will limit the minimum core voltage to a reasonable value. + * This should be good enough for the time being. + */ + core_min_uV = tegra20_core_limit(tegra, core_rdev); + if (core_min_uV < 0) + return core_min_uV; + + err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); + if (err) + return err; + + err = regulator_check_consumers(core_rdev, &core_min_uV, &core_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + core_uV = regulator_get_voltage_rdev(core_rdev); + if (core_uV < 0) + return core_uV; + + core_min_uV = max(cpu_min_uV + 125000, core_min_uV); + if (core_min_uV > core_max_uV) + return -EINVAL; + + if (cpu_uV + 120000 > core_uV) + pr_err("core-cpu voltage constraint violated: %d %d\n", + core_uV, cpu_uV + 120000); + + rtc_uV = regulator_get_voltage_rdev(rtc_rdev); + if (rtc_uV < 0) + return rtc_uV; + + if (cpu_uV + 120000 > rtc_uV) + pr_err("rtc-cpu voltage constraint violated: %d %d\n", + rtc_uV, cpu_uV + 120000); + + if (abs(core_uV - rtc_uV) > 170000) + pr_err("core-rtc voltage constraint violated: %d %d\n", + core_uV, rtc_uV); + + rtc_min_uV = max(cpu_min_uV + 125000, core_min_uV - max_spread); + + err = regulator_check_voltage(rtc_rdev, &rtc_min_uV, &rtc_max_uV); + if (err) + return err; + + while (core_uV != core_min_uV || rtc_uV != rtc_min_uV) { + if (core_uV < core_min_uV) { + core_target_uV = min(core_uV + max_spread, core_min_uV); + core_target_uV = min(rtc_uV + max_spread, core_target_uV); + } else { + core_target_uV = max(core_uV - max_spread, core_min_uV); + core_target_uV = max(rtc_uV - max_spread, core_target_uV); + } + + err = regulator_set_voltage_rdev(core_rdev, + core_target_uV, + core_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + core_uV = core_target_uV; + + if (rtc_uV < rtc_min_uV) { + rtc_target_uV = min(rtc_uV + max_spread, rtc_min_uV); + rtc_target_uV = min(core_uV + max_spread, rtc_target_uV); + } else { + rtc_target_uV = max(rtc_uV - max_spread, rtc_min_uV); + rtc_target_uV = max(core_uV - max_spread, rtc_target_uV); + } + + err = regulator_set_voltage_rdev(rtc_rdev, + rtc_target_uV, + rtc_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + rtc_uV = rtc_target_uV; + } + + return 0; +} + +static int tegra20_core_voltage_update(struct tegra_regulator_coupler *tegra, + struct regulator_dev *cpu_rdev, + struct regulator_dev *core_rdev, + struct regulator_dev *rtc_rdev) +{ + int cpu_uV; + + cpu_uV = regulator_get_voltage_rdev(cpu_rdev); + if (cpu_uV < 0) + return cpu_uV; + + return tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, + cpu_uV, cpu_uV); +} + +static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra, + struct regulator_dev *cpu_rdev, + struct regulator_dev *core_rdev, + struct regulator_dev *rtc_rdev) +{ + int cpu_min_uV_consumers = 0; + int cpu_max_uV = INT_MAX; + int cpu_min_uV = 0; + int cpu_uV; + int err; + + err = regulator_check_voltage(cpu_rdev, &cpu_min_uV, &cpu_max_uV); + if (err) + return err; + + err = regulator_check_consumers(cpu_rdev, &cpu_min_uV, &cpu_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + err = regulator_check_consumers(cpu_rdev, &cpu_min_uV_consumers, + &cpu_max_uV, PM_SUSPEND_ON); + if (err) + return err; + + cpu_uV = regulator_get_voltage_rdev(cpu_rdev); + if (cpu_uV < 0) + return cpu_uV; + + /* + * CPU's regulator may not have any consumers, hence the voltage + * must not be changed in that case because CPU simply won't + * survive the voltage drop if it's running on a higher frequency. + */ + if (!cpu_min_uV_consumers) + cpu_min_uV = cpu_uV; + + if (cpu_min_uV > cpu_uV) { + err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, + cpu_uV, cpu_min_uV); + if (err) + return err; + + err = regulator_set_voltage_rdev(cpu_rdev, cpu_min_uV, + cpu_max_uV, PM_SUSPEND_ON); + if (err) + return err; + } else if (cpu_min_uV < cpu_uV) { + err = regulator_set_voltage_rdev(cpu_rdev, cpu_min_uV, + cpu_max_uV, PM_SUSPEND_ON); + if (err) + return err; + + err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, + cpu_uV, cpu_min_uV); + if (err) + return err; + } + + return 0; +} + +static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler, + struct regulator_dev *rdev, + suspend_state_t state) +{ + struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + struct regulator_dev *core_rdev = tegra->core_rdev; + struct regulator_dev *cpu_rdev = tegra->cpu_rdev; + struct regulator_dev *rtc_rdev = tegra->rtc_rdev; + + if ((core_rdev != rdev && cpu_rdev != rdev && rtc_rdev != rdev) || + state != PM_SUSPEND_ON) { + pr_err("regulators are not coupled properly\n"); + return -EINVAL; + } + + if (rdev == cpu_rdev) + return tegra20_cpu_voltage_update(tegra, cpu_rdev, + core_rdev, rtc_rdev); + + if (rdev == core_rdev) + return tegra20_core_voltage_update(tegra, cpu_rdev, + core_rdev, rtc_rdev); + + pr_err("changing %s voltage not permitted\n", rdev_get_name(rtc_rdev)); + + return -EPERM; +} + +static int tegra20_regulator_attach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + struct device_node *np = rdev->dev.of_node; + + if (of_property_read_bool(np, "nvidia,tegra-core-regulator") && + !tegra->core_rdev) { + tegra->core_rdev = rdev; + return 0; + } + + if (of_property_read_bool(np, "nvidia,tegra-rtc-regulator") && + !tegra->rtc_rdev) { + tegra->rtc_rdev = rdev; + return 0; + } + + if (of_property_read_bool(np, "nvidia,tegra-cpu-regulator") && + !tegra->cpu_rdev) { + tegra->cpu_rdev = rdev; + return 0; + } + + return -EINVAL; +} + +static int tegra20_regulator_detach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + + if (tegra->core_rdev == rdev) { + tegra->core_rdev = NULL; + return 0; + } + + if (tegra->rtc_rdev == rdev) { + tegra->rtc_rdev = NULL; + return 0; + } + + if (tegra->cpu_rdev == rdev) { + tegra->cpu_rdev = NULL; + return 0; + } + + return -EINVAL; +} + +static struct tegra_regulator_coupler tegra20_coupler = { + .coupler = { + .attach_regulator = tegra20_regulator_attach, + .detach_regulator = tegra20_regulator_detach, + .balance_voltage = tegra20_regulator_balance_voltage, + }, +}; + +static int __init tegra_regulator_coupler_init(void) +{ + if (!of_machine_is_compatible("nvidia,tegra20")) + return 0; + + return regulator_coupler_register(&tegra20_coupler.coupler); +} +arch_initcall(tegra_regulator_coupler_init); -- cgit v1.2.3 From 783807436f363e5b1ad4d43ba7debbedfcadbb99 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko <digetx@gmail.com> Date: Thu, 25 Jul 2019 18:18:32 +0300 Subject: soc/tegra: regulators: Add regulators coupler for Tegra30 Add regulators coupler for Tegra30 SoCs that performs voltage balancing of a coupled regulators and thus provides voltage scaling functionality. There are 2 coupled regulators on all Tegra30 SoCs: CORE and CPU. The coupled regulator voltages shall be in a range of 300mV from each other and CORE voltage shall be higher than the CPU by N mV, where N depends on the CPU voltage. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Acked-By: Peter De Schrijver <pdeschrijver@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/Kconfig | 5 + drivers/soc/tegra/Makefile | 1 + drivers/soc/tegra/regulators-tegra30.c | 317 +++++++++++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 drivers/soc/tegra/regulators-tegra30.c (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 437466aa57e1..84bd615c4a92 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -29,6 +29,7 @@ config ARCH_TEGRA_3x_SOC select PL310_ERRATA_769419 if CACHE_L2X0 select SOC_TEGRA_FLOWCTRL select SOC_TEGRA_PMC + select SOC_TEGRA30_VOLTAGE_COUPLER select TEGRA_TIMER help Support for NVIDIA Tegra T30 processor family, based on the @@ -140,3 +141,7 @@ config SOC_TEGRA_POWERGATE_BPMP config SOC_TEGRA20_VOLTAGE_COUPLER bool "Voltage scaling support for Tegra20 SoCs" depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST + +config SOC_TEGRA30_VOLTAGE_COUPLER + bool "Voltage scaling support for Tegra30 SoCs" + depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST diff --git a/drivers/soc/tegra/Makefile b/drivers/soc/tegra/Makefile index 9f0bdd53bef8..9c809c1814bd 100644 --- a/drivers/soc/tegra/Makefile +++ b/drivers/soc/tegra/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_SOC_TEGRA_FLOWCTRL) += flowctrl.o obj-$(CONFIG_SOC_TEGRA_PMC) += pmc.o obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o obj-$(CONFIG_SOC_TEGRA20_VOLTAGE_COUPLER) += regulators-tegra20.o +obj-$(CONFIG_SOC_TEGRA30_VOLTAGE_COUPLER) += regulators-tegra30.o diff --git a/drivers/soc/tegra/regulators-tegra30.c b/drivers/soc/tegra/regulators-tegra30.c new file mode 100644 index 000000000000..8e623ff18e70 --- /dev/null +++ b/drivers/soc/tegra/regulators-tegra30.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Voltage regulators coupler for NVIDIA Tegra30 + * Copyright (C) 2019 GRATE-DRIVER project + * + * Voltage constraints borrowed from downstream kernel sources + * Copyright (C) 2010-2011 NVIDIA Corporation + */ + +#define pr_fmt(fmt) "tegra voltage-coupler: " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/regulator/coupler.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +#include <soc/tegra/fuse.h> + +struct tegra_regulator_coupler { + struct regulator_coupler coupler; + struct regulator_dev *core_rdev; + struct regulator_dev *cpu_rdev; + int core_min_uV; +}; + +static inline struct tegra_regulator_coupler * +to_tegra_coupler(struct regulator_coupler *coupler) +{ + return container_of(coupler, struct tegra_regulator_coupler, coupler); +} + +static int tegra30_core_limit(struct tegra_regulator_coupler *tegra, + struct regulator_dev *core_rdev) +{ + int core_min_uV = 0; + int core_max_uV; + int core_cur_uV; + int err; + + if (tegra->core_min_uV > 0) + return tegra->core_min_uV; + + core_cur_uV = regulator_get_voltage_rdev(core_rdev); + if (core_cur_uV < 0) + return core_cur_uV; + + core_max_uV = max(core_cur_uV, 1200000); + + err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); + if (err) + return err; + + /* + * Limit minimum CORE voltage to a value left from bootloader or, + * if it's unreasonably low value, to the most common 1.2v or to + * whatever maximum value defined via board's device-tree. + */ + tegra->core_min_uV = core_max_uV; + + pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV); + + return tegra->core_min_uV; +} + +static int tegra30_core_cpu_limit(int cpu_uV) +{ + if (cpu_uV < 800000) + return 950000; + + if (cpu_uV < 900000) + return 1000000; + + if (cpu_uV < 1000000) + return 1100000; + + if (cpu_uV < 1100000) + return 1200000; + + if (cpu_uV < 1250000) { + switch (tegra_sku_info.cpu_speedo_id) { + case 0 ... 1: + case 4: + case 7 ... 8: + return 1200000; + + default: + return 1300000; + } + } + + return -EINVAL; +} + +static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, + struct regulator_dev *cpu_rdev, + struct regulator_dev *core_rdev) +{ + int core_min_uV, core_max_uV = INT_MAX; + int cpu_min_uV, cpu_max_uV = INT_MAX; + int cpu_min_uV_consumers = 0; + int core_min_limited_uV; + int core_target_uV; + int cpu_target_uV; + int core_max_step; + int cpu_max_step; + int max_spread; + int core_uV; + int cpu_uV; + int err; + + /* + * CPU voltage should not got lower than 300mV from the CORE. + * CPU voltage should stay below the CORE by 100mV+, depending + * by the CORE voltage. This applies to all Tegra30 SoC's. + */ + max_spread = cpu_rdev->constraints->max_spread[0]; + cpu_max_step = cpu_rdev->constraints->max_uV_step; + core_max_step = core_rdev->constraints->max_uV_step; + + if (!max_spread) { + pr_err_once("cpu-core max-spread is undefined in device-tree\n"); + max_spread = 300000; + } + + if (!cpu_max_step) { + pr_err_once("cpu max-step is undefined in device-tree\n"); + cpu_max_step = 150000; + } + + if (!core_max_step) { + pr_err_once("core max-step is undefined in device-tree\n"); + core_max_step = 150000; + } + + /* + * The CORE voltage scaling is currently not hooked up in drivers, + * hence we will limit the minimum CORE voltage to a reasonable value. + * This should be good enough for the time being. + */ + core_min_uV = tegra30_core_limit(tegra, core_rdev); + if (core_min_uV < 0) + return core_min_uV; + + err = regulator_check_consumers(core_rdev, &core_min_uV, &core_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + core_uV = regulator_get_voltage_rdev(core_rdev); + if (core_uV < 0) + return core_uV; + + cpu_min_uV = core_min_uV - max_spread; + + err = regulator_check_consumers(cpu_rdev, &cpu_min_uV, &cpu_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + err = regulator_check_consumers(cpu_rdev, &cpu_min_uV_consumers, + &cpu_max_uV, PM_SUSPEND_ON); + if (err) + return err; + + err = regulator_check_voltage(cpu_rdev, &cpu_min_uV, &cpu_max_uV); + if (err) + return err; + + cpu_uV = regulator_get_voltage_rdev(cpu_rdev); + if (cpu_uV < 0) + return cpu_uV; + + /* + * CPU's regulator may not have any consumers, hence the voltage + * must not be changed in that case because CPU simply won't + * survive the voltage drop if it's running on a higher frequency. + */ + if (!cpu_min_uV_consumers) + cpu_min_uV = cpu_uV; + + /* + * Bootloader shall set up voltages correctly, but if it + * happens that there is a violation, then try to fix it + * at first. + */ + core_min_limited_uV = tegra30_core_cpu_limit(cpu_uV); + if (core_min_limited_uV < 0) + return core_min_limited_uV; + + core_min_uV = max(core_min_uV, tegra30_core_cpu_limit(cpu_min_uV)); + + err = regulator_check_voltage(core_rdev, &core_min_uV, &core_max_uV); + if (err) + return err; + + if (core_min_limited_uV > core_uV) { + pr_err("core voltage constraint violated: %d %d %d\n", + core_uV, core_min_limited_uV, cpu_uV); + goto update_core; + } + + while (cpu_uV != cpu_min_uV || core_uV != core_min_uV) { + if (cpu_uV < cpu_min_uV) { + cpu_target_uV = min(cpu_uV + cpu_max_step, cpu_min_uV); + } else { + cpu_target_uV = max(cpu_uV - cpu_max_step, cpu_min_uV); + cpu_target_uV = max(core_uV - max_spread, cpu_target_uV); + } + + err = regulator_set_voltage_rdev(cpu_rdev, + cpu_target_uV, + cpu_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + cpu_uV = cpu_target_uV; +update_core: + core_min_limited_uV = tegra30_core_cpu_limit(cpu_uV); + if (core_min_limited_uV < 0) + return core_min_limited_uV; + + core_target_uV = max(core_min_limited_uV, core_min_uV); + + if (core_uV < core_target_uV) { + core_target_uV = min(core_target_uV, core_uV + core_max_step); + core_target_uV = min(core_target_uV, cpu_uV + max_spread); + } else { + core_target_uV = max(core_target_uV, core_uV - core_max_step); + } + + err = regulator_set_voltage_rdev(core_rdev, + core_target_uV, + core_max_uV, + PM_SUSPEND_ON); + if (err) + return err; + + core_uV = core_target_uV; + } + + return 0; +} + +static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler, + struct regulator_dev *rdev, + suspend_state_t state) +{ + struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + struct regulator_dev *core_rdev = tegra->core_rdev; + struct regulator_dev *cpu_rdev = tegra->cpu_rdev; + + if ((core_rdev != rdev && cpu_rdev != rdev) || state != PM_SUSPEND_ON) { + pr_err("regulators are not coupled properly\n"); + return -EINVAL; + } + + return tegra30_voltage_update(tegra, cpu_rdev, core_rdev); +} + +static int tegra30_regulator_attach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + struct device_node *np = rdev->dev.of_node; + + if (of_property_read_bool(np, "nvidia,tegra-core-regulator") && + !tegra->core_rdev) { + tegra->core_rdev = rdev; + return 0; + } + + if (of_property_read_bool(np, "nvidia,tegra-cpu-regulator") && + !tegra->cpu_rdev) { + tegra->cpu_rdev = rdev; + return 0; + } + + return -EINVAL; +} + +static int tegra30_regulator_detach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + + if (tegra->core_rdev == rdev) { + tegra->core_rdev = NULL; + return 0; + } + + if (tegra->cpu_rdev == rdev) { + tegra->cpu_rdev = NULL; + return 0; + } + + return -EINVAL; +} + +static struct tegra_regulator_coupler tegra30_coupler = { + .coupler = { + .attach_regulator = tegra30_regulator_attach, + .detach_regulator = tegra30_regulator_detach, + .balance_voltage = tegra30_regulator_balance_voltage, + }, +}; + +static int __init tegra_regulator_coupler_init(void) +{ + if (!of_machine_is_compatible("nvidia,tegra30")) + return 0; + + return regulator_coupler_register(&tegra30_coupler.coupler); +} +arch_initcall(tegra_regulator_coupler_init); -- cgit v1.2.3 From e57a243f5d896f02510e4be92df0873b4c1aab7f Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko <digetx@gmail.com> Date: Thu, 26 Sep 2019 22:17:54 +0300 Subject: soc/tegra: pmc: Query PCLK clock rate at probe time It is possible to get a lockup if kernel decides to enter LP2 cpuidle from some clk-notifier, in that case CCF's "prepare" mutex is kept locked and thus clk_get_rate(pclk) blocks on the same mutex with interrupts being disabled, hanging machine. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Acked-By: Peter De Schrijver <pdeschrijver@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/pmc.c | 74 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 15 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index c9b1baae5d8a..1f78e8497fde 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -324,6 +324,7 @@ static const char * const tegra210_reset_sources[] = { * @pctl_dev: pin controller exposed by the PMC * @domain: IRQ domain provided by the PMC * @irq: chip implementation for the IRQ domain + * @clk_nb: pclk clock changes handler */ struct tegra_pmc { struct device *dev; @@ -359,6 +360,8 @@ struct tegra_pmc { struct irq_domain *domain; struct irq_chip irq; + + struct notifier_block clk_nb; }; static struct tegra_pmc *pmc = &(struct tegra_pmc) { @@ -1207,7 +1210,7 @@ static int tegra_io_pad_prepare(struct tegra_pmc *pmc, enum tegra_io_pad id, return err; if (pmc->clk) { - rate = clk_get_rate(pmc->clk); + rate = pmc->rate; if (!rate) { dev_err(pmc->dev, "failed to get clock rate\n"); return -ENODEV; @@ -1448,6 +1451,7 @@ void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) { unsigned long long rate = 0; + u64 ticks; u32 value; switch (mode) { @@ -1456,7 +1460,7 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) break; case TEGRA_SUSPEND_LP2: - rate = clk_get_rate(pmc->clk); + rate = pmc->rate; break; default: @@ -1466,21 +1470,15 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) if (WARN_ON_ONCE(rate == 0)) rate = 100000000; - if (rate != pmc->rate) { - u64 ticks; - - ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1; - do_div(ticks, USEC_PER_SEC); - tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER); + ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1; + do_div(ticks, USEC_PER_SEC); + tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER); - ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1; - do_div(ticks, USEC_PER_SEC); - tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER); + ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1; + do_div(ticks, USEC_PER_SEC); + tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER); - wmb(); - - pmc->rate = rate; - } + wmb(); value = tegra_pmc_readl(pmc, PMC_CNTRL); value &= ~PMC_CNTRL_SIDE_EFFECT_LP0; @@ -2140,6 +2138,33 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc) return 0; } +static int tegra_pmc_clk_notify_cb(struct notifier_block *nb, + unsigned long action, void *ptr) +{ + struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, clk_nb); + struct clk_notifier_data *data = ptr; + + switch (action) { + case PRE_RATE_CHANGE: + mutex_lock(&pmc->powergates_lock); + break; + + case POST_RATE_CHANGE: + pmc->rate = data->new_rate; + /* fall through */ + + case ABORT_RATE_CHANGE: + mutex_unlock(&pmc->powergates_lock); + break; + + default: + WARN_ON_ONCE(1); + return notifier_from_errno(-EINVAL); + } + + return NOTIFY_OK; +} + static int tegra_pmc_probe(struct platform_device *pdev) { void __iomem *base; @@ -2203,6 +2228,23 @@ static int tegra_pmc_probe(struct platform_device *pdev) pmc->clk = NULL; } + /* + * PCLK clock rate can't be retrieved using CLK API because it + * causes lockup if CPU enters LP2 idle state from some other + * CLK notifier, hence we're caching the rate's value locally. + */ + if (pmc->clk) { + pmc->clk_nb.notifier_call = tegra_pmc_clk_notify_cb; + err = clk_notifier_register(pmc->clk, &pmc->clk_nb); + if (err) { + dev_err(&pdev->dev, + "failed to register clk notifier\n"); + return err; + } + + pmc->rate = clk_get_rate(pmc->clk); + } + pmc->dev = &pdev->dev; tegra_pmc_init(pmc); @@ -2254,6 +2296,8 @@ cleanup_debugfs: cleanup_sysfs: device_remove_file(&pdev->dev, &dev_attr_reset_reason); device_remove_file(&pdev->dev, &dev_attr_reset_level); + clk_notifier_unregister(pmc->clk, &pmc->clk_nb); + return err; } -- cgit v1.2.3 From 69dfb3d4a89afccca1d8f282e49ad1362100cc43 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko <digetx@gmail.com> Date: Thu, 26 Sep 2019 22:17:55 +0300 Subject: soc/tegra: pmc: Remove unnecessary memory barrier The removed barrier isn't needed because writes/reads are strictly ordered and even if PMC had separate ports for writes, it wouldn't matter since the hardware logic takes into effect after triggering CPU's power-gating and at that point all CPU accesses are guaranteed to be completed. That barrier was copied from the old arch/ code during transition to the soc/ PMC driver and even that the code structure was different back then, the barrier didn't have a real useful purpose from the start. Lastly, the tegra_pmc_writel() naturally inserts wmb() because it uses writel(), and thus this change doesn't actually make any difference in terms of interacting with hardware. Hence let's remove the barrier to clean up code a tad. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com> --- drivers/soc/tegra/pmc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 1f78e8497fde..8db63cfba833 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1478,8 +1478,6 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) do_div(ticks, USEC_PER_SEC); tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER); - wmb(); - value = tegra_pmc_readl(pmc, PMC_CNTRL); value &= ~PMC_CNTRL_SIDE_EFFECT_LP0; value |= PMC_CNTRL_CPU_PWRREQ_OE; -- cgit v1.2.3 From c6b69bf143734a797b45e4728dc5ad80586d206c Mon Sep 17 00:00:00 2001 From: Wei Yongjun <weiyongjun1@huawei.com> Date: Tue, 29 Oct 2019 09:57:56 -0700 Subject: soc: ti: omap-prm: fix return value check in omap_prm_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Fixes: 3e99cb214f03 ("soc: ti: add initial PRM driver with reset control support") Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@oracle.com> --- drivers/soc/ti/omap_prm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c index db47a8bceb87..96c6f777519c 100644 --- a/drivers/soc/ti/omap_prm.c +++ b/drivers/soc/ti/omap_prm.c @@ -375,8 +375,8 @@ static int omap_prm_probe(struct platform_device *pdev) prm->data = data; prm->base = devm_ioremap_resource(&pdev->dev, res); - if (!prm->base) - return -ENOMEM; + if (IS_ERR(prm->base)) + return PTR_ERR(prm->base); return omap_prm_reset_init(pdev, prm); } -- cgit v1.2.3 From 89e551e83869732d5b9fd21d7cfdb1f8d62cf5d0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter <dan.carpenter@oracle.com> Date: Tue, 29 Oct 2019 21:27:42 +0300 Subject: soc: samsung: exynos-asv: Potential NULL dereference in exynos_asv_update_opps() The dev_pm_opp_get_opp_table() returns error pointers if it's disabled in the config and it returns NULL if there is an error. This code only checks for error pointers so it could lead to an Oops inside the dev_pm_opp_put_opp_table() function. Fixes: 5ea428595cc5 ("soc: samsung: Add Exynos Adaptive Supply Voltage driver") Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> --- drivers/soc/samsung/exynos-asv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/samsung/exynos-asv.c b/drivers/soc/samsung/exynos-asv.c index 8abf4dfaa5c5..30bb7b7cc769 100644 --- a/drivers/soc/samsung/exynos-asv.c +++ b/drivers/soc/samsung/exynos-asv.c @@ -93,7 +93,7 @@ static int exynos_asv_update_opps(struct exynos_asv *asv) continue; opp_table = dev_pm_opp_get_opp_table(cpu); - if (IS_ERR(opp_table)) + if (IS_ERR_OR_NULL(opp_table)) continue; if (!last_opp_table || opp_table != last_opp_table) { -- cgit v1.2.3 From 4194b583c104922c6141d6610bfbce26847959df Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 16 Oct 2019 16:33:06 +0200 Subject: soc: renesas: Add missing check for non-zero product register address If the DTB for a device with an RZ/A2 SoC lacks a device node for the BSID register, the ID validation code falls back to using a register at address 0x0, which leads to undefined behavior (e.g. reading back a random value). This could be fixed by letting fam_rza2.reg point to the actual BSID register. However, the hardcoded fallbacks were meant for backwards compatibility with old DTBs only, not for new SoCs. Hence fix this by validating renesas_family.reg before using it. Fixes: 175f435f44b724e3 ("soc: renesas: identify RZ/A2") Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Link: https://lore.kernel.org/r/20191016143306.28995-1-geert+renesas@glider.be --- drivers/soc/renesas/renesas-soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 45135bc88e27..f348ae346c4f 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -334,7 +334,7 @@ static int __init renesas_soc_init(void) if (np) { chipid = of_iomap(np, 0); of_node_put(np); - } else if (soc->id) { + } else if (soc->id && family->reg) { chipid = ioremap(family->reg, 4); } if (chipid) { -- cgit v1.2.3 From f79edb17b618c3122d11eb0c848855b99c42fa24 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 23 Oct 2019 14:33:32 +0200 Subject: soc: renesas: Rename SYSC_R8A7796 to SYSC_R8A77960 Rename CONFIG_SYSC_R8A7796 for R-Car M3-W (R8A77960) to CONFIG_SYSC_R8A77960, to avoid confusion with R-Car M3-W+ (R8A77961), which will use CONFIG_SYSC_R8A77961. Rename r8a7796_sysc_info and r8a7796_sysc_init for consistency. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Link: https://lore.kernel.org/r/20191023123342.13100-2-geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 4 ++-- drivers/soc/renesas/Makefile | 2 +- drivers/soc/renesas/r8a7796-sysc.c | 8 ++++---- drivers/soc/renesas/rcar-sysc.c | 4 ++-- drivers/soc/renesas/rcar-sysc.h | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 3bd0c218bf30..328d7c409202 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -202,7 +202,7 @@ config ARCH_R8A7795 config ARCH_R8A7796 bool "Renesas R-Car M3-W SoC Platform" select ARCH_RCAR_GEN3 - select SYSC_R8A7796 + select SYSC_R8A77960 help This enables support for the Renesas R-Car M3-W SoC. @@ -292,7 +292,7 @@ config SYSC_R8A7795 bool "R-Car H3 System Controller support" if COMPILE_TEST select SYSC_RCAR -config SYSC_R8A7796 +config SYSC_R8A77960 bool "R-Car M3-W System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index e99dc37ea120..d8a7cfdc9c9c 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o -obj-$(CONFIG_SYSC_R8A7796) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77960) += r8a7796-sysc.o obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o diff --git a/drivers/soc/renesas/r8a7796-sysc.c b/drivers/soc/renesas/r8a7796-sysc.c index d374622a667b..c2accd8d7668 100644 --- a/drivers/soc/renesas/r8a7796-sysc.c +++ b/drivers/soc/renesas/r8a7796-sysc.c @@ -47,16 +47,16 @@ static const struct soc_device_attribute r8a7796es1[] __initconst = { { /* sentinel */ } }; -static int __init r8a7796_sysc_init(void) +static int __init r8a77960_sysc_init(void) { if (soc_device_match(r8a7796es1)) - r8a7796_sysc_info.extmask_val = 0; + r8a77960_sysc_info.extmask_val = 0; return 0; } -struct rcar_sysc_info r8a7796_sysc_info __initdata = { - .init = r8a7796_sysc_init, +struct rcar_sysc_info r8a77960_sysc_info __initdata = { + .init = r8a77960_sysc_init, .areas = r8a7796_areas, .num_areas = ARRAY_SIZE(r8a7796_areas), .extmask_offs = 0x2f8, diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index d4f2ed52b2b3..5f17b12e9d0c 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -313,8 +313,8 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A7795 { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, #endif -#ifdef CONFIG_SYSC_R8A7796 - { .compatible = "renesas,r8a7796-sysc", .data = &r8a7796_sysc_info }, +#ifdef CONFIG_SYSC_R8A77960 + { .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info }, #endif #ifdef CONFIG_SYSC_R8A77965 { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index e4c9854f5dc0..379a6b2661eb 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -61,7 +61,7 @@ extern const struct rcar_sysc_info r8a7791_sysc_info; extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; extern struct rcar_sysc_info r8a7795_sysc_info; -extern struct rcar_sysc_info r8a7796_sysc_info; +extern struct rcar_sysc_info r8a77960_sysc_info; extern const struct rcar_sysc_info r8a77965_sysc_info; extern const struct rcar_sysc_info r8a77970_sysc_info; extern const struct rcar_sysc_info r8a77980_sysc_info; -- cgit v1.2.3 From 39e57e14d7eaf8182ce09640c49423122909d5b6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 23 Oct 2019 14:33:33 +0200 Subject: soc: renesas: Add ARCH_R8A77960 for existing R-Car M3-W Add CONFIG_ARCH_R8A77960 as a new config symbol for R-Car M3-W (R8A77960), to replace CONFIG_ARCH_R8A7796, and avoid confusion with R-Car M3-W+ (R8A77961), which will use CONFIG_ARCH_R8A77961. Note that for now, CONFIG_ARCH_R8A7796 is retained, and just selects CONFIG_ARCH_R8A77960. This relaxes dependencies of other subsystems on the SoC configuration symbol, and provides a smooth transition path for config files through "make oldconfig". Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Link: https://lore.kernel.org/r/20191023123342.13100-3-geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 8 ++++++-- drivers/soc/renesas/renesas-soc.c | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 328d7c409202..ce8e86a037d1 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -199,10 +199,14 @@ config ARCH_R8A7795 help This enables support for the Renesas R-Car H3 SoC. -config ARCH_R8A7796 - bool "Renesas R-Car M3-W SoC Platform" +config ARCH_R8A77960 + bool select ARCH_RCAR_GEN3 select SYSC_R8A77960 + +config ARCH_R8A7796 + bool "Renesas R-Car M3-W SoC Platform" + select ARCH_R8A77960 help This enables support for the Renesas R-Car M3-W SoC. diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index f348ae346c4f..76345b63556f 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -262,7 +262,7 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A7795 { .compatible = "renesas,r8a7795", .data = &soc_rcar_h3 }, #endif -#ifdef CONFIG_ARCH_R8A7796 +#ifdef CONFIG_ARCH_R8A77960 { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, #endif #ifdef CONFIG_ARCH_R8A77965 -- cgit v1.2.3 From cadadde21007cbbd395c4f141af94f3f92345f13 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 23 Oct 2019 14:33:34 +0200 Subject: soc: renesas: Add ARCH_R8A77961 for new R-Car M3-W+ Add CONFIG_ARCH_R8A77961 as a configuration symbol for the new Renesas R-Car M3-W+ (R8A77961) SoC. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Link: https://lore.kernel.org/r/20191023123342.13100-4-geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index ce8e86a037d1..7b00daa29092 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -210,6 +210,12 @@ config ARCH_R8A7796 help This enables support for the Renesas R-Car M3-W SoC. +config ARCH_R8A77961 + bool "Renesas R-Car M3-W+ SoC Platform" + select ARCH_RCAR_GEN3 + help + This enables support for the Renesas R-Car M3-W+ SoC. + config ARCH_R8A77965 bool "Renesas R-Car M3-N SoC Platform" select ARCH_RCAR_GEN3 -- cgit v1.2.3 From 9c9f7891093b02eb64ca4e1c7ab776a4296c058f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 23 Oct 2019 14:33:35 +0200 Subject: soc: renesas: Identify R-Car M3-W+ Add support for identifying the R-Car M3-W+ (R8A77961) SoC, which shares the Product ID Number with R-Car M3-W (R8A77960), but differs in CUT Number (Ver. 3.0), and uses a different compatible value. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Link: https://lore.kernel.org/r/20191023123342.13100-5-geert+renesas@glider.be --- drivers/soc/renesas/renesas-soc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 76345b63556f..850f5733dc88 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -265,6 +265,9 @@ static const struct of_device_id renesas_socs[] __initconst = { #ifdef CONFIG_ARCH_R8A77960 { .compatible = "renesas,r8a7796", .data = &soc_rcar_m3_w }, #endif +#ifdef CONFIG_ARCH_R8A77961 + { .compatible = "renesas,r8a77961", .data = &soc_rcar_m3_w }, +#endif #ifdef CONFIG_ARCH_R8A77965 { .compatible = "renesas,r8a77965", .data = &soc_rcar_m3_n }, #endif -- cgit v1.2.3 From 41684bff3b2f4a2d0b5bd95df6dbd1b832a7e8ae Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 23 Oct 2019 14:33:36 +0200 Subject: soc: renesas: rcar-rst: Add R8A77961 support Add support for the Reset block in the R-Car M3-W+ (R8A77961) SoC to the Renesas R-Car RST driver. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Link: https://lore.kernel.org/r/20191023123342.13100-6-geert+renesas@glider.be --- drivers/soc/renesas/rcar-rst.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index cd5592977cef..14d05a070dd3 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -59,6 +59,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { /* R-Car Gen3 */ { .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 }, + { .compatible = "renesas,r8a77961-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 }, { .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 }, -- cgit v1.2.3 From bdde3d3ec934839b3c11689ead467099f1c36c12 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 23 Oct 2019 14:33:37 +0200 Subject: soc: renesas: rcar-sysc: Add R8A77961 support Add support for the power areas in the Renesas R-Car M3-W+ (R8A77961) SoC to the R-Car System Controller driver. R-Car M3-W+ (aka R-Car M3-W ES3.0) is very similar to R-Car M3-W (R8A77960), which allows for both SoCs to share a driver: - R-Car M3-W+ lacks the A2VC power area, so its area must be nullified, - The existing support for the SYSCEXTMASK register added in commit 9bd645af9d2a49ac ("soc: renesas: r8a7796-sysc: Fix power request conflicts") applies to ES3.0 and later only. As R-Car M3-W+ uses a different compatible value, differentiate based on that, instead of on the ES version. Based on a patch in the BSP by Dien Pham <dien.pham.ry@renesas.com>. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Tested-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Link: https://lore.kernel.org/r/20191023123342.13100-7-geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 5 +++++ drivers/soc/renesas/Makefile | 1 + drivers/soc/renesas/r8a7796-sysc.c | 27 +++++++++++++++------------ drivers/soc/renesas/rcar-sysc.c | 3 +++ drivers/soc/renesas/rcar-sysc.h | 3 ++- 5 files changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 7b00daa29092..f93492b72c04 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -213,6 +213,7 @@ config ARCH_R8A7796 config ARCH_R8A77961 bool "Renesas R-Car M3-W+ SoC Platform" select ARCH_RCAR_GEN3 + select SYSC_R8A77961 help This enables support for the Renesas R-Car M3-W+ SoC. @@ -306,6 +307,10 @@ config SYSC_R8A77960 bool "R-Car M3-W System Controller support" if COMPILE_TEST select SYSC_RCAR +config SYSC_R8A77961 + bool "R-Car M3-W+ System Controller support" if COMPILE_TEST + select SYSC_RCAR + config SYSC_R8A77965 bool "R-Car M3-N System Controller support" if COMPILE_TEST select SYSC_RCAR diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index d8a7cfdc9c9c..e595c3c3bd10 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o obj-$(CONFIG_SYSC_R8A77960) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77961) += r8a7796-sysc.o obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o diff --git a/drivers/soc/renesas/r8a7796-sysc.c b/drivers/soc/renesas/r8a7796-sysc.c index c2accd8d7668..471bd5b3b6ad 100644 --- a/drivers/soc/renesas/r8a7796-sysc.c +++ b/drivers/soc/renesas/r8a7796-sysc.c @@ -1,19 +1,19 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Renesas R-Car M3-W System Controller + * Renesas R-Car M3-W/W+ System Controller * * Copyright (C) 2016 Glider bvba + * Copyright (C) 2018-2019 Renesas Electronics Corporation */ #include <linux/bits.h> #include <linux/kernel.h> -#include <linux/sys_soc.h> #include <dt-bindings/power/r8a7796-sysc.h> #include "rcar-sysc.h" -static const struct rcar_sysc_area r8a7796_areas[] __initconst = { +static struct rcar_sysc_area r8a7796_areas[] __initdata = { { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON, PD_SCU }, @@ -41,24 +41,27 @@ static const struct rcar_sysc_area r8a7796_areas[] __initconst = { }; -/* Fixups for R-Car M3-W ES1.x revision */ -static const struct soc_device_attribute r8a7796es1[] __initconst = { - { .soc_id = "r8a7796", .revision = "ES1.*" }, - { /* sentinel */ } +#ifdef CONFIG_SYSC_R8A77960 +const struct rcar_sysc_info r8a77960_sysc_info __initconst = { + .areas = r8a7796_areas, + .num_areas = ARRAY_SIZE(r8a7796_areas), }; +#endif /* CONFIG_SYSC_R8A77960 */ -static int __init r8a77960_sysc_init(void) +#ifdef CONFIG_SYSC_R8A77961 +static int __init r8a77961_sysc_init(void) { - if (soc_device_match(r8a7796es1)) - r8a77960_sysc_info.extmask_val = 0; + rcar_sysc_nullify(r8a7796_areas, ARRAY_SIZE(r8a7796_areas), + R8A7796_PD_A2VC0); return 0; } -struct rcar_sysc_info r8a77960_sysc_info __initdata = { - .init = r8a77960_sysc_init, +const struct rcar_sysc_info r8a77961_sysc_info __initconst = { + .init = r8a77961_sysc_init, .areas = r8a7796_areas, .num_areas = ARRAY_SIZE(r8a7796_areas), .extmask_offs = 0x2f8, .extmask_val = BIT(0), }; +#endif /* CONFIG_SYSC_R8A77961 */ diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 5f17b12e9d0c..f0b291e02b8a 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -316,6 +316,9 @@ static const struct of_device_id rcar_sysc_matches[] __initconst = { #ifdef CONFIG_SYSC_R8A77960 { .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info }, #endif +#ifdef CONFIG_SYSC_R8A77961 + { .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info }, +#endif #ifdef CONFIG_SYSC_R8A77965 { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, #endif diff --git a/drivers/soc/renesas/rcar-sysc.h b/drivers/soc/renesas/rcar-sysc.h index 379a6b2661eb..8d074489fba9 100644 --- a/drivers/soc/renesas/rcar-sysc.h +++ b/drivers/soc/renesas/rcar-sysc.h @@ -61,7 +61,8 @@ extern const struct rcar_sysc_info r8a7791_sysc_info; extern const struct rcar_sysc_info r8a7792_sysc_info; extern const struct rcar_sysc_info r8a7794_sysc_info; extern struct rcar_sysc_info r8a7795_sysc_info; -extern struct rcar_sysc_info r8a77960_sysc_info; +extern const struct rcar_sysc_info r8a77960_sysc_info; +extern const struct rcar_sysc_info r8a77961_sysc_info; extern const struct rcar_sysc_info r8a77965_sysc_info; extern const struct rcar_sysc_info r8a77970_sysc_info; extern const struct rcar_sysc_info r8a77980_sysc_info; -- cgit v1.2.3 From 768e1a8e093677f5e0e7d0a447c1a856c16cbb66 Mon Sep 17 00:00:00 2001 From: Leonard Crestez <leonard.crestez@nxp.com> Date: Wed, 30 Oct 2019 01:17:39 +0200 Subject: soc: imx8mq: Read SOC revision from TF-A SOC revision on older imx8mq is not available in fuses so on anything other than B1 current code just reports "unknown". TF-A already handles this by parsing the ROM and exposes the value through a SMC call. Call this instead of reimplementing the workaround in the kernel itself. Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com> Reviewed-by: Abel Vesa <abel.vesa@nxp.com> Signed-off-by: Shawn Guo <shawnguo@kernel.org> --- drivers/soc/imx/soc-imx8.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index fcbf745a9971..d84ed736cdb0 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -9,6 +9,7 @@ #include <linux/slab.h> #include <linux/sys_soc.h> #include <linux/platform_device.h> +#include <linux/arm-smccc.h> #include <linux/of.h> #define REV_B1 0x21 @@ -16,6 +17,8 @@ #define IMX8MQ_SW_INFO_B1 0x40 #define IMX8MQ_SW_MAGIC_B1 0xff0055aa +#define IMX_SIP_GET_SOC_INFO 0xc2000006 + #define OCOTP_UID_LOW 0x410 #define OCOTP_UID_HIGH 0x420 @@ -29,6 +32,22 @@ struct imx8_soc_data { static u64 soc_uid; +#ifdef CONFIG_HAVE_ARM_SMCCC +static u32 imx8mq_soc_revision_from_atf(void) +{ + struct arm_smccc_res res; + + arm_smccc_smc(IMX_SIP_GET_SOC_INFO, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0 == SMCCC_RET_NOT_SUPPORTED) + return 0; + else + return res.a0 & 0xff; +} +#else +static inline u32 imx8mq_soc_revision_from_atf(void) { return 0; }; +#endif + static u32 __init imx8mq_soc_revision(void) { struct device_node *np; @@ -43,9 +62,16 @@ static u32 __init imx8mq_soc_revision(void) ocotp_base = of_iomap(np, 0); WARN_ON(!ocotp_base); - magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); - if (magic == IMX8MQ_SW_MAGIC_B1) - rev = REV_B1; + /* + * SOC revision on older imx8mq is not available in fuses so query + * the value from ATF instead. + */ + rev = imx8mq_soc_revision_from_atf(); + if (!rev) { + magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); + if (magic == IMX8MQ_SW_MAGIC_B1) + rev = REV_B1; + } soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH); soc_uid <<= 32; -- cgit v1.2.3 From fdfc6997bd083acd066db99792694fa8a31a6cac Mon Sep 17 00:00:00 2001 From: Christian Hewitt <christianshewitt@gmail.com> Date: Mon, 21 Oct 2019 12:20:04 +0400 Subject: soc: amlogic: meson-gx-socinfo: Fix S905D3 ID for VIM3L Chip on the board is S905D3 not S905X3: [ 0.098998] soc soc0: Amlogic Meson SM1 (S905D3) Revision 2b:c (b0:2) Detected Change from v1: use 0xf0 mask instead of 0xf2 as advised by Neil Armstrong. Fixes: 1d7c541b8a5b ("soc: amlogic: meson-gx-socinfo: Add S905X3 ID for VIM3L") Signed-off-by: Christian Hewitt <christianshewitt@gmail.com> Acked-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Kevin Hilman <khilman@baylibre.com> --- drivers/soc/amlogic/meson-gx-socinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 87ed558fa1c2..01fc0d20a70d 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -69,7 +69,7 @@ static const struct meson_gx_package_id { { "S922X", 0x29, 0x40, 0xf0 }, { "A311D", 0x29, 0x10, 0xf0 }, { "S905X3", 0x2b, 0x5, 0xf }, - { "S905X3", 0x2b, 0xb0, 0xf2 }, + { "S905D3", 0x2b, 0xb0, 0xf0 }, { "A113L", 0x2c, 0x0, 0xf8 }, }; -- cgit v1.2.3 From 90a943145e2ef17b559ef9e98a7c12a1abc9ae84 Mon Sep 17 00:00:00 2001 From: Weiyi Lu <weiyi.lu@mediatek.com> Date: Wed, 28 Aug 2019 17:11:36 +0800 Subject: soc: mediatek: Refactor polling timeout and documentation Use USEC_PER_SEC to indicate the polling timeout directly. And add documentation of scp_domain_data. Signed-off-by: Weiyi Lu <weiyi.lu@mediatek.com> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> --- drivers/soc/mediatek/mtk-scpsys.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index 503222d0d0da..e97fc0e45400 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -21,7 +21,7 @@ #include <dt-bindings/power/mt8173-power.h> #define MTK_POLL_DELAY_US 10 -#define MTK_POLL_TIMEOUT (jiffies_to_usecs(HZ)) +#define MTK_POLL_TIMEOUT USEC_PER_SEC #define MTK_SCPD_ACTIVE_WAKEUP BIT(0) #define MTK_SCPD_FWAIT_SRAM BIT(1) @@ -108,6 +108,17 @@ static const char * const clk_names[] = { #define MAX_CLKS 3 +/** + * struct scp_domain_data - scp domain data for power on/off flow + * @name: The domain name. + * @sta_mask: The mask for power on/off status bit. + * @ctl_offs: The offset for main power control register. + * @sram_pdn_bits: The mask for sram power control bits. + * @sram_pdn_ack_bits: The mask for sram power control acked bits. + * @bus_prot_mask: The mask for single step bus protection. + * @clk_id: The basic clocks required by this power domain. + * @caps: The flag for active wake-up action. + */ struct scp_domain_data { const char *name; u32 sta_mask; -- cgit v1.2.3 From d744d035ecb57decf0ae1711228756445708545a Mon Sep 17 00:00:00 2001 From: Weiyi Lu <weiyi.lu@mediatek.com> Date: Wed, 28 Aug 2019 17:11:37 +0800 Subject: soc: mediatek: Refactor regulator control Put regulator enable and disable control in separate functions. Signed-off-by: Weiyi Lu <weiyi.lu@mediatek.com> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> --- drivers/soc/mediatek/mtk-scpsys.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index e97fc0e45400..aed540d2686c 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -191,6 +191,22 @@ static int scpsys_domain_is_on(struct scp_domain *scpd) return -EINVAL; } +static int scpsys_regulator_enable(struct scp_domain *scpd) +{ + if (!scpd->supply) + return 0; + + return regulator_enable(scpd->supply); +} + +static int scpsys_regulator_disable(struct scp_domain *scpd) +{ + if (!scpd->supply) + return 0; + + return regulator_disable(scpd->supply); +} + static int scpsys_power_on(struct generic_pm_domain *genpd) { struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); @@ -201,11 +217,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) int ret, tmp; int i; - if (scpd->supply) { - ret = regulator_enable(scpd->supply); - if (ret) - return ret; - } + ret = scpsys_regulator_enable(scpd); + if (ret < 0) + return ret; for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) { ret = clk_prepare_enable(scpd->clk[i]); @@ -273,8 +287,7 @@ err_pwr_ack: clk_disable_unprepare(scpd->clk[i]); } err_clk: - if (scpd->supply) - regulator_disable(scpd->supply); + scpsys_regulator_disable(scpd); dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); @@ -333,8 +346,9 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) clk_disable_unprepare(scpd->clk[i]); - if (scpd->supply) - regulator_disable(scpd->supply); + ret = scpsys_regulator_disable(scpd); + if (ret < 0) + goto out; return 0; -- cgit v1.2.3 From cef021e2f5cb8a1c8cd23ebc77232e6563ce251d Mon Sep 17 00:00:00 2001 From: Weiyi Lu <weiyi.lu@mediatek.com> Date: Wed, 28 Aug 2019 17:11:38 +0800 Subject: soc: mediatek: Refactor clock control Put clock enable and disable control in separate function. Signed-off-by: Weiyi Lu <weiyi.lu@mediatek.com> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> --- drivers/soc/mediatek/mtk-scpsys.c | 45 ++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 17 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index aed540d2686c..73e4a1aace2f 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -207,6 +207,29 @@ static int scpsys_regulator_disable(struct scp_domain *scpd) return regulator_disable(scpd->supply); } +static void scpsys_clk_disable(struct clk *clk[], int max_num) +{ + int i; + + for (i = max_num - 1; i >= 0; i--) + clk_disable_unprepare(clk[i]); +} + +static int scpsys_clk_enable(struct clk *clk[], int max_num) +{ + int i, ret = 0; + + for (i = 0; i < max_num && clk[i]; i++) { + ret = clk_prepare_enable(clk[i]); + if (ret) { + scpsys_clk_disable(clk, i); + break; + } + } + + return ret; +} + static int scpsys_power_on(struct generic_pm_domain *genpd) { struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); @@ -215,21 +238,14 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) u32 pdn_ack = scpd->data->sram_pdn_ack_bits; u32 val; int ret, tmp; - int i; ret = scpsys_regulator_enable(scpd); if (ret < 0) return ret; - for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) { - ret = clk_prepare_enable(scpd->clk[i]); - if (ret) { - for (--i; i >= 0; i--) - clk_disable_unprepare(scpd->clk[i]); - - goto err_clk; - } - } + ret = scpsys_clk_enable(scpd->clk, MAX_CLKS); + if (ret) + goto err_clk; val = readl(ctl_addr); val |= PWR_ON_BIT; @@ -282,10 +298,7 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) return 0; err_pwr_ack: - for (i = MAX_CLKS - 1; i >= 0; i--) { - if (scpd->clk[i]) - clk_disable_unprepare(scpd->clk[i]); - } + scpsys_clk_disable(scpd->clk, MAX_CLKS); err_clk: scpsys_regulator_disable(scpd); @@ -302,7 +315,6 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) u32 pdn_ack = scpd->data->sram_pdn_ack_bits; u32 val; int ret, tmp; - int i; if (scpd->data->bus_prot_mask) { ret = mtk_infracfg_set_bus_protection(scp->infracfg, @@ -343,8 +355,7 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) if (ret < 0) goto out; - for (i = 0; i < MAX_CLKS && scpd->clk[i]; i++) - clk_disable_unprepare(scpd->clk[i]); + scpsys_clk_disable(scpd->clk, MAX_CLKS); ret = scpsys_regulator_disable(scpd); if (ret < 0) -- cgit v1.2.3 From 0545aa1b7a1472a4d0499ccc7666634c8cf47305 Mon Sep 17 00:00:00 2001 From: Weiyi Lu <weiyi.lu@mediatek.com> Date: Wed, 28 Aug 2019 17:11:39 +0800 Subject: soc: mediatek: Refactor sram control Put sram enable and disable control in separate functions. Signed-off-by: Weiyi Lu <weiyi.lu@mediatek.com> Reviewed-by: Nicolas Boichat <drinkcat@chromium.org> [mb: fix coding style of reading register and changing the read value] Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> --- drivers/soc/mediatek/mtk-scpsys.c | 80 ++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 27 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index 73e4a1aace2f..fafc0d311cf8 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -230,12 +230,57 @@ static int scpsys_clk_enable(struct clk *clk[], int max_num) return ret; } +static int scpsys_sram_enable(struct scp_domain *scpd, void __iomem *ctl_addr) +{ + u32 val; + u32 pdn_ack = scpd->data->sram_pdn_ack_bits; + int tmp; + + val = readl(ctl_addr); + val &= ~scpd->data->sram_pdn_bits; + writel(val, ctl_addr); + + /* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ + if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { + /* + * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for + * MT7622_POWER_DOMAIN_WB and thus just a trivial setup + * is applied here. + */ + usleep_range(12000, 12100); + } else { + /* Either wait until SRAM_PDN_ACK all 1 or 0 */ + int ret = readl_poll_timeout(ctl_addr, tmp, + (tmp & pdn_ack) == 0, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + } + + return 0; +} + +static int scpsys_sram_disable(struct scp_domain *scpd, void __iomem *ctl_addr) +{ + u32 val; + u32 pdn_ack = scpd->data->sram_pdn_ack_bits; + int tmp; + + val = readl(ctl_addr); + val |= scpd->data->sram_pdn_bits; + writel(val, ctl_addr); + + /* Either wait until SRAM_PDN_ACK all 1 or 0 */ + return readl_poll_timeout(ctl_addr, tmp, + (tmp & pdn_ack) == pdn_ack, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); +} + static int scpsys_power_on(struct generic_pm_domain *genpd) { struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); struct scp *scp = scpd->scp; void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; - u32 pdn_ack = scpd->data->sram_pdn_ack_bits; u32 val; int ret, tmp; @@ -247,6 +292,7 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) if (ret) goto err_clk; + /* subsys power on */ val = readl(ctl_addr); val |= PWR_ON_BIT; writel(val, ctl_addr); @@ -268,24 +314,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) val |= PWR_RST_B_BIT; writel(val, ctl_addr); - val &= ~scpd->data->sram_pdn_bits; - writel(val, ctl_addr); - - /* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ - if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { - /* - * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for - * MT7622_POWER_DOMAIN_WB and thus just a trivial setup is - * applied here. - */ - usleep_range(12000, 12100); - - } else { - ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == 0, - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); - if (ret < 0) - goto err_pwr_ack; - } + ret = scpsys_sram_enable(scpd, ctl_addr); + if (ret < 0) + goto err_pwr_ack; if (scpd->data->bus_prot_mask) { ret = mtk_infracfg_clear_bus_protection(scp->infracfg, @@ -312,7 +343,6 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); struct scp *scp = scpd->scp; void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; - u32 pdn_ack = scpd->data->sram_pdn_ack_bits; u32 val; int ret, tmp; @@ -324,16 +354,12 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) goto out; } - val = readl(ctl_addr); - val |= scpd->data->sram_pdn_bits; - writel(val, ctl_addr); - - /* wait until SRAM_PDN_ACK all 1 */ - ret = readl_poll_timeout(ctl_addr, tmp, (tmp & pdn_ack) == pdn_ack, - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + ret = scpsys_sram_disable(scpd, ctl_addr); if (ret < 0) goto out; + /* subsys power off */ + val = readl(ctl_addr); val |= PWR_ISO_BIT; writel(val, ctl_addr); -- cgit v1.2.3 From 662c9d55c5ccb37f3920ecab9720f2ebf2a6ca18 Mon Sep 17 00:00:00 2001 From: Weiyi Lu <weiyi.lu@mediatek.com> Date: Wed, 28 Aug 2019 17:11:40 +0800 Subject: soc: mediatek: Refactor bus protection control Put bus protection enable and disable control in separate functions. Signed-off-by: Weiyi Lu <weiyi.lu@mediatek.com> Signed-off-by: Matthias Brugger <matthias.bgg@gmail.com> --- drivers/soc/mediatek/mtk-scpsys.c | 44 ++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 14 deletions(-) (limited to 'drivers/soc') diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c index fafc0d311cf8..f669d3754627 100644 --- a/drivers/soc/mediatek/mtk-scpsys.c +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -276,6 +276,30 @@ static int scpsys_sram_disable(struct scp_domain *scpd, void __iomem *ctl_addr) MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); } +static int scpsys_bus_protect_enable(struct scp_domain *scpd) +{ + struct scp *scp = scpd->scp; + + if (!scpd->data->bus_prot_mask) + return 0; + + return mtk_infracfg_set_bus_protection(scp->infracfg, + scpd->data->bus_prot_mask, + scp->bus_prot_reg_update); +} + +static int scpsys_bus_protect_disable(struct scp_domain *scpd) +{ + struct scp *scp = scpd->scp; + + if (!scpd->data->bus_prot_mask) + return 0; + + return mtk_infracfg_clear_bus_protection(scp->infracfg, + scpd->data->bus_prot_mask, + scp->bus_prot_reg_update); +} + static int scpsys_power_on(struct generic_pm_domain *genpd) { struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); @@ -318,13 +342,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd) if (ret < 0) goto err_pwr_ack; - if (scpd->data->bus_prot_mask) { - ret = mtk_infracfg_clear_bus_protection(scp->infracfg, - scpd->data->bus_prot_mask, - scp->bus_prot_reg_update); - if (ret) - goto err_pwr_ack; - } + ret = scpsys_bus_protect_disable(scpd); + if (ret < 0) + goto err_pwr_ack; return 0; @@ -346,13 +366,9 @@ static int scpsys_power_off(struct generic_pm_domain *genpd) u32 val; int ret, tmp; - if (scpd->data->bus_prot_mask) { - ret = mtk_infracfg_set_bus_protection(scp->infracfg, - scpd->data->bus_prot_mask, - scp->bus_prot_reg_update); - if (ret) - goto out; - } + ret = scpsys_bus_protect_enable(scpd); + if (ret < 0) + goto out; ret = scpsys_sram_disable(scpd, ctl_addr); if (ret < 0) -- cgit v1.2.3 From c3277f8ee8cdadf011b8390dfdb4c44ecfaa1a7a Mon Sep 17 00:00:00 2001 From: Kamel Bouhara <kamel.bouhara@bootlin.com> Date: Fri, 4 Oct 2019 17:18:02 +0200 Subject: soc: at91: Add Atmel SFR SN (Serial Number) support Add support to read SFR's read-only registers providing the SoC Serial Numbers (SN0+SN1) to userspace. ~ # hexdump -n 8 -e'"%d\n"' /sys/bus/nvmem/devices/atmel-sfr0/nvmem 959527243 371539274 Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com> Tested-by: Tudor Ambarus <tudor.ambarus@microchip.com> Reviewed-by: Tudor Ambarus <tudor.ambarus@microchip.com> Link: https://lore.kernel.org/r/20191004151802.21793-1-kamel.bouhara@bootlin.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com> --- drivers/soc/atmel/Kconfig | 11 ++++++ drivers/soc/atmel/Makefile | 1 + drivers/soc/atmel/sfr.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 drivers/soc/atmel/sfr.c (limited to 'drivers/soc') diff --git a/drivers/soc/atmel/Kconfig b/drivers/soc/atmel/Kconfig index 05528139b023..50caf6db9c0e 100644 --- a/drivers/soc/atmel/Kconfig +++ b/drivers/soc/atmel/Kconfig @@ -5,3 +5,14 @@ config AT91_SOC_ID default ARCH_AT91 help Include support for the SoC bus on the Atmel ARM SoCs. + +config AT91_SOC_SFR + tristate "Special Function Registers support" + depends on ARCH_AT91 || COMPILE_TEST + help + This is a driver for the Special Function Registers available on + Atmel SAMA5Dx SoCs, providing access to specific aspects of the + integrated memory, bridge implementations, processor etc. + + This driver can also be built as a module. If so, the module + will be called sfr. diff --git a/drivers/soc/atmel/Makefile b/drivers/soc/atmel/Makefile index 7ca355d10553..d849a897cd77 100644 --- a/drivers/soc/atmel/Makefile +++ b/drivers/soc/atmel/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_AT91_SOC_ID) += soc.o +obj-$(CONFIG_AT91_SOC_SFR) += sfr.o diff --git a/drivers/soc/atmel/sfr.c b/drivers/soc/atmel/sfr.c new file mode 100644 index 000000000000..0525eef49d1a --- /dev/null +++ b/drivers/soc/atmel/sfr.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * sfr.c - driver for special function registers + * + * Copyright (C) 2019 Bootlin. + * + */ +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/random.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define SFR_SN0 0x4c +#define SFR_SN_SIZE 8 + +struct atmel_sfr_priv { + struct regmap *regmap; +}; + +static int atmel_sfr_read(void *context, unsigned int offset, + void *buf, size_t bytes) +{ + struct atmel_sfr_priv *priv = context; + + return regmap_bulk_read(priv->regmap, SFR_SN0 + offset, + buf, bytes / 4); +} + +static struct nvmem_config atmel_sfr_nvmem_config = { + .name = "atmel-sfr", + .read_only = true, + .word_size = 4, + .stride = 4, + .size = SFR_SN_SIZE, + .reg_read = atmel_sfr_read, +}; + +static int atmel_sfr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct nvmem_device *nvmem; + struct atmel_sfr_priv *priv; + u8 sn[SFR_SN_SIZE]; + int ret; + + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = syscon_node_to_regmap(np); + if (IS_ERR(priv->regmap)) { + dev_err(dev, "cannot get parent's regmap\n"); + return PTR_ERR(priv->regmap); + } + + atmel_sfr_nvmem_config.dev = dev; + atmel_sfr_nvmem_config.priv = priv; + + nvmem = devm_nvmem_register(dev, &atmel_sfr_nvmem_config); + if (IS_ERR(nvmem)) { + dev_err(dev, "error registering nvmem config\n"); + return PTR_ERR(nvmem); + } + + ret = atmel_sfr_read(priv, 0, sn, SFR_SN_SIZE); + if (ret == 0) + add_device_randomness(sn, SFR_SN_SIZE); + + return ret; +} + +static const struct of_device_id atmel_sfr_dt_ids[] = { + { + .compatible = "atmel,sama5d2-sfr", + }, { + .compatible = "atmel,sama5d4-sfr", + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, atmel_sfr_dt_ids); + +static struct platform_driver atmel_sfr_driver = { + .probe = atmel_sfr_probe, + .driver = { + .name = "atmel-sfr", + .of_match_table = atmel_sfr_dt_ids, + }, +}; +module_platform_driver(atmel_sfr_driver); + +MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); +MODULE_DESCRIPTION("Atmel SFR SN driver for SAMA5D2/4 SoC family"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 3b8db0348c503823fb09b5f304b196c3362754ea Mon Sep 17 00:00:00 2001 From: Ran Wang <ran.wang_1@nxp.com> Date: Thu, 24 Oct 2019 17:26:44 +0800 Subject: soc: fsl: add RCPM driver The NXP's QorIQ processors based on ARM Core have RCPM module (Run Control and Power Management), which performs system level tasks associated with power management such as wakeup source control. Note that this driver will not support PowerPC based QorIQ processors, and it depends on PM wakeup source framework which provide collect wake information. Signed-off-by: Ran Wang <ran.wang_1@nxp.com> Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Li Yang <leoyang.li@nxp.com> --- drivers/soc/fsl/Kconfig | 10 ++++ drivers/soc/fsl/Makefile | 1 + drivers/soc/fsl/rcpm.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 drivers/soc/fsl/rcpm.c (limited to 'drivers/soc') diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig index f9ad8ad54a7d..4df32bc4c7a6 100644 --- a/drivers/soc/fsl/Kconfig +++ b/drivers/soc/fsl/Kconfig @@ -40,4 +40,14 @@ config DPAA2_CONSOLE /dev/dpaa2_mc_console and /dev/dpaa2_aiop_console, which can be used to dump the Management Complex and AIOP firmware logs. + +config FSL_RCPM + bool "Freescale RCPM support" + depends on PM_SLEEP && (ARM || ARM64) + help + The NXP QorIQ Processors based on ARM Core have RCPM module + (Run Control and Power Management), which performs all device-level + tasks associated with power management, such as wakeup source control. + Note that currently this driver will not support PowerPC based + QorIQ processor. endmenu diff --git a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile index 71dee8d0d1f0..906f1cd8af01 100644 --- a/drivers/soc/fsl/Makefile +++ b/drivers/soc/fsl/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_FSL_DPAA) += qbman/ obj-$(CONFIG_QUICC_ENGINE) += qe/ obj-$(CONFIG_CPM) += qe/ +obj-$(CONFIG_FSL_RCPM) += rcpm.o obj-$(CONFIG_FSL_GUTS) += guts.o obj-$(CONFIG_FSL_MC_DPIO) += dpio/ obj-$(CONFIG_DPAA2_CONSOLE) += dpaa2-console.o diff --git a/drivers/soc/fsl/rcpm.c b/drivers/soc/fsl/rcpm.c new file mode 100644 index 000000000000..a093dbe6d2cb --- /dev/null +++ b/drivers/soc/fsl/rcpm.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// rcpm.c - Freescale QorIQ RCPM driver +// +// Copyright 2019 NXP +// +// Author: Ran Wang <ran.wang_1@nxp.com> + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/suspend.h> +#include <linux/kernel.h> + +#define RCPM_WAKEUP_CELL_MAX_SIZE 7 + +struct rcpm { + unsigned int wakeup_cells; + void __iomem *ippdexpcr_base; + bool little_endian; +}; + +/** + * rcpm_pm_prepare - performs device-level tasks associated with power + * management, such as programming related to the wakeup source control. + * @dev: Device to handle. + * + */ +static int rcpm_pm_prepare(struct device *dev) +{ + int i, ret, idx; + void __iomem *base; + struct wakeup_source *ws; + struct rcpm *rcpm; + struct device_node *np = dev->of_node; + u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1]; + u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0}; + + rcpm = dev_get_drvdata(dev); + if (!rcpm) + return -EINVAL; + + base = rcpm->ippdexpcr_base; + idx = wakeup_sources_read_lock(); + + /* Begin with first registered wakeup source */ + for_each_wakeup_source(ws) { + + /* skip object which is not attached to device */ + if (!ws->dev || !ws->dev->parent) + continue; + + ret = device_property_read_u32_array(ws->dev->parent, + "fsl,rcpm-wakeup", value, + rcpm->wakeup_cells + 1); + + /* Wakeup source should refer to current rcpm device */ + if (ret || (np->phandle != value[0])) + continue; + + /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the + * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup" + * of wakeup source IP contains an integer array: <phandle to + * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting, + * IPPDEXPCR2 setting, etc>. + * + * So we will go thought them to collect setting data. + */ + for (i = 0; i < rcpm->wakeup_cells; i++) + setting[i] |= value[i + 1]; + } + + wakeup_sources_read_unlock(idx); + + /* Program all IPPDEXPCRn once */ + for (i = 0; i < rcpm->wakeup_cells; i++) { + u32 tmp = setting[i]; + void __iomem *address = base + i * 4; + + if (!tmp) + continue; + + /* We can only OR related bits */ + if (rcpm->little_endian) { + tmp |= ioread32(address); + iowrite32(tmp, address); + } else { + tmp |= ioread32be(address); + iowrite32be(tmp, address); + } + } + + return 0; +} + +static const struct dev_pm_ops rcpm_pm_ops = { + .prepare = rcpm_pm_prepare, +}; + +static int rcpm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *r; + struct rcpm *rcpm; + int ret; + + rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL); + if (!rcpm) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(rcpm->ippdexpcr_base)) { + ret = PTR_ERR(rcpm->ippdexpcr_base); + return ret; + } + + rcpm->little_endian = device_property_read_bool( + &pdev->dev, "little-endian"); + + ret = device_property_read_u32(&pdev->dev, + "#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells); + if (ret) + return ret; + + dev_set_drvdata(&pdev->dev, rcpm); + + return 0; +} + +static const struct of_device_id rcpm_of_match[] = { + { .compatible = "fsl,qoriq-rcpm-2.1+", }, + {} +}; +MODULE_DEVICE_TABLE(of, rcpm_of_match); + +static struct platform_driver rcpm_driver = { + .driver = { + .name = "rcpm", + .of_match_table = rcpm_of_match, + .pm = &rcpm_pm_ops, + }, + .probe = rcpm_probe, +}; + +module_platform_driver(rcpm_driver); -- cgit v1.2.3