* [PATCH v4 1/7] firmware: smccc: Add HAVE_ARM_SMCCC_DISCOVERY to identify SMCCC v1.1 and above
From: Sudeep Holla @ 2020-05-18 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Lorenzo Pieralisi, Arnd Bergmann, Catalin Marinas,
linux-kernel, Steven Price, harb, Sudeep Holla, Will Deacon
In-Reply-To: <20200518091222.27467-1-sudeep.holla@arm.com>
SMCCC v1.0 lacked discoverability of version and features. To accelerate
adoption of few mitigations and protect systems more rapidly from various
vulnerability, PSCI v1.0 was updated to add SMCCC discovery mechanism
though the PSCI firmware implementation of PSCI_FEATURES(SMCCC_VERSION)
which returns success on firmware compliant to SMCCC v1.1 and above.
This inturn makes SMCCC v1.1 and above dependent on ARM_PSCI_FW for
backward compatibility. Let us introduce a new hidden config for the
same to build more features on top of SMCCC v1.1 and above.
While at it, also sort alphabetically the psci entry.
Tested-by: Etienne Carriere <etienne.carriere@st.com>
Reviewed-by: Etienne Carriere <etienne.carriere@st.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
drivers/firmware/Kconfig | 6 ++----
drivers/firmware/smccc/Kconfig | 16 ++++++++++++++++
2 files changed, 18 insertions(+), 4 deletions(-)
create mode 100644 drivers/firmware/smccc/Kconfig
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 8007d4aa76dc..4843e94713a4 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -295,15 +295,13 @@ config TURRIS_MOX_RWTM
other manufacturing data and also utilize the Entropy Bit Generator
for hardware random number generation.
-config HAVE_ARM_SMCCC
- bool
-
-source "drivers/firmware/psci/Kconfig"
source "drivers/firmware/broadcom/Kconfig"
source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
source "drivers/firmware/imx/Kconfig"
source "drivers/firmware/meson/Kconfig"
+source "drivers/firmware/psci/Kconfig"
+source "drivers/firmware/smccc/Kconfig"
source "drivers/firmware/tegra/Kconfig"
source "drivers/firmware/xilinx/Kconfig"
diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig
new file mode 100644
index 000000000000..27b675d76235
--- /dev/null
+++ b/drivers/firmware/smccc/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config HAVE_ARM_SMCCC
+ bool
+ help
+ Include support for the Secure Monitor Call (SMC) and Hypervisor
+ Call (HVC) instructions on Armv7 and above architectures.
+
+config HAVE_ARM_SMCCC_DISCOVERY
+ bool
+ depends on ARM_PSCI_FW
+ default y
+ help
+ SMCCC v1.0 lacked discoverability and hence PSCI v1.0 was updated
+ to add SMCCC discovery mechanism though the PSCI firmware
+ implementation of PSCI_FEATURES(SMCCC_VERSION) which returns
+ success on firmware compliant to SMCCC v1.1 and above.
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v4 2/7] firmware: smccc: Update link to latest SMCCC specification
From: Sudeep Holla @ 2020-05-18 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Lorenzo Pieralisi, Arnd Bergmann, Catalin Marinas,
linux-kernel, Steven Price, harb, Sudeep Holla, Will Deacon
In-Reply-To: <20200518091222.27467-1-sudeep.holla@arm.com>
The current link gets redirected to the revision B published in November
2016 though it actually points to the original revision A published in
June 2013.
Let us update the link to point to the latest version, so that it
doesn't get stale anytime soon. Currently it points to v1.2 published in
March 2020(i.e. DEN0028C).
Tested-by: Etienne Carriere <etienne.carriere@st.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Etienne Carriere <etienne.carriere@st.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
include/linux/arm-smccc.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 59494df0f55b..31b15db9685d 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -10,7 +10,9 @@
/*
* This file provides common defines for ARM SMC Calling Convention as
* specified in
- * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
+ * https://developer.arm.com/docs/den0028/latest
+ *
+ * This code is up-to-date with version DEN 0028 B
*/
#define ARM_SMCCC_STD_CALL _AC(0,U)
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v4 3/7] firmware: smccc: Add the definition for SMCCCv1.2 version/error codes
From: Sudeep Holla @ 2020-05-18 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Lorenzo Pieralisi, Arnd Bergmann, Catalin Marinas,
linux-kernel, Steven Price, harb, Sudeep Holla, Will Deacon
In-Reply-To: <20200518091222.27467-1-sudeep.holla@arm.com>
Add the definition for SMCCC v1.2 version and new error code added.
While at it, also add a note that ARM DEN 0070A is deprecated and is
now merged into the main SMCCC specification(ARM DEN 0028C).
Tested-by: Etienne Carriere <etienne.carriere@st.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Etienne Carriere <etienne.carriere@st.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
include/linux/arm-smccc.h | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 31b15db9685d..c3784ba8e2a4 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -12,7 +12,7 @@
* specified in
* https://developer.arm.com/docs/den0028/latest
*
- * This code is up-to-date with version DEN 0028 B
+ * This code is up-to-date with version DEN 0028 C
*/
#define ARM_SMCCC_STD_CALL _AC(0,U)
@@ -58,6 +58,7 @@
#define ARM_SMCCC_VERSION_1_0 0x10000
#define ARM_SMCCC_VERSION_1_1 0x10001
+#define ARM_SMCCC_VERSION_1_2 0x10002
#define ARM_SMCCC_VERSION_FUNC_ID \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
@@ -316,10 +317,14 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
*/
#define arm_smccc_1_1_hvc(...) __arm_smccc_1_1(SMCCC_HVC_INST, __VA_ARGS__)
-/* Return codes defined in ARM DEN 0070A */
+/*
+ * Return codes defined in ARM DEN 0070A
+ * ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C
+ */
#define SMCCC_RET_SUCCESS 0
#define SMCCC_RET_NOT_SUPPORTED -1
#define SMCCC_RET_NOT_REQUIRED -2
+#define SMCCC_RET_INVALID_PARAMETER -3
/*
* Like arm_smccc_1_1* but always returns SMCCC_RET_NOT_SUPPORTED.
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v4 4/7] firmware: smccc: Drop smccc_version enum and use ARM_SMCCC_VERSION_1_x instead
From: Sudeep Holla @ 2020-05-18 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Lorenzo Pieralisi, Arnd Bergmann, Catalin Marinas,
linux-kernel, Steven Price, harb, Sudeep Holla, Will Deacon
In-Reply-To: <20200518091222.27467-1-sudeep.holla@arm.com>
Instead of maintaining 2 sets of enums/macros for tracking SMCCC version,
let us drop smccc_version enum and use ARM_SMCCC_VERSION_1_x directly
instead.
This is in preparation to drop smccc_version here and move it separately
under drivers/firmware/smccc.
Tested-by: Etienne Carriere <etienne.carriere@st.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Etienne Carriere <etienne.carriere@st.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
arch/arm64/kernel/paravirt.c | 2 +-
drivers/firmware/psci/psci.c | 8 ++++----
include/linux/psci.h | 7 +------
3 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/kernel/paravirt.c b/arch/arm64/kernel/paravirt.c
index 1ef702b0be2d..295d66490584 100644
--- a/arch/arm64/kernel/paravirt.c
+++ b/arch/arm64/kernel/paravirt.c
@@ -120,7 +120,7 @@ static bool has_pv_steal_clock(void)
struct arm_smccc_res res;
/* To detect the presence of PV time support we require SMCCC 1.1+ */
- if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
+ if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
return false;
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 2937d44b5df4..6a56d7196697 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -54,12 +54,12 @@ bool psci_tos_resident_on(int cpu)
struct psci_operations psci_ops = {
.conduit = SMCCC_CONDUIT_NONE,
- .smccc_version = SMCCC_VERSION_1_0,
+ .smccc_version = ARM_SMCCC_VERSION_1_0,
};
enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
{
- if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
+ if (psci_ops.smccc_version < ARM_SMCCC_VERSION_1_1)
return SMCCC_CONDUIT_NONE;
return psci_ops.conduit;
@@ -411,8 +411,8 @@ static void __init psci_init_smccc(void)
if (feature != PSCI_RET_NOT_SUPPORTED) {
u32 ret;
ret = invoke_psci_fn(ARM_SMCCC_VERSION_FUNC_ID, 0, 0, 0);
- if (ret == ARM_SMCCC_VERSION_1_1) {
- psci_ops.smccc_version = SMCCC_VERSION_1_1;
+ if (ret >= ARM_SMCCC_VERSION_1_1) {
+ psci_ops.smccc_version = ret;
ver = ret;
}
}
diff --git a/include/linux/psci.h b/include/linux/psci.h
index a67712b73b6c..29bd0671e5bb 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -21,11 +21,6 @@ bool psci_power_state_is_valid(u32 state);
int psci_set_osi_mode(void);
bool psci_has_osi_support(void);
-enum smccc_version {
- SMCCC_VERSION_1_0,
- SMCCC_VERSION_1_1,
-};
-
struct psci_operations {
u32 (*get_version)(void);
int (*cpu_suspend)(u32 state, unsigned long entry_point);
@@ -36,7 +31,7 @@ struct psci_operations {
unsigned long lowest_affinity_level);
int (*migrate_info_type)(void);
enum arm_smccc_conduit conduit;
- enum smccc_version smccc_version;
+ u32 smccc_version;
};
extern struct psci_operations psci_ops;
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v4 5/7] firmware: smccc: Refactor SMCCC specific bits into separate file
From: Sudeep Holla @ 2020-05-18 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Lorenzo Pieralisi, Arnd Bergmann, Catalin Marinas,
linux-kernel, Steven Price, harb, Sudeep Holla, Will Deacon
In-Reply-To: <20200518091222.27467-1-sudeep.holla@arm.com>
In order to add newer SMCCC v1.1+ functionality and to avoid cluttering
PSCI firmware driver with SMCCC bits, let us move the SMCCC specific
details under drivers/firmware/smccc/smccc.c
We can also drop conduit and smccc_version from psci_operations structure
as SMCCC was the sole user and now it maintains those.
No functionality change in this patch though.
Tested-by: Etienne Carriere <etienne.carriere@st.com>
Reviewed-by: Etienne Carriere <etienne.carriere@st.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
MAINTAINERS | 9 +++++++++
drivers/firmware/Makefile | 3 ++-
drivers/firmware/psci/psci.c | 20 +++++---------------
drivers/firmware/smccc/Makefile | 3 +++
drivers/firmware/smccc/smccc.c | 26 ++++++++++++++++++++++++++
include/linux/psci.h | 2 --
6 files changed, 45 insertions(+), 18 deletions(-)
create mode 100644 drivers/firmware/smccc/Makefile
create mode 100644 drivers/firmware/smccc/smccc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ecc0749810b0..2df80272b35e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15467,6 +15467,15 @@ M: Nicolas Pitre <nico@fluxnic.net>
S: Odd Fixes
F: drivers/net/ethernet/smsc/smc91x.*
+SECURE MONITOR CALL(SMC) CALLING CONVENTION (SMCCC)
+M: Mark Rutland <mark.rutland@arm.com>
+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+M: Sudeep Holla <sudeep.holla@arm.com>
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: drivers/firmware/smccc/
+F: include/linux/arm-smccc.h
+
SMIA AND SMIA++ IMAGE SENSOR DRIVER
M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index e9fb838af4df..99510be9f5ed 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -23,12 +23,13 @@ obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o
obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/
-obj-y += psci/
obj-y += broadcom/
obj-y += meson/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_UEFI_CPER) += efi/
obj-y += imx/
+obj-y += psci/
+obj-y += smccc/
obj-y += tegra/
obj-y += xilinx/
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 6a56d7196697..1330a698a178 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -46,25 +46,14 @@
* require cooperation with a Trusted OS driver.
*/
static int resident_cpu = -1;
+struct psci_operations psci_ops;
+static enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE;
bool psci_tos_resident_on(int cpu)
{
return cpu == resident_cpu;
}
-struct psci_operations psci_ops = {
- .conduit = SMCCC_CONDUIT_NONE,
- .smccc_version = ARM_SMCCC_VERSION_1_0,
-};
-
-enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
-{
- if (psci_ops.smccc_version < ARM_SMCCC_VERSION_1_1)
- return SMCCC_CONDUIT_NONE;
-
- return psci_ops.conduit;
-}
-
typedef unsigned long (psci_fn)(unsigned long, unsigned long,
unsigned long, unsigned long);
static psci_fn *invoke_psci_fn;
@@ -90,6 +79,7 @@ static u32 psci_function_id[PSCI_FN_MAX];
static u32 psci_cpu_suspend_feature;
static bool psci_system_reset2_supported;
+void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit);
static inline bool psci_has_ext_power_state(void)
{
@@ -242,7 +232,7 @@ static void set_conduit(enum arm_smccc_conduit conduit)
WARN(1, "Unexpected PSCI conduit %d\n", conduit);
}
- psci_ops.conduit = conduit;
+ psci_conduit = conduit;
}
static int get_set_conduit_method(struct device_node *np)
@@ -412,7 +402,7 @@ static void __init psci_init_smccc(void)
u32 ret;
ret = invoke_psci_fn(ARM_SMCCC_VERSION_FUNC_ID, 0, 0, 0);
if (ret >= ARM_SMCCC_VERSION_1_1) {
- psci_ops.smccc_version = ret;
+ arm_smccc_version_init(ret, psci_conduit);
ver = ret;
}
}
diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile
new file mode 100644
index 000000000000..6f369fe3f0b9
--- /dev/null
+++ b/drivers/firmware/smccc/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
new file mode 100644
index 000000000000..de92a4b9f8f6
--- /dev/null
+++ b/drivers/firmware/smccc/smccc.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020 Arm Limited
+ */
+
+#define pr_fmt(fmt) "smccc: " fmt
+
+#include <linux/init.h>
+#include <linux/arm-smccc.h>
+
+static u32 smccc_version = ARM_SMCCC_VERSION_1_0;
+static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE;
+
+void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
+{
+ smccc_version = version;
+ smccc_conduit = conduit;
+}
+
+enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
+{
+ if (smccc_version < ARM_SMCCC_VERSION_1_1)
+ return SMCCC_CONDUIT_NONE;
+
+ return smccc_conduit;
+}
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 29bd0671e5bb..14ad9b9ebcd6 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -30,8 +30,6 @@ struct psci_operations {
int (*affinity_info)(unsigned long target_affinity,
unsigned long lowest_affinity_level);
int (*migrate_info_type)(void);
- enum arm_smccc_conduit conduit;
- u32 smccc_version;
};
extern struct psci_operations psci_ops;
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v4 6/7] firmware: smccc: Add function to fetch SMCCC version
From: Sudeep Holla @ 2020-05-18 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Lorenzo Pieralisi, Arnd Bergmann, Catalin Marinas,
linux-kernel, Steven Price, harb, Sudeep Holla, Will Deacon
In-Reply-To: <20200518091222.27467-1-sudeep.holla@arm.com>
For backward compatibility reasons, PSCI maintains SMCCC version as
SMCCC didn't provide ARM_SMCCC_VERSION_FUNC_ID until v1.1.
PSCI initialises both the SMCCC version and conduit. Similar to the
conduit, let us provide accessors to fetch the SMCCC version also so
that other SMCCC v1.1+ features can use it.
Tested-by: Etienne Carriere <etienne.carriere@st.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Etienne Carriere <etienne.carriere@st.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
drivers/firmware/smccc/smccc.c | 5 +++++
include/linux/arm-smccc.h | 11 +++++++++++
2 files changed, 16 insertions(+)
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
index de92a4b9f8f6..4e80921ee212 100644
--- a/drivers/firmware/smccc/smccc.c
+++ b/drivers/firmware/smccc/smccc.c
@@ -24,3 +24,8 @@ enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
return smccc_conduit;
}
+
+u32 arm_smccc_get_version(void)
+{
+ return smccc_version;
+}
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index c3784ba8e2a4..c491d210e3c3 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -100,6 +100,17 @@ enum arm_smccc_conduit {
*/
enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void);
+/**
+ * arm_smccc_get_version()
+ *
+ * Returns the version to be used for SMCCCv1.1 or later.
+ *
+ * When SMCCCv1.1 or above is not present, returns SMCCCv1.0, but this
+ * does not imply the presence of firmware or a valid conduit. Caller
+ * handling SMCCCv1.0 must determine the conduit by other means.
+ */
+u32 arm_smccc_get_version(void);
+
/**
* struct arm_smccc_res - Result from SMC/HVC call
* @a0-a3 result values from registers 0 to 3
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v4 7/7] firmware: smccc: Add ARCH_SOC_ID support
From: Sudeep Holla @ 2020-05-18 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Mark Rutland, Lorenzo Pieralisi, Arnd Bergmann, Catalin Marinas,
linux-kernel, Steven Price, harb, Sudeep Holla, Will Deacon
In-Reply-To: <20200518091222.27467-1-sudeep.holla@arm.com>
SMCCC v1.2 adds a new optional function SMCCC_ARCH_SOC_ID to obtain a
SiP defined SoC identification value. Add support for the same.
Also using the SoC bus infrastructure, let us expose the platform
specific SoC atrributes under sysfs. We also provide custom sysfs for
the vendor ID as JEP-106 bank and identification code.
Tested-by: Etienne Carriere <etienne.carriere@st.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Acked-by: Etienne Carriere <etienne.carriere@st.com>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
---
drivers/firmware/smccc/Kconfig | 9 ++
drivers/firmware/smccc/Makefile | 1 +
drivers/firmware/smccc/soc_id.c | 151 ++++++++++++++++++++++++++++++++
include/linux/arm-smccc.h | 5 ++
4 files changed, 166 insertions(+)
create mode 100644 drivers/firmware/smccc/soc_id.c
diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig
index 27b675d76235..15e7466179a6 100644
--- a/drivers/firmware/smccc/Kconfig
+++ b/drivers/firmware/smccc/Kconfig
@@ -14,3 +14,12 @@ config HAVE_ARM_SMCCC_DISCOVERY
to add SMCCC discovery mechanism though the PSCI firmware
implementation of PSCI_FEATURES(SMCCC_VERSION) which returns
success on firmware compliant to SMCCC v1.1 and above.
+
+config ARM_SMCCC_SOC_ID
+ bool "SoC bus device for the ARM SMCCC SOC_ID"
+ depends on HAVE_ARM_SMCCC_DISCOVERY
+ default y
+ select SOC_BUS
+ help
+ Include support for the SoC bus on the ARM SMCCC firmware based
+ platforms providing some sysfs information about the SoC variant.
diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile
index 6f369fe3f0b9..72ab84042832 100644
--- a/drivers/firmware/smccc/Makefile
+++ b/drivers/firmware/smccc/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
#
obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o
+obj-$(CONFIG_ARM_SMCCC_SOC_ID) += soc_id.o
diff --git a/drivers/firmware/smccc/soc_id.c b/drivers/firmware/smccc/soc_id.c
new file mode 100644
index 000000000000..7e59e95e1fd3
--- /dev/null
+++ b/drivers/firmware/smccc/soc_id.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020 Arm Limited
+ */
+
+#define pr_fmt(fmt) "SMCCC: SOC_ID: " fmt
+
+#include <linux/arm-smccc.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+
+#define SMCCC_SOC_ID_JEP106_BANK_IDX_MASK GENMASK(30, 24)
+/*
+ * As per the SMC Calling Convention specification v1.2 (ARM DEN 0028C)
+ * Section 7.4 SMCCC_ARCH_SOC_ID bits[23:16] are JEP-106 identification
+ * code with parity bit for the SiP. We can drop the parity bit.
+ */
+#define SMCCC_SOC_ID_JEP106_ID_CODE_MASK GENMASK(22, 16)
+#define SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK GENMASK(15, 0)
+
+/* The bank index is equal to the for continuation code bank number - 1 */
+#define JEP106_BANK_CONT_CODE(x) \
+ (u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_BANK_IDX_MASK, (x)) + 1)
+#define JEP106_ID_CODE(x) \
+ (u8)(FIELD_GET(SMCCC_SOC_ID_JEP106_ID_CODE_MASK, (x)))
+#define IMP_DEF_SOC_ID(x) \
+ (u16)(FIELD_GET(SMCCC_SOC_ID_IMP_DEF_SOC_ID_MASK, (x)))
+
+static int soc_id_version;
+static struct soc_device *soc_dev;
+static struct soc_device_attribute *soc_dev_attr;
+
+static ssize_t
+jep106_cont_bank_code_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "0x%02x\n", JEP106_BANK_CONT_CODE(soc_id_version));
+}
+
+static DEVICE_ATTR_RO(jep106_cont_bank_code);
+
+static ssize_t
+jep106_identification_code_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0x%02x\n", JEP106_ID_CODE(soc_id_version));
+}
+
+static DEVICE_ATTR_RO(jep106_identification_code);
+
+static struct attribute *jep106_id_attrs[] = {
+ &dev_attr_jep106_cont_bank_code.attr,
+ &dev_attr_jep106_identification_code.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(jep106_id);
+
+static int __init smccc_soc_init(void)
+{
+ struct device *dev;
+ int ret, soc_id_rev;
+ struct arm_smccc_res res;
+ static char soc_id_str[8], soc_id_rev_str[12];
+
+ if (arm_smccc_get_version() < ARM_SMCCC_VERSION_1_2)
+ return 0;
+
+ if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) {
+ pr_err("%s: invalid SMCCC conduit\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
+ ARM_SMCCC_ARCH_SOC_ID, &res);
+
+ if (res.a0 == SMCCC_RET_NOT_SUPPORTED) {
+ pr_info("ARCH_SOC_ID not implemented, skipping ....\n");
+ return 0;
+ }
+
+ if ((int)res.a0 < 0) {
+ pr_info("ARCH_FEATURES(ARCH_SOC_ID) returned error: %lx\n",
+ res.a0);
+ return -EINVAL;
+ }
+
+ arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 0, &res);
+ if ((int)res.a0 < 0) {
+ pr_err("ARCH_SOC_ID(0) returned error: %lx\n", res.a0);
+ return -EINVAL;
+ }
+
+ soc_id_version = res.a0;
+
+ arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
+ if ((int)res.a0 < 0) {
+ pr_err("ARCH_SOC_ID(1) returned error: %lx\n", res.a0);
+ return -EINVAL;
+ }
+
+ soc_id_rev = res.a0;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (!soc_dev_attr)
+ return -ENOMEM;
+
+ sprintf(soc_id_str, "0x%04x", IMP_DEF_SOC_ID(soc_id_version));
+ sprintf(soc_id_rev_str, "0x%08x", soc_id_rev);
+
+ soc_dev_attr->soc_id = soc_id_str;
+ soc_dev_attr->revision = soc_id_rev_str;
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ ret = PTR_ERR(soc_dev);
+ goto free_soc;
+ }
+
+ dev = soc_device_to_device(soc_dev);
+
+ ret = devm_device_add_groups(dev, jep106_id_groups);
+ if (ret) {
+ dev_err(dev, "sysfs create failed: %d\n", ret);
+ goto unregister_soc;
+ }
+
+ pr_info("ID = %s Revision = %s\n", soc_dev_attr->soc_id,
+ soc_dev_attr->revision);
+
+ return 0;
+
+unregister_soc:
+ soc_device_unregister(soc_dev);
+free_soc:
+ kfree(soc_dev_attr);
+ return ret;
+}
+module_init(smccc_soc_init);
+
+static void __exit smccc_soc_exit(void)
+{
+ if (soc_dev)
+ soc_device_unregister(soc_dev);
+ kfree(soc_dev_attr);
+}
+module_exit(smccc_soc_exit);
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index c491d210e3c3..6510f1bfcb05 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -70,6 +70,11 @@
ARM_SMCCC_SMC_32, \
0, 1)
+#define ARM_SMCCC_ARCH_SOC_ID \
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
+ ARM_SMCCC_SMC_32, \
+ 0, 2)
+
#define ARM_SMCCC_ARCH_WORKAROUND_1 \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
ARM_SMCCC_SMC_32, \
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH] iio: stm32-dac: Replace indio_dev->mlock with own device lock
From: Fabrice Gasnier @ 2020-05-18 9:22 UTC (permalink / raw)
To: Jonathan Cameron, Sergiu Cuciurean
Cc: Lars-Peter Clausen, Peter Meerwald-Stadler, linux-iio,
linux-kernel, Maxime Coquelin, Hartmut Knaack, linux-stm32,
linux-arm-kernel, Alexandre Torgue
In-Reply-To: <20200516163521.2812cf86@archlinux>
On 5/16/20 5:35 PM, Jonathan Cameron wrote:
> On Thu, 14 May 2020 11:50:12 +0300
> Sergiu Cuciurean <sergiu.cuciurean@analog.com> wrote:
>
>> As part of the general cleanup of indio_dev->mlock, this change replaces
>> it with a local lock on the device's state structure.
>>
>> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
>> ---
>> drivers/iio/dac/stm32-dac.c | 12 ++++++++----
>> 1 file changed, 8 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c
>> index f22c1d9129b2..74b9474c8590 100644
>> --- a/drivers/iio/dac/stm32-dac.c
>> +++ b/drivers/iio/dac/stm32-dac.c
>> @@ -26,9 +26,11 @@
>> /**
>> * struct stm32_dac - private data of DAC driver
>> * @common: reference to DAC common data
>> + * @lock: lock to protect the data buffer during regmap ops
>
> In this particular case I'm not sure that's what mlock was being used for.
> I think it's about avoiding races around checking if powered down and
> actually doing it.
Hi Sergiu,
Indeed, purpose is to protect against a race here when reading CR, and
updating it via regmap (this also makes the subsequent pm_runtime calls
to be balanced based on this).
(Side note: there is no data buffer involved for the DAC.)
Could you please update the comment ?
Thanks,
Fabrice
>
>
>> */
>> struct stm32_dac {
>> struct stm32_dac_common *common;
>> + struct mutex lock;
>> };
>>
>> static int stm32_dac_is_enabled(struct iio_dev *indio_dev, int channel)
>> @@ -58,10 +60,10 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
>> int ret;
>>
>> /* already enabled / disabled ? */
>> - mutex_lock(&indio_dev->mlock);
>> + mutex_lock(&dac->lock);
>> ret = stm32_dac_is_enabled(indio_dev, ch);
>> if (ret < 0 || enable == !!ret) {
>> - mutex_unlock(&indio_dev->mlock);
>> + mutex_unlock(&dac->lock);
>> return ret < 0 ? ret : 0;
>> }
>>
>> @@ -69,13 +71,13 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
>> ret = pm_runtime_get_sync(dev);
>> if (ret < 0) {
>> pm_runtime_put_noidle(dev);
>> - mutex_unlock(&indio_dev->mlock);
>> + mutex_unlock(&dac->lock);
>> return ret;
>> }
>> }
>>
>> ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
>> - mutex_unlock(&indio_dev->mlock);
>> + mutex_unlock(&dac->lock);
>> if (ret < 0) {
>> dev_err(&indio_dev->dev, "%s failed\n", en ?
>> "Enable" : "Disable");
>> @@ -328,6 +330,8 @@ static int stm32_dac_probe(struct platform_device *pdev)
>> indio_dev->info = &stm32_dac_iio_info;
>> indio_dev->modes = INDIO_DIRECT_MODE;
>>
>> + mutex_init(&dac->lock);
>> +
>> ret = stm32_dac_chan_of_init(indio_dev);
>> if (ret < 0)
>> return ret;
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v8 0/3] PM / AVS: SVS: Introduce SVS engine
From: Roger Lu @ 2020-05-18 9:24 UTC (permalink / raw)
To: Kevin Hilman, Rob Herring, Nicolas Boichat, Stephen Boyd
Cc: Mark Rutland, Nishanth Menon, Angus Lin, devicetree, linux-pm,
Roger Lu, linux-kernel, Xiaoqing Liu, YT Lee, Fan Chen,
linux-mediatek, HenryC Chen, Charles Yang, Matthias Brugger,
linux-arm-kernel
1. SVS driver use OPP adjust event in [1] to update OPP table voltage part.
2. SVS dts node refers to CPU opp table [2] and GPU opp table [3].
3. SVS and thermal dts use the same thermal efuse [4].
4. SVS dts needs power-domain [5][6] and PMIC regulator [7].
[1] https://patchwork.kernel.org/patch/11193513/
[2] https://patchwork.kernel.org/patch/11304935/
[3] https://patchwork.kernel.org/patch/11423009/
[4] https://patchwork.kernel.org/patch/11316495/
[5] https://lore.kernel.org/patchwork/patch/1236875/
[6] https://lore.kernel.org/patchwork/patch/1236878/
[7] https://patchwork.kernel.org/patch/11284617/
pending discussion:
- SVS sub-node architecture concern in below patch.
https://lore.kernel.org/patchwork/patch/1175994/
https://lore.kernel.org/patchwork/patch/1175994/
changes since v7:
- Add "mtk_svs.yaml" for device-tree binding document.
- Add svs_add_thermal_device_link() in driver for svs device (consumer)
to link thermal device (supplier).
- replace pm_qos_add_request() with cpu_latency_qos_add_request() for
letting CPUs leave idle-off state.
Roger Lu (3):
dt-bindings: power: avs: add mtk svs dt-bindings
arm64: dts: mt8183: add svs device information
PM / AVS: SVS: Introduce SVS engine
.../bindings/power/avs/mtk_svs.yaml | 141 ++
arch/arm64/boot/dts/mediatek/mt8183-evb.dts | 16 +
arch/arm64/boot/dts/mediatek/mt8183.dtsi | 41 +
drivers/power/avs/Kconfig | 10 +
drivers/power/avs/Makefile | 1 +
drivers/power/avs/mtk_svs.c | 2119 +++++++++++++++++
include/linux/power/mtk_svs.h | 23 +
7 files changed, 2351 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/avs/mtk_svs.yaml
create mode 100644 drivers/power/avs/mtk_svs.c
create mode 100644 include/linux/power/mtk_svs.h
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v8 3/3] PM / AVS: SVS: Introduce SVS engine
From: Roger Lu @ 2020-05-18 9:24 UTC (permalink / raw)
To: Kevin Hilman, Rob Herring, Nicolas Boichat, Stephen Boyd
Cc: Mark Rutland, Nishanth Menon, Angus Lin, devicetree, linux-pm,
Roger Lu, linux-kernel, Xiaoqing Liu, YT Lee, Fan Chen,
linux-mediatek, HenryC Chen, Charles Yang, Matthias Brugger,
linux-arm-kernel
In-Reply-To: <20200518092403.22647-1-roger.lu@mediatek.com>
The SVS (Smart Voltage Scaling) engine is a piece
of hardware which is used to calculate optimized
voltage values of several power domains,
e.g. CPU/GPU/CCI, according to chip process corner,
temperatures, and other factors. Then DVFS driver
could apply those optimized voltage values to reduce
power consumption.
Signed-off-by: Roger Lu <roger.lu@mediatek.com>
---
drivers/power/avs/Kconfig | 10 +
drivers/power/avs/Makefile | 1 +
drivers/power/avs/mtk_svs.c | 2119 +++++++++++++++++++++++++++++++++
include/linux/power/mtk_svs.h | 23 +
4 files changed, 2153 insertions(+)
create mode 100644 drivers/power/avs/mtk_svs.c
create mode 100644 include/linux/power/mtk_svs.h
diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig
index cdb4237bfd02..67089ac6040e 100644
--- a/drivers/power/avs/Kconfig
+++ b/drivers/power/avs/Kconfig
@@ -35,3 +35,13 @@ config ROCKCHIP_IODOMAIN
Say y here to enable support io domains on Rockchip SoCs. It is
necessary for the io domain setting of the SoC to match the
voltage supplied by the regulators.
+
+config MTK_SVS
+ bool "MediaTek Smart Voltage Scaling(SVS)"
+ depends on POWER_AVS && MTK_EFUSE && NVMEM
+ help
+ The SVS engine is a piece of hardware which is used to calculate
+ optimized voltage values of several power domains, e.g.
+ CPU clusters/GPU/CCI, according to chip process corner, temperatures,
+ and other factors. Then DVFS driver could apply those optimized voltage
+ values to reduce power consumption.
diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile
index 9007d05853e2..231adf078582 100644
--- a/drivers/power/avs/Makefile
+++ b/drivers/power/avs/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o
obj-$(CONFIG_QCOM_CPR) += qcom-cpr.o
obj-$(CONFIG_ROCKCHIP_IODOMAIN) += rockchip-io-domain.o
+obj-$(CONFIG_MTK_SVS) += mtk_svs.o
diff --git a/drivers/power/avs/mtk_svs.c b/drivers/power/avs/mtk_svs.c
new file mode 100644
index 000000000000..a4083b3ef175
--- /dev/null
+++ b/drivers/power/avs/mtk_svs.c
@@ -0,0 +1,2119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 MediaTek Inc.
+ */
+
+#define pr_fmt(fmt) "[mtk_svs] " fmt
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_qos.h>
+#include <linux/pm_runtime.h>
+#include <linux/power/mtk_svs.h>
+#include <linux/proc_fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/thermal.h>
+#include <linux/uaccess.h>
+
+/* svs 1-line sw id */
+#define SVS_CPU_LITTLE BIT(0)
+#define SVS_CPU_BIG BIT(1)
+#define SVS_CCI BIT(2)
+#define SVS_GPU BIT(3)
+
+/* svs bank mode support */
+#define SVSB_MODE_ALL_DISABLE (0)
+#define SVSB_MODE_INIT01 BIT(1)
+#define SVSB_MODE_INIT02 BIT(2)
+#define SVSB_MODE_MON BIT(3)
+
+/* svs bank init01 condition */
+#define SVSB_INIT01_VOLT_IGNORE BIT(1)
+#define SVSB_INIT01_VOLT_INC_ONLY BIT(2)
+
+/* svs bank common setting */
+#define HIGH_TEMP_MAX (U32_MAX)
+#define RUNCONFIG_DEFAULT (0x80000000)
+#define DC_SIGNED_BIT (0x8000)
+#define INTEN_INIT0x (0x00005f01)
+#define INTEN_MONVOPEN (0x00ff0000)
+#define SVSEN_OFF (0x0)
+#define SVSEN_MASK (0x7)
+#define SVSEN_INIT01 (0x1)
+#define SVSEN_INIT02 (0x5)
+#define SVSEN_MON (0x2)
+#define INTSTS_MONVOP (0x00ff0000)
+#define INTSTS_COMPLETE (0x1)
+#define INTSTS_CLEAN (0x00ffffff)
+
+#define proc_fops_rw(name) \
+ static int name ## _proc_open(struct inode *inode, \
+ struct file *file) \
+ { \
+ return single_open(file, name ## _proc_show, \
+ PDE_DATA(inode)); \
+ } \
+ static const struct proc_ops name ## _proc_fops = { \
+ .proc_open = name ## _proc_open, \
+ .proc_read = seq_read, \
+ .proc_lseek = seq_lseek, \
+ .proc_release = single_release, \
+ .proc_write = name ## _proc_write, \
+ }
+
+#define proc_fops_ro(name) \
+ static int name ## _proc_open(struct inode *inode, \
+ struct file *file) \
+ { \
+ return single_open(file, name ## _proc_show, \
+ PDE_DATA(inode)); \
+ } \
+ static const struct proc_ops name ## _proc_fops = { \
+ .proc_open = name ## _proc_open, \
+ .proc_read = seq_read, \
+ .proc_lseek = seq_lseek, \
+ .proc_release = single_release, \
+ }
+
+#define proc_entry(name) {__stringify(name), &name ## _proc_fops}
+
+static DEFINE_SPINLOCK(mtk_svs_lock);
+struct mtk_svs;
+
+enum svsb_phase {
+ SVSB_PHASE_INIT01 = 0,
+ SVSB_PHASE_INIT02,
+ SVSB_PHASE_MON,
+ SVSB_PHASE_ERROR,
+};
+
+enum reg_index {
+ TEMPMONCTL0 = 0,
+ TEMPMONCTL1,
+ TEMPMONCTL2,
+ TEMPMONINT,
+ TEMPMONINTSTS,
+ TEMPMONIDET0,
+ TEMPMONIDET1,
+ TEMPMONIDET2,
+ TEMPH2NTHRE,
+ TEMPHTHRE,
+ TEMPCTHRE,
+ TEMPOFFSETH,
+ TEMPOFFSETL,
+ TEMPMSRCTL0,
+ TEMPMSRCTL1,
+ TEMPAHBPOLL,
+ TEMPAHBTO,
+ TEMPADCPNP0,
+ TEMPADCPNP1,
+ TEMPADCPNP2,
+ TEMPADCMUX,
+ TEMPADCEXT,
+ TEMPADCEXT1,
+ TEMPADCEN,
+ TEMPPNPMUXADDR,
+ TEMPADCMUXADDR,
+ TEMPADCEXTADDR,
+ TEMPADCEXT1ADDR,
+ TEMPADCENADDR,
+ TEMPADCVALIDADDR,
+ TEMPADCVOLTADDR,
+ TEMPRDCTRL,
+ TEMPADCVALIDMASK,
+ TEMPADCVOLTAGESHIFT,
+ TEMPADCWRITECTRL,
+ TEMPMSR0,
+ TEMPMSR1,
+ TEMPMSR2,
+ TEMPADCHADDR,
+ TEMPIMMD0,
+ TEMPIMMD1,
+ TEMPIMMD2,
+ TEMPMONIDET3,
+ TEMPADCPNP3,
+ TEMPMSR3,
+ TEMPIMMD3,
+ TEMPPROTCTL,
+ TEMPPROTTA,
+ TEMPPROTTB,
+ TEMPPROTTC,
+ TEMPSPARE0,
+ TEMPSPARE1,
+ TEMPSPARE2,
+ TEMPSPARE3,
+ TEMPMSR0_1,
+ TEMPMSR1_1,
+ TEMPMSR2_1,
+ TEMPMSR3_1,
+ DESCHAR,
+ TEMPCHAR,
+ DETCHAR,
+ AGECHAR,
+ DCCONFIG,
+ AGECONFIG,
+ FREQPCT30,
+ FREQPCT74,
+ LIMITVALS,
+ VBOOT,
+ DETWINDOW,
+ CONFIG,
+ TSCALCS,
+ RUNCONFIG,
+ SVSEN,
+ INIT2VALS,
+ DCVALUES,
+ AGEVALUES,
+ VOP30,
+ VOP74,
+ TEMP,
+ INTSTS,
+ INTSTSRAW,
+ INTEN,
+ CHKINT,
+ CHKSHIFT,
+ STATUS,
+ VDESIGN30,
+ VDESIGN74,
+ DVT30,
+ DVT74,
+ AGECOUNT,
+ SMSTATE0,
+ SMSTATE1,
+ CTL0,
+ DESDETSEC,
+ TEMPAGESEC,
+ CTRLSPARE0,
+ CTRLSPARE1,
+ CTRLSPARE2,
+ CTRLSPARE3,
+ CORESEL,
+ THERMINTST,
+ INTST,
+ THSTAGE0ST,
+ THSTAGE1ST,
+ THSTAGE2ST,
+ THAHBST0,
+ THAHBST1,
+ SPARE0,
+ SPARE1,
+ SPARE2,
+ SPARE3,
+ THSLPEVEB,
+ reg_num,
+};
+
+static const u32 svs_regs_v2[] = {
+ [TEMPMONCTL0] = 0x000,
+ [TEMPMONCTL1] = 0x004,
+ [TEMPMONCTL2] = 0x008,
+ [TEMPMONINT] = 0x00c,
+ [TEMPMONINTSTS] = 0x010,
+ [TEMPMONIDET0] = 0x014,
+ [TEMPMONIDET1] = 0x018,
+ [TEMPMONIDET2] = 0x01c,
+ [TEMPH2NTHRE] = 0x024,
+ [TEMPHTHRE] = 0x028,
+ [TEMPCTHRE] = 0x02c,
+ [TEMPOFFSETH] = 0x030,
+ [TEMPOFFSETL] = 0x034,
+ [TEMPMSRCTL0] = 0x038,
+ [TEMPMSRCTL1] = 0x03c,
+ [TEMPAHBPOLL] = 0x040,
+ [TEMPAHBTO] = 0x044,
+ [TEMPADCPNP0] = 0x048,
+ [TEMPADCPNP1] = 0x04c,
+ [TEMPADCPNP2] = 0x050,
+ [TEMPADCMUX] = 0x054,
+ [TEMPADCEXT] = 0x058,
+ [TEMPADCEXT1] = 0x05c,
+ [TEMPADCEN] = 0x060,
+ [TEMPPNPMUXADDR] = 0x064,
+ [TEMPADCMUXADDR] = 0x068,
+ [TEMPADCEXTADDR] = 0x06c,
+ [TEMPADCEXT1ADDR] = 0x070,
+ [TEMPADCENADDR] = 0x074,
+ [TEMPADCVALIDADDR] = 0x078,
+ [TEMPADCVOLTADDR] = 0x07c,
+ [TEMPRDCTRL] = 0x080,
+ [TEMPADCVALIDMASK] = 0x084,
+ [TEMPADCVOLTAGESHIFT] = 0x088,
+ [TEMPADCWRITECTRL] = 0x08c,
+ [TEMPMSR0] = 0x090,
+ [TEMPMSR1] = 0x094,
+ [TEMPMSR2] = 0x098,
+ [TEMPADCHADDR] = 0x09c,
+ [TEMPIMMD0] = 0x0a0,
+ [TEMPIMMD1] = 0x0a4,
+ [TEMPIMMD2] = 0x0a8,
+ [TEMPMONIDET3] = 0x0b0,
+ [TEMPADCPNP3] = 0x0b4,
+ [TEMPMSR3] = 0x0b8,
+ [TEMPIMMD3] = 0x0bc,
+ [TEMPPROTCTL] = 0x0c0,
+ [TEMPPROTTA] = 0x0c4,
+ [TEMPPROTTB] = 0x0c8,
+ [TEMPPROTTC] = 0x0cc,
+ [TEMPSPARE0] = 0x0f0,
+ [TEMPSPARE1] = 0x0f4,
+ [TEMPSPARE2] = 0x0f8,
+ [TEMPSPARE3] = 0x0fc,
+ [TEMPMSR0_1] = 0x190,
+ [TEMPMSR1_1] = 0x194,
+ [TEMPMSR2_1] = 0x198,
+ [TEMPMSR3_1] = 0x1b8,
+ [DESCHAR] = 0xc00,
+ [TEMPCHAR] = 0xc04,
+ [DETCHAR] = 0xc08,
+ [AGECHAR] = 0xc0c,
+ [DCCONFIG] = 0xc10,
+ [AGECONFIG] = 0xc14,
+ [FREQPCT30] = 0xc18,
+ [FREQPCT74] = 0xc1c,
+ [LIMITVALS] = 0xc20,
+ [VBOOT] = 0xc24,
+ [DETWINDOW] = 0xc28,
+ [CONFIG] = 0xc2c,
+ [TSCALCS] = 0xc30,
+ [RUNCONFIG] = 0xc34,
+ [SVSEN] = 0xc38,
+ [INIT2VALS] = 0xc3c,
+ [DCVALUES] = 0xc40,
+ [AGEVALUES] = 0xc44,
+ [VOP30] = 0xc48,
+ [VOP74] = 0xc4c,
+ [TEMP] = 0xc50,
+ [INTSTS] = 0xc54,
+ [INTSTSRAW] = 0xc58,
+ [INTEN] = 0xc5c,
+ [CHKINT] = 0xc60,
+ [CHKSHIFT] = 0xc64,
+ [STATUS] = 0xc68,
+ [VDESIGN30] = 0xc6c,
+ [VDESIGN74] = 0xc70,
+ [DVT30] = 0xc74,
+ [DVT74] = 0xc78,
+ [AGECOUNT] = 0xc7c,
+ [SMSTATE0] = 0xc80,
+ [SMSTATE1] = 0xc84,
+ [CTL0] = 0xc88,
+ [DESDETSEC] = 0xce0,
+ [TEMPAGESEC] = 0xce4,
+ [CTRLSPARE0] = 0xcf0,
+ [CTRLSPARE1] = 0xcf4,
+ [CTRLSPARE2] = 0xcf8,
+ [CTRLSPARE3] = 0xcfc,
+ [CORESEL] = 0xf00,
+ [THERMINTST] = 0xf04,
+ [INTST] = 0xf08,
+ [THSTAGE0ST] = 0xf0c,
+ [THSTAGE1ST] = 0xf10,
+ [THSTAGE2ST] = 0xf14,
+ [THAHBST0] = 0xf18,
+ [THAHBST1] = 0xf1c,
+ [SPARE0] = 0xf20,
+ [SPARE1] = 0xf24,
+ [SPARE2] = 0xf28,
+ [SPARE3] = 0xf2c,
+ [THSLPEVEB] = 0xf30,
+};
+
+struct thermal_parameter {
+ int adc_ge_t;
+ int adc_oe_t;
+ int ge;
+ int oe;
+ int gain;
+ int o_vtsabb;
+ int o_vtsmcu1;
+ int o_vtsmcu2;
+ int o_vtsmcu3;
+ int o_vtsmcu4;
+ int o_vtsmcu5;
+ int degc_cali;
+ int adc_cali_en_t;
+ int o_slope;
+ int o_slope_sign;
+ int ts_id;
+};
+
+struct svs_bank_ops {
+ void (*set_freqs_pct)(struct mtk_svs *svs);
+ void (*get_vops)(struct mtk_svs *svs);
+};
+
+struct svs_bank {
+ struct svs_bank_ops *ops;
+ struct completion init_completion;
+ struct device *dev;
+ struct regulator *buck;
+ struct mutex lock; /* Lock to protect update voltage process */
+ bool suspended;
+ bool mtcmos_request;
+ s32 volt_offset;
+ u32 mode_support;
+ u32 opp_freqs[16];
+ u32 freqs_pct[16];
+ u32 opp_volts[16];
+ u32 init02_volts[16];
+ u32 volts[16];
+ u32 reg_data[3][reg_num];
+ u32 freq_base;
+ u32 vboot;
+ u32 volt_step;
+ u32 volt_base;
+ u32 init01_volt_flag;
+ u32 phase;
+ u32 vmax;
+ u32 vmin;
+ u32 bts;
+ u32 mts;
+ u32 bdes;
+ u32 mdes;
+ u32 mtdes;
+ u32 dcbdet;
+ u32 dcmdet;
+ u32 dthi;
+ u32 dtlo;
+ u32 det_window;
+ u32 det_max;
+ u32 age_config;
+ u32 age_voffset_in;
+ u32 agem;
+ u32 dc_config;
+ u32 dc_voffset_in;
+ u32 dvt_fixed;
+ u32 vco;
+ u32 chk_shift;
+ u32 svs_temp;
+ u32 upper_temp_bound;
+ u32 lower_temp_bound;
+ u32 high_temp_threashold;
+ u32 high_temp_offset;
+ u32 low_temp_threashold;
+ u32 low_temp_offset;
+ u32 core_sel;
+ u32 opp_count;
+ u32 int_st;
+ u32 systemclk_en;
+ u32 sw_id;
+ u32 bank_id;
+ u32 ctl0;
+ u8 *of_compatible;
+ u8 *name;
+ u8 *tzone_name;
+ u8 *buck_name;
+};
+
+struct svs_platform {
+ struct svs_bank *banks;
+ bool (*efuse_parsing)(struct mtk_svs *svs);
+ bool fake_efuse;
+ bool need_hw_reset;
+ const u32 *regs;
+ unsigned long irqflags;
+ u32 bank_num;
+ u32 efuse_num;
+ u32 efuse_check;
+ u32 thermal_efuse_num;
+ u8 *name;
+};
+
+struct mtk_svs {
+ const struct svs_platform *platform;
+ struct svs_bank *bank;
+ struct device *dev;
+ void __iomem *base;
+ struct clk *main_clk;
+ u32 *efuse;
+ u32 *thermal_efuse;
+};
+
+unsigned long claim_mtk_svs_lock(void)
+ __acquires(&mtk_svs_lock)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mtk_svs_lock, flags);
+
+ return flags;
+}
+EXPORT_SYMBOL_GPL(claim_mtk_svs_lock);
+
+void release_mtk_svs_lock(unsigned long flags)
+ __releases(&mtk_svs_lock)
+{
+ spin_unlock_irqrestore(&mtk_svs_lock, flags);
+}
+EXPORT_SYMBOL_GPL(release_mtk_svs_lock);
+
+static u32 percent(u32 numerator, u32 denominator)
+{
+ u32 pct;
+
+ /* If not divide 1000, "numerator * 100" would be data overflow. */
+ numerator /= 1000;
+ denominator /= 1000;
+ pct = ((numerator * 100) + denominator - 1) / denominator;
+ pct &= GENMASK(7, 0);
+
+ return pct;
+}
+
+static u32 svs_readl(struct mtk_svs *svs, enum reg_index rg_i)
+{
+ return readl(svs->base + svs->platform->regs[rg_i]);
+}
+
+static void svs_writel(struct mtk_svs *svs, u32 val, enum reg_index rg_i)
+{
+ writel(val, svs->base + svs->platform->regs[rg_i]);
+}
+
+static void svs_switch_bank(struct mtk_svs *svs)
+{
+ struct svs_bank *svsb = svs->bank;
+
+ svs_writel(svs, svsb->core_sel, CORESEL);
+}
+
+static u32 svsb_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step,
+ u32 svsb_volt_base)
+{
+ u32 u_volt;
+
+ u_volt = (svsb_volt * svsb_volt_step) + svsb_volt_base;
+
+ return u_volt;
+}
+
+static int svsb_get_zone_temperature(struct svs_bank *svsb, int *tzone_temp)
+{
+ struct thermal_zone_device *tzd;
+
+ tzd = thermal_zone_get_zone_by_name(svsb->tzone_name);
+ if (IS_ERR(tzd))
+ return PTR_ERR(tzd);
+
+ return thermal_zone_get_temp(tzd, tzone_temp);
+}
+
+static int svsb_set_volts(struct svs_bank *svsb, bool force_update)
+{
+ u32 i, svsb_volt, opp_volt, temp_offset = 0;
+ int tzone_temp, ret;
+
+ mutex_lock(&svsb->lock);
+
+ /*
+ * If bank is suspended, it means signed-off voltages are applied.
+ * Don't need to update opp voltage anymore.
+ */
+ if (svsb->suspended && !force_update) {
+ pr_notice("%s: bank is suspended\n", svsb->name);
+ mutex_unlock(&svsb->lock);
+ return -EPERM;
+ }
+
+ /* Get thermal effect */
+ if (svsb->phase == SVSB_PHASE_MON) {
+ if (svsb->svs_temp > svsb->upper_temp_bound &&
+ svsb->svs_temp < svsb->lower_temp_bound) {
+ pr_err("%s: svs_temp is abnormal (0x%x)?\n",
+ svsb->name, svsb->svs_temp);
+ mutex_unlock(&svsb->lock);
+ return -EINVAL;
+ }
+
+ ret = svsb_get_zone_temperature(svsb, &tzone_temp);
+ if (ret) {
+ pr_err("%s: cannot get zone \"%s\" temperature(%d)\n",
+ svsb->name, svsb->tzone_name, ret);
+ pr_err("%s: set signed-off voltage this time.\n",
+ svsb->name);
+ svsb->phase = SVSB_PHASE_ERROR;
+ }
+
+ if (tzone_temp >= svsb->high_temp_threashold)
+ temp_offset += svsb->high_temp_offset;
+
+ if (tzone_temp <= svsb->low_temp_threashold)
+ temp_offset += svsb->low_temp_offset;
+ }
+
+ /* vmin <= svsb_volt (opp_volt) <= signed-off voltage */
+ for (i = 0; i < svsb->opp_count; i++) {
+ if (svsb->phase == SVSB_PHASE_MON) {
+ svsb_volt = max((svsb->volts[i] + svsb->volt_offset +
+ temp_offset), svsb->vmin);
+ opp_volt = svsb_volt_to_opp_volt(svsb_volt,
+ svsb->volt_step,
+ svsb->volt_base);
+ } else if (svsb->phase == SVSB_PHASE_INIT02) {
+ svsb_volt = max((svsb->init02_volts[i] +
+ svsb->volt_offset), svsb->vmin);
+ opp_volt = svsb_volt_to_opp_volt(svsb_volt,
+ svsb->volt_step,
+ svsb->volt_base);
+ } else if (svsb->phase == SVSB_PHASE_ERROR) {
+ opp_volt = svsb->opp_volts[i];
+ } else {
+ pr_err("%s: unknown phase: %u?\n",
+ svsb->name, svsb->phase);
+ mutex_unlock(&svsb->lock);
+ return -EINVAL;
+ }
+
+ opp_volt = min(opp_volt, svsb->opp_volts[i]);
+ ret = dev_pm_opp_adjust_voltage(svsb->dev, svsb->opp_freqs[i],
+ opp_volt, opp_volt,
+ svsb->opp_volts[i]);
+ if (ret) {
+ pr_err("%s: set voltage failed: %d\n", svsb->name, ret);
+ mutex_unlock(&svsb->lock);
+ return ret;
+ }
+ }
+
+ mutex_unlock(&svsb->lock);
+
+ return 0;
+}
+
+static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx)
+{
+ u32 vy;
+
+ if (v0 == v1 || f0 == f1)
+ return v0;
+
+ /* *100 to have decimal fraction factor, +99 for rounding up. */
+ vy = (v0 * 100) - ((((v0 - v1) * 100) / (f0 - f1)) * (f0 - fx));
+ vy = (vy + 99) / 100;
+
+ return vy;
+}
+
+static void svs_get_vops_v2(struct mtk_svs *svs)
+{
+ struct svs_bank *svsb = svs->bank;
+ u32 temp, i;
+
+ temp = svs_readl(svs, VOP30);
+ svsb->volts[6] = (temp >> 24) & GENMASK(7, 0);
+ svsb->volts[4] = (temp >> 16) & GENMASK(7, 0);
+ svsb->volts[2] = (temp >> 8) & GENMASK(7, 0);
+ svsb->volts[0] = (temp & GENMASK(7, 0));
+
+ temp = svs_readl(svs, VOP74);
+ svsb->volts[14] = (temp >> 24) & GENMASK(7, 0);
+ svsb->volts[12] = (temp >> 16) & GENMASK(7, 0);
+ svsb->volts[10] = (temp >> 8) & GENMASK(7, 0);
+ svsb->volts[8] = (temp & GENMASK(7, 0));
+
+ for (i = 0; i <= 7; i++) {
+ if (i < 7) {
+ svsb->volts[(i * 2) + 1] =
+ interpolate(svsb->freqs_pct[i * 2],
+ svsb->freqs_pct[(i + 1) * 2],
+ svsb->volts[i * 2],
+ svsb->volts[(i + 1) * 2],
+ svsb->freqs_pct[(i * 2) + 1]);
+ } else if (i == 7) {
+ svsb->volts[(i * 2) + 1] =
+ interpolate(svsb->freqs_pct[(i - 1) * 2],
+ svsb->freqs_pct[i * 2],
+ svsb->volts[(i - 1) * 2],
+ svsb->volts[i * 2],
+ svsb->freqs_pct[(i * 2) + 1]);
+ }
+ }
+}
+
+static void svs_set_freqs_pct_v2(struct mtk_svs *svs)
+{
+ struct svs_bank *svsb = svs->bank;
+
+ svs_writel(svs,
+ ((svsb->freqs_pct[6] << 24) & GENMASK(31, 24)) |
+ ((svsb->freqs_pct[4] << 16) & GENMASK(23, 16)) |
+ ((svsb->freqs_pct[2] << 8) & GENMASK(15, 8)) |
+ (svsb->freqs_pct[0] & GENMASK(7, 0)),
+ FREQPCT30);
+ svs_writel(svs,
+ ((svsb->freqs_pct[14] << 24) & GENMASK(31, 24)) |
+ ((svsb->freqs_pct[12] << 16) & GENMASK(23, 16)) |
+ ((svsb->freqs_pct[10] << 8) & GENMASK(15, 8)) |
+ ((svsb->freqs_pct[8]) & GENMASK(7, 0)),
+ FREQPCT74);
+}
+
+static void svs_set_phase(struct mtk_svs *svs, u32 target_phase)
+{
+ struct svs_bank *svsb = svs->bank;
+ u32 des_char, temp_char, det_char, limit_vals;
+ u32 init2vals, ts_calcs, val, filter, i;
+
+ svs_switch_bank(svs);
+
+ des_char = ((svsb->bdes << 8) & GENMASK(15, 8)) |
+ (svsb->mdes & GENMASK(7, 0));
+ svs_writel(svs, des_char, DESCHAR);
+
+ temp_char = ((svsb->vco << 16) & GENMASK(23, 16)) |
+ ((svsb->mtdes << 8) & GENMASK(15, 8)) |
+ (svsb->dvt_fixed & GENMASK(7, 0));
+ svs_writel(svs, temp_char, TEMPCHAR);
+
+ det_char = ((svsb->dcbdet << 8) & GENMASK(15, 8)) |
+ (svsb->dcmdet & GENMASK(7, 0));
+ svs_writel(svs, det_char, DETCHAR);
+
+ svs_writel(svs, svsb->dc_config, DCCONFIG);
+ svs_writel(svs, svsb->age_config, AGECONFIG);
+
+ if (!svsb->agem) {
+ svs_writel(svs, RUNCONFIG_DEFAULT, RUNCONFIG);
+ } else {
+ val = 0x0;
+
+ for (i = 0; i < 24; i += 2) {
+ filter = 0x3 << i;
+
+ if (!(svsb->age_config & filter))
+ val |= (0x1 << i);
+ else
+ val |= (svsb->age_config & filter);
+ }
+ svs_writel(svs, val, RUNCONFIG);
+ }
+
+ svsb->ops->set_freqs_pct(svs);
+
+ limit_vals = ((svsb->vmax << 24) & GENMASK(31, 24)) |
+ ((svsb->vmin << 16) & GENMASK(23, 16)) |
+ ((svsb->dthi << 8) & GENMASK(15, 8)) |
+ (svsb->dtlo & GENMASK(7, 0));
+ svs_writel(svs, limit_vals, LIMITVALS);
+ svs_writel(svs, (svsb->vboot & GENMASK(7, 0)), VBOOT);
+ svs_writel(svs, (svsb->det_window & GENMASK(15, 0)), DETWINDOW);
+ svs_writel(svs, (svsb->det_max & GENMASK(15, 0)), CONFIG);
+
+ if (svsb->chk_shift)
+ svs_writel(svs, (svsb->chk_shift & GENMASK(7, 0)), CHKSHIFT);
+
+ if (svsb->ctl0)
+ svs_writel(svs, svsb->ctl0, CTL0);
+
+ svs_writel(svs, INTSTS_CLEAN, INTSTS);
+
+ switch (target_phase) {
+ case SVSB_PHASE_INIT01:
+ svs_writel(svs, INTEN_INIT0x, INTEN);
+ svs_writel(svs, SVSEN_INIT01, SVSEN);
+ break;
+ case SVSB_PHASE_INIT02:
+ svs_writel(svs, INTEN_INIT0x, INTEN);
+ init2vals = ((svsb->age_voffset_in << 16) & GENMASK(31, 16)) |
+ (svsb->dc_voffset_in & GENMASK(15, 0));
+ svs_writel(svs, init2vals, INIT2VALS);
+ svs_writel(svs, SVSEN_INIT02, SVSEN);
+ break;
+ case SVSB_PHASE_MON:
+ ts_calcs = ((svsb->bts << 12) & GENMASK(23, 12)) |
+ (svsb->mts & GENMASK(11, 0));
+ svs_writel(svs, ts_calcs, TSCALCS);
+ svs_writel(svs, INTEN_MONVOPEN, INTEN);
+ svs_writel(svs, SVSEN_MON, SVSEN);
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static inline void svs_init01_isr_handler(struct mtk_svs *svs)
+{
+ struct svs_bank *svsb = svs->bank;
+ enum reg_index rg_i;
+
+ pr_notice("%s: %s: VDN74:0x%08x, VDN30:0x%08x, DCVALUES:0x%08x\n",
+ svsb->name, __func__, svs_readl(svs, VDESIGN74),
+ svs_readl(svs, VDESIGN30), svs_readl(svs, DCVALUES));
+
+ for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+ svsb->reg_data[SVSB_PHASE_INIT01][rg_i] = svs_readl(svs, rg_i);
+
+ svsb->phase = SVSB_PHASE_INIT01;
+ svsb->dc_voffset_in = ~(svs_readl(svs, DCVALUES) & GENMASK(15, 0)) + 1;
+ if (svsb->init01_volt_flag == SVSB_INIT01_VOLT_IGNORE)
+ svsb->dc_voffset_in = 0;
+ else if ((svsb->dc_voffset_in & DC_SIGNED_BIT) &&
+ (svsb->init01_volt_flag == SVSB_INIT01_VOLT_INC_ONLY))
+ svsb->dc_voffset_in = 0;
+
+ svsb->age_voffset_in = svs_readl(svs, AGEVALUES) & GENMASK(15, 0);
+
+ svs_writel(svs, SVSEN_OFF, SVSEN);
+ svs_writel(svs, INTSTS_COMPLETE, INTSTS);
+
+ /* svs init01 clock gating */
+ svsb->core_sel &= ~svsb->systemclk_en;
+ complete(&svsb->init_completion);
+}
+
+static inline void svs_init02_isr_handler(struct mtk_svs *svs)
+{
+ struct svs_bank *svsb = svs->bank;
+ enum reg_index rg_i;
+
+ pr_notice("%s: %s: VOP74:0x%08x, VOP30:0x%08x, DCVALUES:0x%08x\n",
+ svsb->name, __func__, svs_readl(svs, VOP74),
+ svs_readl(svs, VOP30), svs_readl(svs, DCVALUES));
+
+ for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+ svsb->reg_data[SVSB_PHASE_INIT02][rg_i] = svs_readl(svs, rg_i);
+
+ svsb->phase = SVSB_PHASE_INIT02;
+ svsb->ops->get_vops(svs);
+ memcpy(svsb->init02_volts, svsb->volts, sizeof(u32) * svsb->opp_count);
+
+ svs_writel(svs, SVSEN_OFF, SVSEN);
+ svs_writel(svs, INTSTS_COMPLETE, INTSTS);
+
+ complete(&svsb->init_completion);
+}
+
+static inline void svs_mon_mode_isr_handler(struct mtk_svs *svs)
+{
+ struct svs_bank *svsb = svs->bank;
+ enum reg_index rg_i;
+
+ for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+ svsb->reg_data[SVSB_PHASE_MON][rg_i] = svs_readl(svs, rg_i);
+
+ svsb->phase = SVSB_PHASE_MON;
+ svsb->svs_temp = svs_readl(svs, TEMP) & GENMASK(7, 0);
+ svsb->ops->get_vops(svs);
+
+ svs_writel(svs, INTSTS_MONVOP, INTSTS);
+}
+
+static inline void svs_error_isr_handler(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb = svs->bank;
+ enum reg_index rg_i;
+
+ pr_err("%s(): %s(%s)", __func__, svsp->name, svsb->name);
+ pr_err("CORESEL(0x%x) = 0x%08x\n",
+ svsp->regs[CORESEL], svs_readl(svs, CORESEL)),
+ pr_err("SVSEN(0x%x) = 0x%08x, INTSTS(0x%x) = 0x%08x\n",
+ svsp->regs[SVSEN], svs_readl(svs, SVSEN),
+ svsp->regs[INTSTS], svs_readl(svs, INTSTS));
+ pr_err("SMSTATE0(0x%x) = 0x%08x, SMSTATE1(0x%x) = 0x%08x\n",
+ svsp->regs[SMSTATE0], svs_readl(svs, SMSTATE0),
+ svsp->regs[SMSTATE1], svs_readl(svs, SMSTATE1));
+ pr_err("TEMP(0x%x) = 0x%08x, TEMPMSR0(0x%x) = 0x%08x\n",
+ svsp->regs[TEMP], svs_readl(svs, TEMP),
+ svsp->regs[TEMPMSR0], svs_readl(svs, TEMPMSR0));
+ pr_err("TEMPMSR1(0x%x) = 0x%08x, TEMPMSR2(0x%x) = 0x%08x\n",
+ svsp->regs[TEMPMSR1], svs_readl(svs, TEMPMSR1),
+ svsp->regs[TEMPMSR2], svs_readl(svs, TEMPMSR2));
+ pr_err("TEMPMONCTL0(0x%x) = 0x%08x, TEMPMSRCTL1(0x%x) = 0x%08x\n",
+ svsp->regs[TEMPMONCTL0], svs_readl(svs, TEMPMONCTL0),
+ svsp->regs[TEMPMSRCTL1], svs_readl(svs, TEMPMSRCTL1));
+
+ for (rg_i = TEMPMONCTL0; rg_i < reg_num; rg_i++)
+ svsb->reg_data[SVSB_PHASE_MON][rg_i] = svs_readl(svs, rg_i);
+
+ svsb->mode_support = SVSB_MODE_ALL_DISABLE;
+
+ if (svsb->phase != SVSB_PHASE_INIT01)
+ svsb->phase = SVSB_PHASE_ERROR;
+
+ svs_writel(svs, SVSEN_OFF, SVSEN);
+ svs_writel(svs, INTSTS_CLEAN, INTSTS);
+}
+
+static irqreturn_t svs_isr(int irq, void *data)
+{
+ struct mtk_svs *svs = (struct mtk_svs *)data;
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb = NULL;
+ unsigned long flags;
+ u32 idx, int_sts, svs_en;
+
+ flags = claim_mtk_svs_lock();
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svs->bank = svsb;
+
+ if (svsb->int_st & svs_readl(svs, INTST))
+ continue;
+
+ if (!svsb->suspended) {
+ svs_switch_bank(svs);
+ int_sts = svs_readl(svs, INTSTS);
+ svs_en = svs_readl(svs, SVSEN);
+
+ if (int_sts == INTSTS_COMPLETE &&
+ ((svs_en & SVSEN_MASK) == SVSEN_INIT01))
+ svs_init01_isr_handler(svs);
+ else if ((int_sts == INTSTS_COMPLETE) &&
+ ((svs_en & SVSEN_MASK) == SVSEN_INIT02))
+ svs_init02_isr_handler(svs);
+ else if (!!(int_sts & INTSTS_MONVOP))
+ svs_mon_mode_isr_handler(svs);
+ else
+ svs_error_isr_handler(svs);
+ }
+
+ break;
+ }
+ release_mtk_svs_lock(flags);
+
+ if (svsb->phase != SVSB_PHASE_INIT01)
+ svsb_set_volts(svsb, false);
+
+ return IRQ_HANDLED;
+}
+
+static void svs_mon_mode(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ unsigned long flags;
+ u32 idx;
+
+ flags = claim_mtk_svs_lock();
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svs->bank = svsb;
+
+ if (!(svsb->mode_support & SVSB_MODE_MON))
+ continue;
+
+ svs_set_phase(svs, SVSB_PHASE_MON);
+ }
+ release_mtk_svs_lock(flags);
+}
+
+static int svs_init02(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ unsigned long flags, time_left;
+ u32 idx;
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svs->bank = svsb;
+
+ if (!(svsb->mode_support & SVSB_MODE_INIT02))
+ continue;
+
+ reinit_completion(&svsb->init_completion);
+ flags = claim_mtk_svs_lock();
+ svs_set_phase(svs, SVSB_PHASE_INIT02);
+ release_mtk_svs_lock(flags);
+ time_left =
+ wait_for_completion_timeout(&svsb->init_completion,
+ msecs_to_jiffies(2000));
+ if (!time_left) {
+ pr_err("%s: init02 completion timeout\n", svsb->name);
+ return -EBUSY;
+ }
+ }
+
+ return 0;
+}
+
+static int svs_init01(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ struct pm_qos_request *qos_request;
+ unsigned long flags, time_left;
+ bool search_done;
+ int ret = -EPERM;
+ u32 opp_freqs, opp_vboot, buck_volt, idx, i;
+
+ qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL);
+ if (!qos_request)
+ return -ENOMEM;
+
+ /* Let CPUs leave idle-off state for initializing svs_init01. */
+ cpu_latency_qos_add_request(qos_request, 0);
+
+ /*
+ * Sometimes two svs banks use the same buck.
+ * Therefore, we set each svs bank to vboot voltage first.
+ */
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ search_done = false;
+
+ ret = regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST);
+ if (ret)
+ pr_notice("%s: fail to set fast mode: %d\n",
+ svsb->name, ret);
+
+ if (svsb->mtcmos_request) {
+ ret = regulator_enable(svsb->buck);
+ if (ret) {
+ pr_err("%s: fail to enable %s power: %d\n",
+ svsb->name, svsb->buck_name, ret);
+ goto init01_finish;
+ }
+
+ ret = dev_pm_domain_attach(svsb->dev, false);
+ if (ret) {
+ pr_err("%s: attach pm domain fail: %d\n",
+ svsb->name, ret);
+ goto init01_finish;
+ }
+
+ pm_runtime_enable(svsb->dev);
+ ret = pm_runtime_get_sync(svsb->dev);
+ if (ret < 0) {
+ pr_err("%s: turn mtcmos on fail: %d\n",
+ svsb->name, ret);
+ goto init01_finish;
+ }
+ }
+
+ /*
+ * Find the fastest freq that can be run at vboot and
+ * fix to that freq until svs_init01 is done.
+ */
+ opp_vboot = svsb_volt_to_opp_volt(svsb->vboot,
+ svsb->volt_step,
+ svsb->volt_base);
+
+ for (i = 0; i < svsb->opp_count; i++) {
+ opp_freqs = svsb->opp_freqs[i];
+ if (!search_done && svsb->opp_volts[i] <= opp_vboot) {
+ ret = dev_pm_opp_adjust_voltage(svsb->dev,
+ opp_freqs,
+ opp_vboot,
+ opp_vboot,
+ opp_vboot);
+ if (ret) {
+ pr_err("%s: set voltage failed: %d\n",
+ svsb->name, ret);
+ goto init01_finish;
+ }
+
+ search_done = true;
+ } else {
+ dev_pm_opp_disable(svsb->dev,
+ svsb->opp_freqs[i]);
+ }
+ }
+ }
+
+ /* svs bank init01 begins */
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svs->bank = svsb;
+
+ if (!(svsb->mode_support & SVSB_MODE_INIT01))
+ continue;
+
+ opp_vboot = svsb_volt_to_opp_volt(svsb->vboot,
+ svsb->volt_step,
+ svsb->volt_base);
+
+ buck_volt = regulator_get_voltage(svsb->buck);
+ if (buck_volt != opp_vboot) {
+ pr_err("%s: buck voltage: %u, expected vboot: %u\n",
+ svsb->name, buck_volt, opp_vboot);
+ ret = -EPERM;
+ goto init01_finish;
+ }
+
+ init_completion(&svsb->init_completion);
+ flags = claim_mtk_svs_lock();
+ svs_set_phase(svs, SVSB_PHASE_INIT01);
+ release_mtk_svs_lock(flags);
+ time_left =
+ wait_for_completion_timeout(&svsb->init_completion,
+ msecs_to_jiffies(2000));
+ if (!time_left) {
+ pr_err("%s: init01 completion timeout\n", svsb->name);
+ ret = -EBUSY;
+ goto init01_finish;
+ }
+ }
+
+init01_finish:
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+
+ for (i = 0; i < svsb->opp_count; i++)
+ dev_pm_opp_enable(svsb->dev, svsb->opp_freqs[i]);
+
+ if (regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL))
+ pr_notice("%s: fail to set normal mode: %d\n",
+ svsb->name, ret);
+
+ if (svsb->mtcmos_request) {
+ if (pm_runtime_put_sync(svsb->dev))
+ pr_err("%s: turn mtcmos off fail: %d\n",
+ svsb->name, ret);
+ pm_runtime_disable(svsb->dev);
+ dev_pm_domain_detach(svsb->dev, 0);
+ if (regulator_disable(svsb->buck))
+ pr_err("%s: fail to disable %s power: %d\n",
+ svsb->name, svsb->buck_name, ret);
+ }
+ }
+
+ cpu_latency_qos_remove_request(qos_request);
+ kfree(qos_request);
+
+ return ret;
+}
+
+static int svs_start(struct mtk_svs *svs)
+{
+ int ret;
+
+ ret = svs_init01(svs);
+ if (ret)
+ return ret;
+
+ ret = svs_init02(svs);
+ if (ret)
+ return ret;
+
+ svs_mon_mode(svs);
+
+ return ret;
+}
+
+static bool svs_mt8183_efuse_parsing(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct thermal_parameter tp;
+ struct svs_bank *svsb;
+ bool mon_mode_support = true;
+ int format[6], x_roomt[6], tb_roomt = 0;
+ struct nvmem_cell *cell;
+ size_t len;
+ u32 idx, i, ft_pgm, mts, temp0, temp1, temp2;
+
+ if (svsp->fake_efuse) {
+ pr_notice("fake efuse\n");
+ svs->efuse[0] = 0x00310080;
+ svs->efuse[1] = 0xabfbf757;
+ svs->efuse[2] = 0x47c747c7;
+ svs->efuse[3] = 0xabfbf757;
+ svs->efuse[4] = 0xe7fca0ec;
+ svs->efuse[5] = 0x47bf4b88;
+ svs->efuse[6] = 0xabfb8fa5;
+ svs->efuse[7] = 0xabfb217b;
+ svs->efuse[8] = 0x4bf34be1;
+ svs->efuse[9] = 0xabfb670d;
+ svs->efuse[16] = 0xabfbc653;
+ svs->efuse[17] = 0x47f347e1;
+ svs->efuse[18] = 0xabfbd848;
+ }
+
+ for (i = 0; i < svsp->efuse_num; i++)
+ if (svs->efuse[i])
+ pr_notice("M_HW_RES%d: 0x%08x\n", i, svs->efuse[i]);
+
+ /* svs efuse parsing */
+ ft_pgm = (svs->efuse[0] >> 4) & GENMASK(3, 0);
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ if (ft_pgm <= 1)
+ svsb->init01_volt_flag = SVSB_INIT01_VOLT_IGNORE;
+
+ switch (svsb->sw_id) {
+ case SVS_CPU_LITTLE:
+ svsb->bdes = svs->efuse[16] & GENMASK(7, 0);
+ svsb->mdes = (svs->efuse[16] >> 8) & GENMASK(7, 0);
+ svsb->dcbdet = (svs->efuse[16] >> 16) & GENMASK(7, 0);
+ svsb->dcmdet = (svs->efuse[16] >> 24) & GENMASK(7, 0);
+ svsb->mtdes = (svs->efuse[17] >> 16) & GENMASK(7, 0);
+
+ if (ft_pgm <= 3)
+ svsb->volt_offset += 10;
+ else
+ svsb->volt_offset += 2;
+ break;
+ case SVS_CPU_BIG:
+ svsb->bdes = svs->efuse[18] & GENMASK(7, 0);
+ svsb->mdes = (svs->efuse[18] >> 8) & GENMASK(7, 0);
+ svsb->dcbdet = (svs->efuse[18] >> 16) & GENMASK(7, 0);
+ svsb->dcmdet = (svs->efuse[18] >> 24) & GENMASK(7, 0);
+ svsb->mtdes = svs->efuse[17] & GENMASK(7, 0);
+
+ if (ft_pgm <= 3)
+ svsb->volt_offset += 15;
+ else
+ svsb->volt_offset += 12;
+ break;
+ case SVS_CCI:
+ svsb->bdes = svs->efuse[4] & GENMASK(7, 0);
+ svsb->mdes = (svs->efuse[4] >> 8) & GENMASK(7, 0);
+ svsb->dcbdet = (svs->efuse[4] >> 16) & GENMASK(7, 0);
+ svsb->dcmdet = (svs->efuse[4] >> 24) & GENMASK(7, 0);
+ svsb->mtdes = (svs->efuse[5] >> 16) & GENMASK(7, 0);
+
+ if (ft_pgm <= 3)
+ svsb->volt_offset += 10;
+ else
+ svsb->volt_offset += 2;
+ break;
+ case SVS_GPU:
+ svsb->bdes = svs->efuse[6] & GENMASK(7, 0);
+ svsb->mdes = (svs->efuse[6] >> 8) & GENMASK(7, 0);
+ svsb->dcbdet = (svs->efuse[6] >> 16) & GENMASK(7, 0);
+ svsb->dcmdet = (svs->efuse[6] >> 24) & GENMASK(7, 0);
+ svsb->mtdes = svs->efuse[5] & GENMASK(7, 0);
+
+ if (ft_pgm >= 2) {
+ svsb->freq_base = 800000000; /* 800MHz */
+ svsb->dvt_fixed = 2;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (svsp->fake_efuse) {
+ svs->thermal_efuse[0] = 0x02873f69;
+ svs->thermal_efuse[1] = 0xa11d9142;
+ svs->thermal_efuse[2] = 0xa2526900;
+ } else {
+ /* Get thermal efuse by nvmem */
+ cell = nvmem_cell_get(svs->dev, "calibration-data");
+ if (IS_ERR(cell)) {
+ pr_err("no thermal efuse? disable mon mode\n");
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svsb->mode_support &= ~SVSB_MODE_MON;
+ }
+
+ return true;
+ }
+
+ svs->thermal_efuse = (u32 *)nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+ }
+
+ /* Thermal efuse parsing */
+ tp.adc_ge_t = (svs->thermal_efuse[1] >> 22) & GENMASK(9, 0);
+ tp.adc_oe_t = (svs->thermal_efuse[1] >> 12) & GENMASK(9, 0);
+
+ tp.o_vtsmcu1 = (svs->thermal_efuse[0] >> 17) & GENMASK(8, 0);
+ tp.o_vtsmcu2 = (svs->thermal_efuse[0] >> 8) & GENMASK(8, 0);
+ tp.o_vtsmcu3 = svs->thermal_efuse[1] & GENMASK(8, 0);
+ tp.o_vtsmcu4 = (svs->thermal_efuse[2] >> 23) & GENMASK(8, 0);
+ tp.o_vtsmcu5 = (svs->thermal_efuse[2] >> 5) & GENMASK(8, 0);
+ tp.o_vtsabb = (svs->thermal_efuse[2] >> 14) & GENMASK(8, 0);
+
+ tp.degc_cali = (svs->thermal_efuse[0] >> 1) & GENMASK(5, 0);
+ tp.adc_cali_en_t = svs->thermal_efuse[0] & BIT(0);
+ tp.o_slope_sign = (svs->thermal_efuse[0] >> 7) & BIT(0);
+
+ tp.ts_id = (svs->thermal_efuse[1] >> 9) & BIT(0);
+ tp.o_slope = (svs->thermal_efuse[0] >> 26) & GENMASK(5, 0);
+
+ if (tp.adc_cali_en_t == 1) {
+ if (!tp.ts_id)
+ tp.o_slope = 0;
+
+ if ((tp.adc_ge_t < 265 || tp.adc_ge_t > 758) ||
+ (tp.adc_oe_t < 265 || tp.adc_oe_t > 758) ||
+ (tp.o_vtsmcu1 < -8 || tp.o_vtsmcu1 > 484) ||
+ (tp.o_vtsmcu2 < -8 || tp.o_vtsmcu2 > 484) ||
+ (tp.o_vtsmcu3 < -8 || tp.o_vtsmcu3 > 484) ||
+ (tp.o_vtsmcu4 < -8 || tp.o_vtsmcu4 > 484) ||
+ (tp.o_vtsmcu5 < -8 || tp.o_vtsmcu5 > 484) ||
+ (tp.o_vtsabb < -8 || tp.o_vtsabb > 484) ||
+ (tp.degc_cali < 1 || tp.degc_cali > 63)) {
+ pr_err("bad thermal efuse data, disable mon mode\n");
+ mon_mode_support = false;
+ }
+ } else {
+ pr_err("no thermal efuse data, disable mon mode\n");
+ mon_mode_support = false;
+ }
+
+ if (!mon_mode_support) {
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svsb->mode_support &= ~SVSB_MODE_MON;
+ }
+
+ return true;
+ }
+
+ tp.ge = ((tp.adc_ge_t - 512) * 10000) / 4096;
+ tp.oe = (tp.adc_oe_t - 512);
+ tp.gain = (10000 + tp.ge);
+
+ format[0] = (tp.o_vtsmcu1 + 3350 - tp.oe);
+ format[1] = (tp.o_vtsmcu2 + 3350 - tp.oe);
+ format[2] = (tp.o_vtsmcu3 + 3350 - tp.oe);
+ format[3] = (tp.o_vtsmcu4 + 3350 - tp.oe);
+ format[4] = (tp.o_vtsmcu5 + 3350 - tp.oe);
+ format[5] = (tp.o_vtsabb + 3350 - tp.oe);
+
+ for (i = 0; i < 6; i++)
+ x_roomt[i] = (((format[i] * 10000) / 4096) * 10000) / tp.gain;
+
+ temp0 = (10000 * 100000 / tp.gain) * 15 / 18;
+
+ if (!tp.o_slope_sign)
+ mts = (temp0 * 10) / (1534 + tp.o_slope * 10);
+ else
+ mts = (temp0 * 10) / (1534 - tp.o_slope * 10);
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svsb->mts = mts;
+
+ switch (svsb->sw_id) {
+ case SVS_CPU_LITTLE:
+ tb_roomt = x_roomt[3];
+ break;
+ case SVS_CPU_BIG:
+ tb_roomt = x_roomt[4];
+ break;
+ case SVS_CCI:
+ tb_roomt = x_roomt[3];
+ break;
+ case SVS_GPU:
+ tb_roomt = x_roomt[1];
+ break;
+ default:
+ break;
+ }
+
+ temp0 = (tp.degc_cali * 10 / 2);
+ temp1 = ((10000 * 100000 / 4096 / tp.gain) *
+ tp.oe + tb_roomt * 10) * 15 / 18;
+
+ if (!tp.o_slope_sign)
+ temp2 = temp1 * 100 / (1534 + tp.o_slope * 10);
+ else
+ temp2 = temp1 * 100 / (1534 - tp.o_slope * 10);
+
+ svsb->bts = (temp0 + temp2 - 250) * 4 / 10;
+ }
+
+ return true;
+}
+
+static bool svs_is_supported(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct nvmem_cell *cell;
+ size_t len;
+ bool ret;
+
+ if (svsp->fake_efuse) {
+ len = svsp->efuse_num * sizeof(u32);
+ svs->efuse = devm_kzalloc(svs->dev, len, GFP_KERNEL);
+ if (!svs->efuse) {
+ pr_err("no memory for allocating svs_efuse\n");
+ return false;
+ }
+
+ len = svsp->thermal_efuse_num * sizeof(u32);
+ svs->thermal_efuse = devm_kzalloc(svs->dev, len, GFP_KERNEL);
+ if (!svs->thermal_efuse) {
+ pr_err("no memory for allocating svs_thermal_efuse\n");
+ return false;
+ }
+
+ goto svsp_efuse_parsing;
+ }
+
+ /* Get svs efuse by nvmem */
+ cell = nvmem_cell_get(svs->dev, "svs-calibration-data");
+ if (IS_ERR(cell)) {
+ pr_err("no \"svs-calibration-data\" from dts? disable svs\n");
+ return false;
+ }
+
+ svs->efuse = (u32 *)nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (!svs->efuse[svsp->efuse_check]) {
+ pr_err("svs_efuse[%u] = 0x%x?\n",
+ svsp->efuse_check, svs->efuse[svsp->efuse_check]);
+ return false;
+ }
+
+svsp_efuse_parsing:
+ ret = svsp->efuse_parsing(svs);
+
+ return ret;
+}
+
+static int svs_resource_setup(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ struct platform_device *pdev;
+ struct device_node *np = NULL;
+ struct dev_pm_opp *opp;
+ unsigned long freq;
+ int count, ret;
+ u32 idx, i;
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+
+ switch (svsb->sw_id) {
+ case SVS_CPU_LITTLE:
+ svsb->name = "SVS_CPU_LITTLE";
+ break;
+ case SVS_CPU_BIG:
+ svsb->name = "SVS_CPU_BIG";
+ break;
+ case SVS_CCI:
+ svsb->name = "SVS_CCI";
+ break;
+ case SVS_GPU:
+ svsb->name = "SVS_GPU";
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ /* Add svs bank device for opp-table/mtcmos/buck control */
+ pdev = platform_device_alloc(svsb->name, 0);
+ if (!pdev) {
+ pr_err("%s: fail to alloc pdev for svs_bank\n",
+ svsb->name);
+ return -ENOMEM;
+ }
+
+ for_each_child_of_node(svs->dev->of_node, np) {
+ if (of_device_is_compatible(np, svsb->of_compatible)) {
+ pdev->dev.of_node = np;
+ break;
+ }
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ pr_err("%s: fail to add svs_bank device: %d\n",
+ svsb->name, ret);
+ return ret;
+ }
+
+ svsb->dev = &pdev->dev;
+ dev_set_drvdata(svsb->dev, svs);
+ ret = dev_pm_opp_of_add_table(svsb->dev);
+ if (ret) {
+ pr_err("%s: fail to add opp table: %d\n",
+ svsb->name, ret);
+ return ret;
+ }
+
+ mutex_init(&svsb->lock);
+
+ svsb->buck = devm_regulator_get_optional(svsb->dev,
+ svsb->buck_name);
+ if (IS_ERR(svsb->buck)) {
+ pr_err("%s: cannot get regulator \"%s-supply\"\n",
+ svsb->name, svsb->buck_name);
+ return PTR_ERR(svsb->buck);
+ }
+
+ count = dev_pm_opp_get_opp_count(svsb->dev);
+ if (svsb->opp_count != count) {
+ pr_err("%s: opp_count not \"%u\" but get \"%d\"?\n",
+ svsb->name, svsb->opp_count, count);
+ return count;
+ }
+
+ for (i = 0, freq = U32_MAX; i < svsb->opp_count; i++, freq--) {
+ opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq);
+ if (IS_ERR(opp)) {
+ pr_err("%s: error opp entry!!, err = %ld\n",
+ svsb->name, PTR_ERR(opp));
+ return PTR_ERR(opp);
+ }
+
+ svsb->opp_freqs[i] = freq;
+ svsb->opp_volts[i] = dev_pm_opp_get_voltage(opp);
+ svsb->freqs_pct[i] = percent(svsb->opp_freqs[i],
+ svsb->freq_base);
+ dev_pm_opp_put(opp);
+ }
+ }
+
+ return 0;
+}
+
+static int svs_suspend(struct device *dev)
+{
+ struct mtk_svs *svs = dev_get_drvdata(dev);
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ unsigned long flags;
+ int ret;
+ u32 idx;
+
+ /* Wait if there is processing svs_isr(). Suspend all banks. */
+ flags = claim_mtk_svs_lock();
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svs->bank = svsb;
+ svs_switch_bank(svs);
+ svs_writel(svs, SVSEN_OFF, SVSEN);
+ svs_writel(svs, INTSTS_CLEAN, INTSTS);
+ svsb->suspended = true;
+ }
+ release_mtk_svs_lock(flags);
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ if (svsb->phase != SVSB_PHASE_INIT01) {
+ svsb->phase = SVSB_PHASE_ERROR;
+ svsb_set_volts(svsb, true);
+ }
+ }
+
+ if (svsp->need_hw_reset) {
+ ret = device_reset(svs->dev);
+ if (ret) {
+ pr_err("%s: ret = %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ clk_disable_unprepare(svs->main_clk);
+
+ return 0;
+}
+
+static int svs_resume(struct device *dev)
+{
+ struct mtk_svs *svs = dev_get_drvdata(dev);
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ int ret;
+ u32 idx;
+
+ ret = clk_prepare_enable(svs->main_clk);
+ if (ret) {
+ pr_err("cannot enable main_clk, disable svs\n");
+ return ret;
+ }
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+ svsb->suspended = false;
+ }
+
+ ret = svs_init02(svs);
+ if (ret)
+ return ret;
+
+ svs_mon_mode(svs);
+
+ return 0;
+}
+
+static int svs_debug_proc_show(struct seq_file *m, void *v)
+{
+ struct svs_bank *svsb = (struct svs_bank *)m->private;
+
+ if (svsb->phase == SVSB_PHASE_INIT01)
+ seq_puts(m, "init1\n");
+ else if (svsb->phase == SVSB_PHASE_INIT02)
+ seq_puts(m, "init2\n");
+ else if (svsb->phase == SVSB_PHASE_MON)
+ seq_puts(m, "mon mode\n");
+ else if (svsb->phase == SVSB_PHASE_ERROR)
+ seq_puts(m, "disabled\n");
+ else
+ seq_puts(m, "unknown\n");
+
+ return 0;
+}
+
+static ssize_t svs_debug_proc_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file));
+ struct mtk_svs *svs = dev_get_drvdata(svsb->dev);
+ unsigned long flags;
+ int enabled, ret;
+ char *buf = NULL;
+
+ if (count >= PAGE_SIZE)
+ return -EINVAL;
+
+ buf = (char *)memdup_user_nul(buffer, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = kstrtoint(buf, 10, &enabled);
+ if (ret)
+ return ret;
+
+ if (!enabled) {
+ flags = claim_mtk_svs_lock();
+ svs->bank = svsb;
+ svsb->mode_support = SVSB_MODE_ALL_DISABLE;
+ svs_switch_bank(svs);
+ svs_writel(svs, SVSEN_OFF, SVSEN);
+ svs_writel(svs, INTSTS_CLEAN, INTSTS);
+ release_mtk_svs_lock(flags);
+
+ svsb->phase = SVSB_PHASE_ERROR;
+ svsb_set_volts(svsb, true);
+ }
+
+ kfree(buf);
+
+ return count;
+}
+
+proc_fops_rw(svs_debug);
+
+static int svs_dump_proc_show(struct seq_file *m, void *v)
+{
+ struct mtk_svs *svs = (struct mtk_svs *)m->private;
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ unsigned long svs_reg_addr;
+ u32 idx, i, j;
+
+ for (i = 0; i < svsp->efuse_num; i++)
+ if (svs->efuse && svs->efuse[i])
+ seq_printf(m, "M_HW_RES%d = 0x%08x\n",
+ i, svs->efuse[i]);
+
+ for (i = 0; i < svsp->thermal_efuse_num; i++)
+ if (svs->thermal_efuse)
+ seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n",
+ i, svs->thermal_efuse[i]);
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+
+ for (i = SVSB_PHASE_INIT01; i <= SVSB_PHASE_MON; i++) {
+ seq_printf(m, "Bank_number = %u\n", svsb->bank_id);
+
+ if (i < SVSB_PHASE_MON)
+ seq_printf(m, "mode = init%d\n", i + 1);
+ else
+ seq_puts(m, "mode = mon\n");
+
+ for (j = TEMPMONCTL0; j < reg_num; j++) {
+ svs_reg_addr = (unsigned long)(svs->base +
+ svsp->regs[j]);
+ seq_printf(m, "0x%08lx = 0x%08x\n",
+ svs_reg_addr, svsb->reg_data[i][j]);
+ }
+ }
+ }
+
+ return 0;
+}
+
+proc_fops_ro(svs_dump);
+
+static int svs_status_proc_show(struct seq_file *m, void *v)
+{
+ struct svs_bank *svsb = (struct svs_bank *)m->private;
+ struct dev_pm_opp *opp;
+ unsigned long freq;
+ int tzone_temp, ret;
+ u32 i;
+
+ ret = svsb_get_zone_temperature(svsb, &tzone_temp);
+ if (ret)
+ seq_printf(m, "%s: cannot get zone \"%s\" temperature\n",
+ svsb->name, svsb->tzone_name);
+ else
+ seq_printf(m, "%s: temperature = %d\n", svsb->name, tzone_temp);
+
+ for (i = 0, freq = U32_MAX; i < svsb->opp_count; i++, freq--) {
+ opp = dev_pm_opp_find_freq_floor(svsb->dev, &freq);
+ if (IS_ERR(opp)) {
+ seq_printf(m, "%s: error opp entry!!, err = %ld\n",
+ svsb->name, PTR_ERR(opp));
+ return PTR_ERR(opp);
+ }
+
+ seq_printf(m, "opp_freqs[%02u]: %lu, volts[%02u]: %lu, ",
+ i, freq, i, dev_pm_opp_get_voltage(opp));
+ seq_printf(m, "svsb_volts[%02u]: 0x%x, freqs_pct[%02u]: %u\n",
+ i, svsb->volts[i], i, svsb->freqs_pct[i]);
+ dev_pm_opp_put(opp);
+ }
+
+ return 0;
+}
+
+proc_fops_ro(svs_status);
+
+static int svs_volt_offset_proc_show(struct seq_file *m, void *v)
+{
+ struct svs_bank *svsb = (struct svs_bank *)m->private;
+
+ seq_printf(m, "%d\n", svsb->volt_offset);
+
+ return 0;
+}
+
+static ssize_t svs_volt_offset_proc_write(struct file *file,
+ const char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ struct svs_bank *svsb = (struct svs_bank *)PDE_DATA(file_inode(file));
+ char *buf = NULL;
+ s32 volt_offset;
+
+ if (count >= PAGE_SIZE)
+ return -EINVAL;
+
+ buf = (char *)memdup_user_nul(buffer, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ if (!kstrtoint(buf, 10, &volt_offset)) {
+ svsb->volt_offset = volt_offset;
+ svsb_set_volts(svsb, true);
+ }
+
+ kfree(buf);
+
+ return count;
+}
+
+proc_fops_rw(svs_volt_offset);
+
+static int svs_create_svs_procfs(struct mtk_svs *svs)
+{
+ const struct svs_platform *svsp = svs->platform;
+ struct svs_bank *svsb;
+ struct proc_dir_entry *svs_dir, *bank_dir;
+ u32 idx, i;
+
+ struct pentry {
+ const char *name;
+ const struct proc_ops *fops;
+ };
+
+ struct pentry svs_entries[] = {
+ proc_entry(svs_dump),
+ };
+
+ struct pentry bank_entries[] = {
+ proc_entry(svs_debug),
+ proc_entry(svs_status),
+ proc_entry(svs_volt_offset),
+ };
+
+ svs_dir = proc_mkdir("svs", NULL);
+ if (!svs_dir) {
+ pr_err("mkdir /proc/svs failed\n");
+ return -EPERM;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(svs_entries); i++) {
+ if (!proc_create_data(svs_entries[i].name, 0664,
+ svs_dir, svs_entries[i].fops, svs)) {
+ pr_err("create /proc/svs/%s failed\n",
+ svs_entries[i].name);
+ return -EPERM;
+ }
+ }
+
+ for (idx = 0; idx < svsp->bank_num; idx++) {
+ svsb = &svsp->banks[idx];
+
+ if (svsb->mode_support == SVSB_MODE_ALL_DISABLE)
+ continue;
+
+ bank_dir = proc_mkdir(svsb->name, svs_dir);
+ if (!bank_dir) {
+ pr_err("mkdir /proc/svs/%s failed\n", svsb->name);
+ return -EPERM;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(bank_entries); i++) {
+ if (!proc_create_data(bank_entries[i].name, 0664,
+ bank_dir, bank_entries[i].fops,
+ svsb)) {
+ pr_err("create /proc/svs/%s/%s failed\n",
+ svsb->name, bank_entries[i].name);
+ return -EPERM;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct svs_bank_ops svs_mt8183_banks_ops = {
+ .set_freqs_pct = svs_set_freqs_pct_v2,
+ .get_vops = svs_get_vops_v2,
+};
+
+static struct svs_bank svs_mt8183_banks[4] = {
+ {
+ .of_compatible = "mediatek,mt8183-svs-cpu-little",
+ .sw_id = SVS_CPU_LITTLE,
+ .bank_id = 0,
+ .ops = &svs_mt8183_banks_ops,
+ .tzone_name = "tzts4",
+ .buck_name = "vcpu-little",
+ .mtcmos_request = false,
+ .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY,
+ .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02,
+ .opp_count = 16,
+ .freq_base = 1989000000,
+ .vboot = 0x30,
+ .volt_step = 6250,
+ .volt_base = 500000,
+ .volt_offset = 0,
+ .vmax = 0x64,
+ .vmin = 0x18,
+ .dthi = 0x1,
+ .dtlo = 0xfe,
+ .det_window = 0xa28,
+ .det_max = 0xffff,
+ .age_config = 0x555555,
+ .agem = 0,
+ .dc_config = 0x555555,
+ .dvt_fixed = 0x7,
+ .vco = 0x10,
+ .chk_shift = 0x77,
+ .upper_temp_bound = 0x64,
+ .lower_temp_bound = 0xb2,
+ .high_temp_threashold = HIGH_TEMP_MAX,
+ .low_temp_threashold = 25000,
+ .low_temp_offset = 0,
+ .core_sel = 0x8fff0000,
+ .systemclk_en = BIT(31),
+ .int_st = BIT(0),
+ .ctl0 = 0x00010001,
+ },
+ {
+ .of_compatible = "mediatek,mt8183-svs-cpu-big",
+ .sw_id = SVS_CPU_BIG,
+ .bank_id = 1,
+ .ops = &svs_mt8183_banks_ops,
+ .tzone_name = "tzts5",
+ .buck_name = "vcpu-big",
+ .mtcmos_request = false,
+ .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY,
+ .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02,
+ .opp_count = 16,
+ .freq_base = 1989000000,
+ .vboot = 0x30,
+ .volt_step = 6250,
+ .volt_base = 500000,
+ .volt_offset = 0,
+ .vmax = 0x58,
+ .vmin = 0x10,
+ .dthi = 0x1,
+ .dtlo = 0xfe,
+ .det_window = 0xa28,
+ .det_max = 0xffff,
+ .age_config = 0x555555,
+ .agem = 0,
+ .dc_config = 0x555555,
+ .dvt_fixed = 0x7,
+ .vco = 0x10,
+ .chk_shift = 0x77,
+ .upper_temp_bound = 0x64,
+ .lower_temp_bound = 0xb2,
+ .high_temp_threashold = HIGH_TEMP_MAX,
+ .low_temp_threashold = 25000,
+ .low_temp_offset = 0,
+ .core_sel = 0x8fff0001,
+ .systemclk_en = BIT(31),
+ .int_st = BIT(1),
+ .ctl0 = 0x00000001,
+ },
+ {
+ .of_compatible = "mediatek,mt8183-svs-cci",
+ .sw_id = SVS_CCI,
+ .bank_id = 2,
+ .ops = &svs_mt8183_banks_ops,
+ .tzone_name = "tzts4",
+ .buck_name = "vcci",
+ .mtcmos_request = false,
+ .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY,
+ .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02,
+ .opp_count = 16,
+ .freq_base = 1196000000,
+ .vboot = 0x30,
+ .volt_step = 6250,
+ .volt_base = 500000,
+ .volt_offset = 0,
+ .vmax = 0x64,
+ .vmin = 0x18,
+ .dthi = 0x1,
+ .dtlo = 0xfe,
+ .det_window = 0xa28,
+ .det_max = 0xffff,
+ .age_config = 0x555555,
+ .agem = 0,
+ .dc_config = 0x555555,
+ .dvt_fixed = 0x7,
+ .vco = 0x10,
+ .chk_shift = 0x77,
+ .upper_temp_bound = 0x64,
+ .lower_temp_bound = 0xb2,
+ .high_temp_threashold = HIGH_TEMP_MAX,
+ .low_temp_threashold = 25000,
+ .low_temp_offset = 0,
+ .core_sel = 0x8fff0002,
+ .systemclk_en = BIT(31),
+ .int_st = BIT(2),
+ .ctl0 = 0x00100003,
+ },
+ {
+ .of_compatible = "mediatek,mt8183-svs-gpu",
+ .sw_id = SVS_GPU,
+ .bank_id = 3,
+ .ops = &svs_mt8183_banks_ops,
+ .tzone_name = "tzts2",
+ .buck_name = "vgpu",
+ .mtcmos_request = true,
+ .init01_volt_flag = SVSB_INIT01_VOLT_INC_ONLY,
+ .mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02 |
+ SVSB_MODE_MON,
+ .opp_count = 16,
+ .freq_base = 900000000,
+ .vboot = 0x30,
+ .volt_step = 6250,
+ .volt_base = 500000,
+ .volt_offset = 0,
+ .vmax = 0x40,
+ .vmin = 0x14,
+ .dthi = 0x1,
+ .dtlo = 0xfe,
+ .det_window = 0xa28,
+ .det_max = 0xffff,
+ .age_config = 0x555555,
+ .agem = 0,
+ .dc_config = 0x555555,
+ .dvt_fixed = 0x3,
+ .vco = 0x10,
+ .chk_shift = 0x77,
+ .upper_temp_bound = 0x64,
+ .lower_temp_bound = 0xb2,
+ .high_temp_threashold = HIGH_TEMP_MAX,
+ .low_temp_threashold = 25000,
+ .low_temp_offset = 3,
+ .core_sel = 0x8fff0003,
+ .systemclk_en = BIT(31),
+ .int_st = BIT(3),
+ .ctl0 = 0x00050001,
+ },
+};
+
+static const struct svs_platform svs_mt8183_platform = {
+ .name = "mt8183-svs",
+ .banks = svs_mt8183_banks,
+ .efuse_parsing = svs_mt8183_efuse_parsing,
+ .regs = svs_regs_v2,
+ .irqflags = IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ .need_hw_reset = false,
+ .fake_efuse = false,
+ .bank_num = 4,
+ .efuse_num = 25,
+ .efuse_check = 2,
+ .thermal_efuse_num = 3,
+};
+
+static const struct of_device_id mtk_svs_of_match[] = {
+ {
+ .compatible = "mediatek,mt8183-svs",
+ .data = &svs_mt8183_platform,
+ }, {
+ /* Sentinel */
+ },
+};
+
+static int svs_add_thermal_device_link(struct platform_device *svs_pdev)
+{
+ struct device_node *therm_node;
+ struct platform_device *therm_pdev;
+ struct device_link *sup_link;
+ char const *therm_node_name[] = {"thermal"};
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(therm_node_name); i++) {
+ therm_node = of_find_node_by_name(NULL, therm_node_name[i]);
+ if (therm_node)
+ break;
+ }
+
+ if (!therm_node) {
+ pr_err("no available thermal node? pass device link\n");
+ return 0;
+ }
+
+ therm_pdev = of_find_device_by_node(therm_node);
+ if (!therm_pdev) {
+ pr_err("no \"%pOF\" platform device? pass device link\n",
+ therm_node);
+ of_node_put(therm_node);
+ return 0;
+ }
+
+ of_node_put(therm_node);
+
+ sup_link = device_link_add(&svs_pdev->dev, &therm_pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER);
+
+ if (sup_link->status == DL_STATE_DORMANT)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+
+static int svs_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_dev_id;
+ struct mtk_svs *svs;
+ int ret;
+ u32 svs_irq;
+
+ ret = svs_add_thermal_device_link(pdev);
+ if (ret)
+ return ret;
+
+ svs = devm_kzalloc(&pdev->dev, sizeof(*svs), GFP_KERNEL);
+ if (!svs)
+ return -ENOMEM;
+
+ svs->dev = &pdev->dev;
+ if (!svs->dev->of_node) {
+ pr_err("cannot find device node\n");
+ return -ENODEV;
+ }
+
+ svs->base = of_iomap(svs->dev->of_node, 0);
+ if (IS_ERR(svs->base)) {
+ pr_err("cannot find svs register base\n");
+ return PTR_ERR(svs->base);
+ }
+
+ of_dev_id = of_match_node(mtk_svs_of_match, svs->dev->of_node);
+ if (!of_dev_id || !of_dev_id->data)
+ return -EINVAL;
+
+ svs->platform = of_dev_id->data;
+ dev_set_drvdata(svs->dev, svs);
+
+ svs_irq = irq_of_parse_and_map(svs->dev->of_node, 0);
+ ret = devm_request_threaded_irq(svs->dev, svs_irq, NULL, svs_isr,
+ svs->platform->irqflags, "mtk-svs",
+ svs);
+ if (ret) {
+ pr_err("register irq(%d) failed: %d\n", svs_irq, ret);
+ return ret;
+ }
+
+ svs->main_clk = devm_clk_get(svs->dev, "main");
+ if (IS_ERR(svs->main_clk)) {
+ pr_err("failed to get clock: %ld\n", PTR_ERR(svs->main_clk));
+ return PTR_ERR(svs->main_clk);
+ }
+
+ ret = clk_prepare_enable(svs->main_clk);
+ if (ret) {
+ pr_err("cannot enable main clk: %d\n", ret);
+ return ret;
+ }
+
+ if (!svs_is_supported(svs)) {
+ pr_notice("svs is not supported\n");
+ ret = -EPERM;
+ goto svs_probe_fail;
+ }
+
+ ret = svs_resource_setup(svs);
+ if (ret)
+ goto svs_probe_fail;
+
+ ret = svs_start(svs);
+ if (ret)
+ goto svs_probe_fail;
+
+ ret = svs_create_svs_procfs(svs);
+ if (ret)
+ goto svs_probe_fail;
+
+ return 0;
+
+svs_probe_fail:
+ clk_disable_unprepare(svs->main_clk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops svs_pm_ops = {
+ .suspend = svs_suspend,
+ .resume = svs_resume,
+};
+
+static struct platform_driver svs_driver = {
+ .probe = svs_probe,
+ .driver = {
+ .name = "mtk-svs",
+ .pm = &svs_pm_ops,
+ .of_match_table = of_match_ptr(mtk_svs_of_match),
+ },
+};
+
+static int __init svs_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&svs_driver);
+ if (ret) {
+ pr_err("svs platform driver register failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+late_initcall_sync(svs_init);
+
+MODULE_DESCRIPTION("MediaTek SVS Driver v1.0");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/power/mtk_svs.h b/include/linux/power/mtk_svs.h
new file mode 100644
index 000000000000..5c03982e3576
--- /dev/null
+++ b/include/linux/power/mtk_svs.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2020 MediaTek Inc.
+ */
+
+#ifndef __MTK_SVS_H__
+#define __MTK_SVS_H__
+
+#if IS_ENABLED(CONFIG_MTK_SVS)
+unsigned long claim_mtk_svs_lock(void);
+void release_mtk_svs_lock(unsigned long flags);
+#else
+static inline unsigned long claim_mtk_svs_lock(void)
+{
+ return 0;
+}
+
+static inline void release_mtk_svs_lock(unsigned long flags)
+{
+}
+#endif /* CONFIG_MTK_SVS */
+
+#endif /* __MTK_SVS_H__ */
--
2.18.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v4 7/7] firmware: smccc: Add ARCH_SOC_ID support
From: Arnd Bergmann @ 2020-05-18 9:30 UTC (permalink / raw)
To: Sudeep Holla
Cc: Mark Rutland, Lorenzo Pieralisi, Catalin Marinas,
linux-kernel@vger.kernel.org, Steven Price, harb, Will Deacon,
Linux ARM
In-Reply-To: <20200518091222.27467-8-sudeep.holla@arm.com>
On Mon, May 18, 2020 at 11:12 AM Sudeep Holla <sudeep.holla@arm.com> wrote:
> +static ssize_t
> +jep106_cont_bank_code_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + return sprintf(buf, "0x%02x\n", JEP106_BANK_CONT_CODE(soc_id_version));
> +}
> +
> +static DEVICE_ATTR_RO(jep106_cont_bank_code);
> +
> +static ssize_t
> +jep106_identification_code_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return sprintf(buf, "0x%02x\n", JEP106_ID_CODE(soc_id_version));
> +}
I think we should try hard to avoid nonstandard attributes for the soc device.
Did you run into a problem with finding one of the existing attributes
that can be used to hold the fields?
Arnd
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v8 1/3] dt-bindings: power: avs: add mtk svs dt-bindings
From: Roger Lu @ 2020-05-18 9:24 UTC (permalink / raw)
To: Kevin Hilman, Rob Herring, Nicolas Boichat, Stephen Boyd
Cc: Mark Rutland, Nishanth Menon, Angus Lin, devicetree, linux-pm,
Roger Lu, linux-kernel, Xiaoqing Liu, YT Lee, Fan Chen,
linux-mediatek, HenryC Chen, Charles Yang, Matthias Brugger,
linux-arm-kernel
In-Reply-To: <20200518092403.22647-1-roger.lu@mediatek.com>
Document the binding for enabling mtk svs on MediaTek SoC.
Signed-off-by: Roger Lu <roger.lu@mediatek.com>
---
.../bindings/power/avs/mtk_svs.yaml | 141 ++++++++++++++++++
1 file changed, 141 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/avs/mtk_svs.yaml
diff --git a/Documentation/devicetree/bindings/power/avs/mtk_svs.yaml b/Documentation/devicetree/bindings/power/avs/mtk_svs.yaml
new file mode 100644
index 000000000000..77ab606814ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/avs/mtk_svs.yaml
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/avs/mtk_svs.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Introduce SVS engine
+
+maintainers:
+ - Kevin Hilman <khilman@kernel.org>
+ - Nishanth Menon <nm@ti.com>
+
+description: |+
+ The SVS (Smart Voltage Scaling) engine is a piece of hardware which is
+ used to calculate optimized voltage values of several power domains,
+ e.g. CPU/GPU/CCI, according to chip process corner, temperatures,
+ and other factors. Then DVFS driver could apply those optimized voltage
+ values to reduce power consumption.
+
+properties:
+ compatible:
+ const: mediatek,mt8183-svs
+
+ reg:
+ description: Address range of the MTK SVS controller.
+ maxItems: 1
+
+ interrupts:
+ description: IRQ for the MTK SVS controller.
+ maxItems: 1
+
+ clocks:
+ description: Main clock for svs controller to work.
+
+ clock-names:
+ const: main
+
+ nvmem-cells:
+ maxItems: 2
+ description:
+ Phandle to the calibration data provided by a nvmem device.
+
+ nvmem-cell-names:
+ items:
+ - const: svs-calibration-data
+ - const: calibration-data
+
+patternProperties:
+ "^svs-(cpu-little|cpu-big|cci|gpu)$":
+ type: object
+ description:
+ Each subnode represents one SVS bank.
+ - svs-cpu-little (SVS bank device node of little CPU)
+ - svs-cpu-big (SVS bank device node of big CPU)
+ - svs-cci (SVS bank device node of CCI)
+ - svs-gpu (SVS bank device node of GPU)
+
+ properties:
+ compatible:
+ enum:
+ - mediatek,mt8183-svs-cpu-little
+ - mediatek,mt8183-svs-cpu-big
+ - mediatek,mt8183-svs-cci
+ - mediatek,mt8183-svs-gpu
+
+ power-domains:
+ description: Phandle to the associated power domain
+ maxItems: 1
+
+ operating-points-v2: true
+
+ vcpu-little-supply:
+ description: PMIC buck of little CPU
+
+ vcpu-big-supply:
+ description: PMIC buck of big CPU
+
+ vcci-supply:
+ description: PMIC buck of CCI
+
+ vgpu-spply:
+ description: PMIC buck of GPU
+
+ required:
+ - compatible
+ - operating-points-v2
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - nvmem-cells
+ - nvmem-cell-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/mt8183-clk.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/power/mt8183-power.h>
+
+ svs: svs@1100b000 {
+ compatible = "mediatek,mt8183-svs";
+ reg = <0 0x1100b000 0 0x1000>;
+ interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_INFRA_THERM>;
+ clock-names = "main";
+ nvmem-cells = <&svs_calibration>, <&thermal_calibration>;
+ nvmem-cell-names = "svs-calibration-data", "calibration-data";
+
+ svs_cpu_little: svs-cpu-little {
+ compatible = "mediatek,mt8183-svs-cpu-little";
+ operating-points-v2 = <&cluster0_opp>;
+ vcpu-little-supply = <&mt6358_vproc12_reg>;
+ };
+
+ svs_cpu_big: svs-cpu-big {
+ compatible = "mediatek,mt8183-svs-cpu-big";
+ operating-points-v2 = <&cluster1_opp>;
+ vcpu-big-supply = <&mt6358_vproc11_reg>;
+ };
+
+ svs_cci: svs-cci {
+ compatible = "mediatek,mt8183-svs-cci";
+ operating-points-v2 = <&cci_opp>;
+ vcci-supply = <&mt6358_vproc12_reg>;
+ };
+
+ svs_gpu: svs-gpu {
+ compatible = "mediatek,mt8183-svs-gpu";
+ power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
+ operating-points-v2 = <&gpu_opp_table>;
+ vgpu-spply = <&mt6358_vgpu_reg>;
+ };
+ };
--
2.18.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH v8 2/3] arm64: dts: mt8183: add svs device information
From: Roger Lu @ 2020-05-18 9:24 UTC (permalink / raw)
To: Kevin Hilman, Rob Herring, Nicolas Boichat, Stephen Boyd
Cc: Mark Rutland, Nishanth Menon, Angus Lin, devicetree, linux-pm,
Roger Lu, linux-kernel, Xiaoqing Liu, YT Lee, Fan Chen,
linux-mediatek, HenryC Chen, Charles Yang, Matthias Brugger,
linux-arm-kernel
In-Reply-To: <20200518092403.22647-1-roger.lu@mediatek.com>
add pmic/clock/irq/efuse setting in svs node
Signed-off-by: Roger Lu <roger.lu@mediatek.com>
---
arch/arm64/boot/dts/mediatek/mt8183-evb.dts | 16 ++++++++
arch/arm64/boot/dts/mediatek/mt8183.dtsi | 41 +++++++++++++++++++++
2 files changed, 57 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8183-evb.dts b/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
index 1fb195c683c3..6ae1d9a1bcbf 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8183-evb.dts
@@ -231,6 +231,22 @@
};
};
+&svs_cpu_little {
+ vcpu-little-supply = <&mt6358_vproc12_reg>;
+};
+
+&svs_cpu_big {
+ vcpu-big-supply = <&mt6358_vproc11_reg>;
+};
+
+&svs_cci {
+ vcci-supply = <&mt6358_vproc12_reg>;
+};
+
+&svs_gpu {
+ vgpu-spply = <&mt6358_vgpu_reg>;
+};
+
&uart0 {
status = "okay";
};
diff --git a/arch/arm64/boot/dts/mediatek/mt8183.dtsi b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
index 97863adb7bc0..6b6b8dc1824f 100644
--- a/arch/arm64/boot/dts/mediatek/mt8183.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8183.dtsi
@@ -453,6 +453,39 @@
status = "disabled";
};
+ svs: svs@1100b000 {
+ compatible = "mediatek,mt8183-svs";
+ reg = <0 0x1100b000 0 0x1000>;
+ interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_INFRA_THERM>;
+ clock-names = "main";
+ nvmem-cells = <&svs_calibration>,
+ <&thermal_calibration>;
+ nvmem-cell-names = "svs-calibration-data",
+ "calibration-data";
+
+ svs_cpu_little: svs-cpu-little {
+ compatible = "mediatek,mt8183-svs-cpu-little";
+ operating-points-v2 = <&cluster0_opp>;
+ };
+
+ svs_cpu_big: svs-cpu-big {
+ compatible = "mediatek,mt8183-svs-cpu-big";
+ operating-points-v2 = <&cluster1_opp>;
+ };
+
+ svs_cci: svs-cci {
+ compatible = "mediatek,mt8183-svs-cci";
+ operating-points-v2 = <&cci_opp>;
+ };
+
+ svs_gpu: svs-gpu {
+ compatible = "mediatek,mt8183-svs-gpu";
+ power-domains = <&scpsys MT8183_POWER_DOMAIN_MFG_2D>;
+ operating-points-v2 = <&gpu_opp_table>;
+ };
+ };
+
i2c3: i2c@1100f000 {
compatible = "mediatek,mt8183-i2c";
reg = <0 0x1100f000 0 0x1000>,
@@ -644,6 +677,14 @@
compatible = "mediatek,mt8183-efuse",
"mediatek,efuse";
reg = <0 0x11f10000 0 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ thermal_calibration: calib@180 {
+ reg = <0x180 0xc>;
+ };
+ svs_calibration: calib@580 {
+ reg = <0x580 0x64>;
+ };
};
mfgcfg: syscon@13000000 {
--
2.18.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH] dt-bindings: reset: Convert i.MX reset to json-schema
From: Philipp Zabel @ 2020-05-18 9:41 UTC (permalink / raw)
To: Anson Huang, robh+dt, shawnguo, s.hauer, kernel, festevam,
devicetree, linux-arm-kernel, linux-kernel
Cc: Linux-imx
In-Reply-To: <1589250194-29441-1-git-send-email-Anson.Huang@nxp.com>
Hi Anson,
On Tue, 2020-05-12 at 10:23 +0800, Anson Huang wrote:
> Convert the i.MX reset binding to DT schema format using json-schema.
>
> Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
thank you for the conversion.
> ---
> .../devicetree/bindings/reset/fsl,imx-src.txt | 49 ------------------
> .../devicetree/bindings/reset/fsl,imx-src.yaml | 58 ++++++++++++++++++++++
> 2 files changed, 58 insertions(+), 49 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> create mode 100644 Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
>
> diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt b/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> deleted file mode 100644
> index 6ed79e6..0000000
> --- a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> +++ /dev/null
> @@ -1,49 +0,0 @@
> -Freescale i.MX System Reset Controller
> -======================================
> -
> -Please also refer to reset.txt in this directory for common reset
> -controller binding usage.
> -
> -Required properties:
> -- compatible: Should be "fsl,<chip>-src"
> -- reg: should be register base and length as documented in the
> - datasheet
> -- interrupts: Should contain SRC interrupt and CPU WDOG interrupt,
> - in this order.
> -- #reset-cells: 1, see below
> -
> -example:
> -
> -src: src@20d8000 {
> - compatible = "fsl,imx6q-src";
> - reg = <0x020d8000 0x4000>;
> - interrupts = <0 91 0x04 0 96 0x04>;
> - #reset-cells = <1>;
> -};
> -
> -Specifying reset lines connected to IP modules
> -==============================================
> -
> -The system reset controller can be used to reset the GPU, VPU,
> -IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
> -nodes should specify the reset line on the SRC in their resets
> -property, containing a phandle to the SRC device node and a
> -RESET_INDEX specifying which module to reset, as described in
> -reset.txt
> -
> -example:
> -
> - ipu1: ipu@2400000 {
> - resets = <&src 2>;
> - };
> - ipu2: ipu@2800000 {
> - resets = <&src 4>;
> - };
> -
> -The following RESET_INDEX values are valid for i.MX5:
> -GPU_RESET 0
> -VPU_RESET 1
> -IPU1_RESET 2
> -OPEN_VG_RESET 3
> -The following additional RESET_INDEX value is valid for i.MX6:
> -IPU2_RESET 4
> diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> new file mode 100644
> index 0000000..276a533
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> @@ -0,0 +1,58 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/reset/fsl,imx-src.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Freescale i.MX System Reset Controller
> +
> +maintainers:
> + - Philipp Zabel <p.zabel@pengutronix.de>
> +
> +description: |
> + The system reset controller can be used to reset the GPU, VPU,
> + IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
> + nodes should specify the reset line on the SRC in their resets
> + property, containing a phandle to the SRC device node and a
> + RESET_INDEX specifying which module to reset, as described in
> + reset.txt
> +
> + The following RESET_INDEX values are valid for i.MX5:
> + GPU_RESET 0
> + VPU_RESET 1
> + IPU1_RESET 2
> + OPEN_VG_RESET 3
> + The following additional RESET_INDEX value is valid for i.MX6:
> + IPU2_RESET 4
> +
> +properties:
> + compatible:
> + items:
> + - const: "fsl,imx51-src"
"fsl,imx51-src" is the only compatible the driver matches on, but we
have these combinations on compatible SRCs in the device trees:
"fsl,imx50-src", "fsl,imx51-src"
"fsl,imx51-src"
"fsl,imx53-src", "fsl,imx51-src"
"fsl,imx6q-src", "fsl,imx51-src"
"fsl,imx6sl-src", "fsl,imx51-src"
"fsl,imx6sll-src", "fsl,imx51-src"
"fsl,imx6sx-src", "fsl,imx51-src"
"fsl,imx6ul-src", "fsl,imx51-src"
That could be described using oneOf and and an items list of const
values per SoC like in the qcom bindings.
regards
Philipp
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v1 8/9] arm64: dts: actions: Add MMC controller support for S700
From: Andreas Färber @ 2020-05-18 9:48 UTC (permalink / raw)
To: André Przywara, Manivannan Sadhasivam, Amit Tomer
Cc: devicetree, Rob Herring, linux-actions, linux-arm-kernel,
cristian.ciocaltea
In-Reply-To: <29500523-0fe4-955d-57de-d4b9f11cedb8@arm.com>
Hi,
Am 18.05.20 um 10:29 schrieb André Przywara:
> On 18/05/2020 07:17, Manivannan Sadhasivam wrote:
>> I don't know if the DT fallback is discouraged or not.
>
> I don't know if there is an "official" statement on this, but last thing
> I heard, adding SoC specific compatibles to generic fallback strings was
> encouraged. Hence my proposal, to add one.
I believe the official guidance would be to never be too generic in the
first place. I.e., prefer s500 (oldest model tested) over generic owl.
But now that we have it, prepending a more specific one (rather than
replacing it) is the only way to go. In that case the binding needs to
be changed to allow both the old and the recommended new variant, as
André points out. Please add a comment to help bindings readers choose
which of the two to adopt then.
Amit, next time please quote errors you see right away, that could've
spared a handful of messages discussing about the driver when it was in
fact just a bindings issue (which you had been asked to fix by André).
Regards,
Andreas
--
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer
HRB 36809 (AG Nürnberg)
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v6] arm64: Expose FAR_EL1 tag bits in sigcontext
From: Dave Martin @ 2020-05-18 9:53 UTC (permalink / raw)
To: Peter Collingbourne
Cc: Andrey Konovalov, Kevin Brodsky, Kostya Serebryany,
Evgenii Stepanov, Catalin Marinas, Vincenzo Frascino, Will Deacon,
Linux ARM, Richard Henderson
In-Reply-To: <CAMn1gO4V05nD-Tq4dES0QyO73bAT-Nwo1ABnz0nuous8Rq+dGA@mail.gmail.com>
On Thu, May 14, 2020 at 05:58:21PM -0700, Peter Collingbourne wrote:
> On Wed, May 13, 2020 at 1:28 PM Dave Martin <Dave.Martin@arm.com> wrote:
> >
> > On Wed, May 13, 2020 at 11:09:14AM -0700, Peter Collingbourne wrote:
> > > The kernel currently clears the tag bits (i.e. bits 56-63) in the fault
> > > address exposed via siginfo.si_addr and sigcontext.fault_address. However,
> > > the tag bits may be needed by tools in order to accurately diagnose
> > > memory errors, such as HWASan [1] or future tools based on the Memory
> > > Tagging Extension (MTE).
> > >
> > > We should not stop clearing these bits in the existing fault address fields,
> > > because there may be existing userspace applications that are expecting the tag
> > > bits to be cleared. Instead, create a fault_addr_top_byte_context in sigcontext
> > > (similar to the existing esr_context), and store the tag bits of FAR_EL1 there.
> > >
> > > [1] http://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html
> > >
> > > Signed-off-by: Peter Collingbourne <pcc@google.com>
> > > ---
> > > v6:
> > > - bring back comment about __reserved[]
> > >
> > > v5:
> > > - add padding to fault_addr_top_byte_context in order to ensure the correct
> > > size and preserve sp alignment
> > >
> > > v4:
> > > - expose only the tag bits in the context instead of the entire FAR_EL1
> > > - remove mention of the new context from the sigcontext.__reserved[] note
> > >
> > > v3:
> > > - add documentation to tagged-pointers.rst
> > > - update comments in sigcontext.h
> > >
> > > v2:
> > > - revert changes to hw_breakpoint.c
> > > - rename set_thread_esr to set_thread_far_esr
> > >
> > > Documentation/arm64/tagged-pointers.rst | 17 +++++----
> > > arch/arm64/include/asm/exception.h | 2 +-
> > > arch/arm64/include/asm/processor.h | 2 +-
> > > arch/arm64/include/uapi/asm/sigcontext.h | 22 ++++++++----
> > > arch/arm64/kernel/entry-common.c | 2 --
> > > arch/arm64/kernel/signal.c | 22 +++++++++++-
> > > arch/arm64/mm/fault.c | 45 ++++++++++++++----------
> > > 7 files changed, 77 insertions(+), 35 deletions(-)
> > >
> > > diff --git a/Documentation/arm64/tagged-pointers.rst b/Documentation/arm64/tagged-pointers.rst
> > > index eab4323609b9..c6e9592a9dea 100644
> > > --- a/Documentation/arm64/tagged-pointers.rst
> > > +++ b/Documentation/arm64/tagged-pointers.rst
> > > @@ -53,12 +53,17 @@ visibility.
> > > Preserving tags
> > > ---------------
> > >
> > > -Non-zero tags are not preserved when delivering signals. This means that
> > > -signal handlers in applications making use of tags cannot rely on the
> > > -tag information for user virtual addresses being maintained for fields
> > > -inside siginfo_t. One exception to this rule is for signals raised in
> > > -response to watchpoint debug exceptions, where the tag information will
> > > -be preserved.
> > > +Non-zero tags are not preserved in the fault address fields
> > > +siginfo.si_addr or sigcontext.fault_address when delivering
> > > +signals. This means that signal handlers in applications making use
> > > +of tags cannot rely on the tag information for user virtual addresses
> > > +being maintained in these fields. One exception to this rule is for
> > > +signals raised in response to watchpoint debug exceptions, where the
> > > +tag information will be preserved.
> > > +
> > > +The fault address tag is preserved in the fault_addr_top_byte field of
> > > +the signal frame record fault_addr_top_byte_context, which is present
> > > +for signals raised in response to data aborts and instruction aborts.
> > >
> > > The architecture prevents the use of a tagged PC, so the upper byte will
> > > be set to a sign-extension of bit 55 on exception return.
> > > diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h
> > > index 7a6e81ca23a8..90e772d9b2cd 100644
> > > --- a/arch/arm64/include/asm/exception.h
> > > +++ b/arch/arm64/include/asm/exception.h
> > > @@ -32,7 +32,7 @@ static inline u32 disr_to_esr(u64 disr)
> > > }
> > >
> > > asmlinkage void enter_from_user_mode(void);
> > > -void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
> > > +void do_mem_abort(unsigned long far, unsigned int esr, struct pt_regs *regs);
> > > void do_undefinstr(struct pt_regs *regs);
> > > asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr);
> > > void do_debug_exception(unsigned long addr_if_watchpoint, unsigned int esr,
> > > diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
> > > index 240fe5e5b720..63185be29ff9 100644
> > > --- a/arch/arm64/include/asm/processor.h
> > > +++ b/arch/arm64/include/asm/processor.h
> > > @@ -144,7 +144,7 @@ struct thread_struct {
> > > void *sve_state; /* SVE registers, if any */
> > > unsigned int sve_vl; /* SVE vector length */
> > > unsigned int sve_vl_onexec; /* SVE vl after next exec */
> > > - unsigned long fault_address; /* fault info */
> > > + unsigned long fault_address; /* FAR_EL1 value */
> > > unsigned long fault_code; /* ESR_EL1 value */
> > > struct debug_info debug; /* debugging */
> > > #ifdef CONFIG_ARM64_PTR_AUTH
> > > diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h
> > > index 8b0ebce92427..2a3fe3de899d 100644
> > > --- a/arch/arm64/include/uapi/asm/sigcontext.h
> > > +++ b/arch/arm64/include/uapi/asm/sigcontext.h
> > > @@ -44,11 +44,12 @@ struct sigcontext {
> > > *
> > > * 0x210 fpsimd_context
> > > * 0x10 esr_context
> > > + * 0x10 fault_addr_top_byte_context
> > > * 0x8a0 sve_context (vl <= 64) (optional)
> > > * 0x20 extra_context (optional)
> > > * 0x10 terminator (null _aarch64_ctx)
> > > *
> > > - * 0x510 (reserved for future allocation)
> > > + * 0x500 (reserved for future allocation)
> > > *
> > > * New records that can exceed this space need to be opt-in for userspace, so
> > > * that an expanded signal frame is not generated unexpectedly. The mechanism
> > > @@ -94,17 +95,26 @@ struct esr_context {
> > > __u64 esr;
> > > };
> > >
> > > +/* Top byte of fault address (normally not exposed via si_addr) */
> > > +#define FAULT_ADDR_TOP_BYTE_MAGIC 0x46544201
> > > +
> > > +struct fault_addr_top_byte_context {
> > > + struct _aarch64_ctx head;
> > > + __u8 fault_addr_top_byte;
> > > + __u8 __reserved[7];
> > > +};
> > > +
> >
> > Nit: the name here is a bit cumbersome (obviously bikeshedding...)
> >
> >
> > For the rest, some of my comments may be bogus -- I haven't dug into
> > this stuff for a little while!
> >
> >
> > Anyway:
> >
> > Do we really get the whole top byte of the address in the FAR? If so,
> > fine, but I'm having trouble finding a clear statement in the
> > architecture one way or the other. (I didn't attempt to dive into the
> > pseudocode.)
>
> I rely on this statement in the ARM:
>
> https://developer.arm.com/docs/ddi0595/b/aarch64-system-registers/far_el1
> "For a Data Abort or Watchpoint exception, if address tagging is
> enabled for the address accessed by the data access that caused the
> exception, then this field includes the tag."
Yes, I think that covers it. I hadn't found a clear definition of
"tag", but I think the TBI mechanism makes it "reasonably obvious" the
non-address (i.e., tag) bits are [63:56].
> And note that address tagging here essentially means TBI (which is
> always enabled on Linux), and not memory tagging.
>
> > Also, since we're burning 16 bytes here, I'd prefer if we make this
> > extensible. At present the __reserved[7] is unusable because
> > userspace has no way to know whether it's valid or not.
> >
> > Options include an additional flag byte (0 for now), or just making
> > the whole thing a __u64. In that case we can leave the top byte bits
> > in their original positions if we want, but it would be a good idea to
> > include a flag to say that field is valid at all. (See comments below
> > on Synchronous external abort.)
> >
> > So, say, foo_context->fault_info = (esr & (~0ULL << 56)) | TOP_BYTE_VALID.
> > (with #defines for the bits/fields as appropriate).
>
> The flag bits seem like a good idea. Thinking ahead to the MTE sync
> tag fault (which might not provide us with bits 60-63), we may
> consider having separate bits to indicate "bits 56-59 valid" and "bits
> 60-63 valid", set both bits for regular data aborts and only the
> former for sync tag faults, which would avoid the need to define a
> separate context for these faults. And if a future architecture
> revision provides us with bits 60-63 for tag faults, we could start
> setting both flag bits even for tag faults.
Seems reasonable, but a "tag mask" field of some sort might be
preferable to hard-wiring, just in case a future update to MTE supports
more than 4 bits.
> > > /*
> > > * extra_context: describes extra space in the signal frame for
> > > * additional structures that don't fit in sigcontext.__reserved[].
> > > *
> > > * Note:
> > > *
> > > - * 1) fpsimd_context, esr_context and extra_context must be placed in
> > > - * sigcontext.__reserved[] if present. They cannot be placed in the
> > > - * extra space. Any other record can be placed either in the extra
> > > - * space or in sigcontext.__reserved[], unless otherwise specified in
> > > - * this file.
> > > + * 1) fpsimd_context, esr_context, fault_addr_top_byte_context and
> > > + * extra_context must be placed in sigcontext.__reserved[] if present.
> > > + * They cannot be placed in the extra space. Any other record can be
> > > + * placed either in the extra space or in sigcontext.__reserved[],
> > > + * unless otherwise specified in this file.
> > > *
> > > * 2) There must not be more than one extra_context.
> > > *
> > > diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
> > > index c839b5bf1904..045b4f518836 100644
> > > --- a/arch/arm64/kernel/entry-common.c
> > > +++ b/arch/arm64/kernel/entry-common.c
> > > @@ -22,7 +22,6 @@ static void notrace el1_abort(struct pt_regs *regs, unsigned long esr)
> > > unsigned long far = read_sysreg(far_el1);
> > >
> > > local_daif_inherit(regs);
> > > - far = untagged_addr(far);
> > > do_mem_abort(far, esr, regs);
> > > }
> > > NOKPROBE_SYMBOL(el1_abort);
> > > @@ -104,7 +103,6 @@ static void notrace el0_da(struct pt_regs *regs, unsigned long esr)
> > >
> > > user_exit_irqoff();
> > > local_daif_restore(DAIF_PROCCTX);
> > > - far = untagged_addr(far);
> > > do_mem_abort(far, esr, regs);
> > > }
> > > NOKPROBE_SYMBOL(el0_da);
> > > diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
> > > index 339882db5a91..baa88dc02e5c 100644
> > > --- a/arch/arm64/kernel/signal.c
> > > +++ b/arch/arm64/kernel/signal.c
> > > @@ -55,6 +55,7 @@ struct rt_sigframe_user_layout {
> > >
> > > unsigned long fpsimd_offset;
> > > unsigned long esr_offset;
> > > + unsigned long ftb_offset;
> > > unsigned long sve_offset;
> > > unsigned long extra_offset;
> > > unsigned long end_offset;
> > > @@ -383,6 +384,7 @@ static int parse_user_sigframe(struct user_ctxs *user,
> > > break;
> > >
> > > case ESR_MAGIC:
> > > + case FAULT_ADDR_TOP_BYTE_MAGIC:
> > > /* ignore */
> > > break;
> > >
> > > @@ -581,6 +583,12 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
> > > sizeof(struct esr_context));
> > > if (err)
> > > return err;
> > > +
> > > + err = sigframe_alloc(
> > > + user, &user->ftb_offset,
> > > + sizeof(struct fault_addr_top_byte_context));
> >
> > Nit: inconsistent indentation?
> >
> > (Mostly just because it makes the change look odd against the hunk
> > context, but not a big deal.)
>
> With consistent indentation we violate 80 cols due to the extra long
> struct name. The indentation is what clang-format is giving me.
I suspected that might be why. Fair enough (though a shorter name would
be no bad thing, it's not worth changing that just for nicer indentation).
>
> > > + if (err)
> > > + return err;
> > > }
> > >
> > > if (system_supports_sve()) {
> > > @@ -621,7 +629,8 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
> > > __put_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err);
> > > __put_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err);
> > >
> > > - __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
> > > + __put_user_error(untagged_addr(current->thread.fault_address),
> > > + &sf->uc.uc_mcontext.fault_address, err);
> > >
> > > err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
> > >
> > > @@ -641,6 +650,17 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,
> > > __put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
> > > }
> > >
> > > + if (err == 0 && user->ftb_offset) {
> > > + struct fault_addr_top_byte_context __user *ftb_ctx =
> > > + apply_user_offset(user, user->ftb_offset);
> > > +
> > > + __put_user_error(FAULT_ADDR_TOP_BYTE_MAGIC,
> > > + &ftb_ctx->head.magic, err);
> > > + __put_user_error(sizeof(*ftb_ctx), &ftb_ctx->head.size, err);
> > > + __put_user_error(current->thread.fault_address >> 56,
> > > + &ftb_ctx->fault_addr_top_byte, err);
> > > + }
> > > +
> >
> > How do we handle the fact that the top byte of FAR is sometimes UNKNOWN?
> >
> > For Synchronous external aborts in particular, those bits are documented
> > as UNKNOWN, but I don't see any special handling, There may be other
> > cases I haven't spotted.
> >
> > For preference we can omit this record entirely if we don't have any
> > information we can report, but certainly we shouldn't expose UNKNOWN
> > bits.
>
> In this case we mask out the top byte in do_sea before passing the
> address to arm64_notify_die (which clears fault_address and passes the
> address argument on to arm64_force_sig_fault to be exposed via
> si_addr). So the record would always contain a 0 byte. It seems
> reasonable to omit the record in this case instead.
Ah, right. Missed that.
The record is already omitted when fault_code == 0 IIUC, so perhaps
we're already doing the right thing for synchronous external aborts.
> > [ Aside:
> >
> > Also, what if we're not reporting a memory abort at all? Does
> > thread.fault_address just contain junk from the last fault? I see
> > nothing anywhere that cleans this up. (This is historical and not
> > your fault, but it would be good to close this down while we're about
> > it.)
> >
> >
> > Hmmm, looking at the code I think we probably leak fault_address etc.
> > across execve() too, so it may even be stale junk from an old process
> > :/
> >
> > Maybe I just confused myself.
> >
> > End aside. ]
>
> Yes, it's unclear whether we always manage to not expose a fault
> address if we're not reporting a data or instruction abort. The code
> would need to arrange for fault_code to be set to 0 in order to avoid
> exposing previous fault_address values via future signals. I don't see
> anywhere where we're resetting these fields after delivering a signal,
> so it seems possible by calling arm64_force_sig_fault without first
> setting fault_code (most callers do this, but the calls in
> arch/arm64/kernel/debug-monitors.c and arch/arm64/kernel/ptrace.c seem
> not to), or simply by calling force_sig_fault (which happens in many
> places throughout the kernel).
>
> Maybe something like this would do the trick? (Untested, and forgive
> spaces instead of tabs, grumble grumble gmail):
>
> diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
> index baa88dc02e5c..5867f2fdbe64 100644
> --- a/arch/arm64/kernel/signal.c
> +++ b/arch/arm64/kernel/signal.c
> @@ -648,6 +648,7 @@ static int setup_sigframe(struct
> rt_sigframe_user_layout *user,
> __put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err);
> __put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err);
> __put_user_error(current->thread.fault_code,
> &esr_ctx->esr, err);
> + current->thread.fault_code = 0;
Perhaps, but we'd need to be careful. For example, can we run out of
user stack before this and deliver a SIGSEGV, but with the old
fault_code still set? Then we'd emit the old fault code with the
new "can't deliver signal" signal, which doesn't make sense.
Stuff may also go wrong with signal prioritisation.
If a higher-priority signal (say SIGINT) comes in after a data abort
enters the kernel but before the resulting SIGSEGV is dequeued for
delivery, wouldn't we deliver SIGINT first, with the bogus fault code?
With your change we'd then have cleared the fault code by the time we
deliver the SIGSEGV it actually relates to, if I've understood right.
Today, I think we just attach that fault code to every signal that's
delivered until something overwrites or resets it, which means that
a signal that needs fault_code gets it, at the expense of attaching
it to a bunch of other random signals too.
Checking the signal number and si_code might help us to know what we
should be doing with fault_code. We need to have sure userspace can't
trick us with a non kernel generated signal here. It would also be
necessary to check how PTRACE_SETSIGINFO interacts with this.
Cheers
---Dave
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* RE: [PATCH] dt-bindings: reset: Convert i.MX reset to json-schema
From: Anson Huang @ 2020-05-18 9:57 UTC (permalink / raw)
To: Aisheng Dong, p.zabel@pengutronix.de, robh+dt@kernel.org,
shawnguo@kernel.org, s.hauer@pengutronix.de,
kernel@pengutronix.de, festevam@gmail.com,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Cc: dl-linux-imx
In-Reply-To: <AM6PR04MB496643088C27DB303166ED0D80B80@AM6PR04MB4966.eurprd04.prod.outlook.com>
> Subject: RE: [PATCH] dt-bindings: reset: Convert i.MX reset to json-schema
>
> > From: Anson Huang <Anson.Huang@nxp.com>
> > Sent: Tuesday, May 12, 2020 10:23 AM
> >
> > Convert the i.MX reset binding to DT schema format using json-schema.
> >
> > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > ---
> > .../devicetree/bindings/reset/fsl,imx-src.txt | 49 ------------------
> > .../devicetree/bindings/reset/fsl,imx-src.yaml | 58
> > ++++++++++++++++++++++
> > 2 files changed, 58 insertions(+), 49 deletions(-) delete mode
> > 100644 Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > create mode 100644
> > Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > b/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > deleted file mode 100644
> > index 6ed79e6..0000000
> > --- a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > +++ /dev/null
> > @@ -1,49 +0,0 @@
> > -Freescale i.MX System Reset Controller
> > -======================================
> > -
> > -Please also refer to reset.txt in this directory for common reset
> > -controller binding usage.
> > -
> > -Required properties:
> > -- compatible: Should be "fsl,<chip>-src"
> > -- reg: should be register base and length as documented in the
> > - datasheet
> > -- interrupts: Should contain SRC interrupt and CPU WDOG interrupt,
> > - in this order.
> > -- #reset-cells: 1, see below
> > -
> > -example:
> > -
> > -src: src@20d8000 {
> > - compatible = "fsl,imx6q-src";
> > - reg = <0x020d8000 0x4000>;
> > - interrupts = <0 91 0x04 0 96 0x04>;
> > - #reset-cells = <1>;
> > -};
> > -
> > -Specifying reset lines connected to IP modules
> > -==============================================
> > -
> > -The system reset controller can be used to reset the GPU, VPU, -IPU,
> > and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device -nodes
> > should specify the reset line on the SRC in their resets -property,
> > containing a phandle to the SRC device node and a -RESET_INDEX
> > specifying which module to reset, as described in -reset.txt
> > -
> > -example:
> > -
> > - ipu1: ipu@2400000 {
> > - resets = <&src 2>;
> > - };
> > - ipu2: ipu@2800000 {
> > - resets = <&src 4>;
> > - };
> > -
> > -The following RESET_INDEX values are valid for i.MX5:
> > -GPU_RESET 0
> > -VPU_RESET 1
> > -IPU1_RESET 2
> > -OPEN_VG_RESET 3
> > -The following additional RESET_INDEX value is valid for i.MX6:
> > -IPU2_RESET 4
> > diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> > b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> > new file mode 100644
> > index 0000000..276a533
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> > @@ -0,0 +1,58 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2
> > +---
> > +title: Freescale i.MX System Reset Controller
> > +
> > +maintainers:
> > + - Philipp Zabel <p.zabel@pengutronix.de>
> > +
> > +description: |
> > + The system reset controller can be used to reset the GPU, VPU,
> > + IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
> > + nodes should specify the reset line on the SRC in their resets
> > + property, containing a phandle to the SRC device node and a
> > + RESET_INDEX specifying which module to reset, as described in
> > + reset.txt
> > +
> > + The following RESET_INDEX values are valid for i.MX5:
> > + GPU_RESET 0
> > + VPU_RESET 1
> > + IPU1_RESET 2
> > + OPEN_VG_RESET 3
> > + The following additional RESET_INDEX value is valid for i.MX6:
> > + IPU2_RESET 4
> > +
> > +properties:
> > + compatible:
> > + items:
> > + - const: "fsl,imx51-src"
>
> What about mx6?
Will add all i.MX6 compatible according to DT.
>
> > +
> > + reg:
> > + maxItems: 1
> > +
> > + interrupts:
>
> Need description here
OK.
>
> > + minItems: 1
> > + maxItems: 2
> > +
> > + '#reset-cells':
> > + const: 1
> > +
> > +required:
> > + - compatible
> > + - reg
> > + - interrupts
> > + - '#reset-cells'
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + src@73fd0000 {
>
> s/src/reset-controller
OK.
Anson
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 3/3] hwmon: sparx5: Add Sparx5 SoC temperature driver
From: Lars Povlsen @ 2020-05-18 9:57 UTC (permalink / raw)
To: Guenter Roeck
Cc: linux-hwmon, devicetree, Alexandre Belloni, Jean Delvare,
linux-kernel, Microchip Linux Driver Support, SoC Team,
linux-arm-kernel, Lars Povlsen
In-Reply-To: <20200513152018.GA60367@roeck-us.net>
Guenter Roeck writes:
> On Wed, May 13, 2020 at 03:41:40PM +0200, Lars Povlsen wrote:
>> This patch adds a temperature sensor driver to the Sparx5 SoC.
>>
>> Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
>> Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com>
>> ---
>> drivers/hwmon/Kconfig | 10 +++
>> drivers/hwmon/Makefile | 2 +-
>> drivers/hwmon/sparx5-temp.c | 154 ++++++++++++++++++++++++++++++++++++
>> 3 files changed, 165 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/hwmon/sparx5-temp.c
>>
>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>> index 4c62f900bf7e8..130cb1f1748ff 100644
>> --- a/drivers/hwmon/Kconfig
>> +++ b/drivers/hwmon/Kconfig
>> @@ -480,6 +480,16 @@ config SENSORS_I5K_AMB
>> This driver can also be built as a module. If so, the module
>> will be called i5k_amb.
>>
>> +config SENSORS_SPARX5
>> + tristate "Sparx5 SoC temperature sensor"
>> + depends on ARCH_SPARX5
>> + help
>> + If you say yes here you get support for temperature monitoring
>> + with the Microchip Sparx5 SoC.
>> +
>> + This driver can also be built as a module. If so, the module
>> + will be called sparx5-temp.
>> +
>> config SENSORS_F71805F
>> tristate "Fintek F71805F/FG, F71806F/FG and F71872F/FG"
>> depends on !PPC
>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>> index b0b9c8e571762..28a09986b7a62 100644
>> --- a/drivers/hwmon/Makefile
>> +++ b/drivers/hwmon/Makefile
>> @@ -64,6 +64,7 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
>> obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
>> obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
>> obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
>> +obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
>> obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
>> obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
>> obj-$(CONFIG_SENSORS_F75375S) += f75375s.o
>> @@ -190,4 +191,3 @@ obj-$(CONFIG_SENSORS_OCC) += occ/
>> obj-$(CONFIG_PMBUS) += pmbus/
>>
>> ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG
>> -
>> diff --git a/drivers/hwmon/sparx5-temp.c b/drivers/hwmon/sparx5-temp.c
>> new file mode 100644
>> index 0000000000000..bf9dd102a9825
>> --- /dev/null
>> +++ b/drivers/hwmon/sparx5-temp.c
>> @@ -0,0 +1,154 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/* Sparx5 SoC temperature sensor driver
>> + *
>> + * Copyright (C) 2020 Lars Povlsen <lars.povlsen@microchip.com>
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/hwmon.h>
>> +#include <linux/hwmon-sysfs.h>
>
> Unnecessary include
Ack.
>
>> +#include <linux/init.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +
>> +#define TEMP_CTRL 0
>> +#define TEMP_CFG 4
>> +#define TEMP_CFG_CYCLES GENMASK(24, 15)
>> +#define TEMP_CFG_CYCLES_OFF 15
>> +#define TEMP_CFG_ENA BIT(0)
>> +#define TEMP_STAT 8
>> +#define TEMP_STAT_VALID BIT(12)
>> +#define TEMP_STAT_TEMP GENMASK(11, 0)
>> +
>> +struct s5_hwmon {
>> + void __iomem *base;
>> +};
>> +
>> +static void s5_temp_enable(struct s5_hwmon *hwmon)
>> +{
>> + u32 val = readl(hwmon->base + TEMP_CFG);
>> + u32 clk = 250;
>> +
>> + val &= ~TEMP_CFG_CYCLES;
>> + val |= (clk << TEMP_CFG_CYCLES_OFF);
>> + val |= TEMP_CFG_ENA;
>> +
>> + writel(val, hwmon->base + TEMP_CFG);
>> +}
>> +
>> +static void s5_temp_disable(void *data)
>> +{
>> + struct s5_hwmon *hwmon = data;
>> + u32 val = readl(hwmon->base + TEMP_CFG);
>> +
>> + val &= ~TEMP_CFG_ENA;
>> +
>> + writel(val, hwmon->base + TEMP_CFG);
>> +}
>> +
>> +static int s5_read(struct device *dev, enum hwmon_sensor_types type,
>> + u32 attr, int channel, long *temp)
>> +{
>> + struct s5_hwmon *hwmon = dev_get_drvdata(dev);
>> + int rc = 0, value;
>> + u32 stat;
>> +
>> + switch (attr) {
>> + case hwmon_temp_input:
>> + stat = readl_relaxed(hwmon->base + TEMP_STAT);
>> + if (stat & TEMP_STAT_VALID) {
>> + value = (stat & TEMP_STAT_TEMP);
>
> Unnecessary ( )
Removed.
>
>> + value = DIV_ROUND_CLOSEST(value * 3522, 4096) - 1094;
>> + value *= 100;
>> + *temp = value;
>> + } else
>> + rc = -EINVAL;
>
> -EINVAL is for bad used input. -EIO, maybe, unless there is a better error.
> Also,
>
> if (!(stat & TEMP_STAT_VALID))
> return -EIO;
Ok, changed.
> ...
>
> would be easier to read (and not result in a checkpatch warning).
>
>> + break;
>> + default:
>> + rc = -EOPNOTSUPP;
>
> break;
>
Added.
>> + }
>> +
>> + return rc;
>> +}
>> +
>> +static umode_t s5_is_visible(const void *_data, enum hwmon_sensor_types type,
>> + u32 attr, int channel)
>> +{
>> + if (type != hwmon_temp)
>> + return 0;
>> +
>> + switch (attr) {
>> + case hwmon_temp_input:
>> + return 0444;
>> + default:
>> + return 0;
>> + }
>> +}
>> +
>> +static const struct hwmon_channel_info *s5_info[] = {
>> + HWMON_CHANNEL_INFO(chip,
>> + HWMON_C_REGISTER_TZ),
>> + HWMON_CHANNEL_INFO(temp,
>> + HWMON_T_INPUT),
>> + NULL
>> +};
>> +
>> +static const struct hwmon_ops s5_hwmon_ops = {
>> + .is_visible = s5_is_visible,
>> + .read = s5_read,
>> +};
>> +
>> +static const struct hwmon_chip_info s5_chip_info = {
>> + .ops = &s5_hwmon_ops,
>> + .info = s5_info,
>> +};
>> +
>> +static int s5_temp_probe(struct platform_device *pdev)
>> +{
>> + struct device *hwmon_dev;
>> + struct s5_hwmon *hwmon;
>> + int err = 0;
>
> Unnecessary initialization
>
Removed
>> +
>> + hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
>> + if (!hwmon)
>> + return -ENOMEM;
>> +
>> + hwmon->base = devm_platform_ioremap_resource(pdev, 0);
>> + if (IS_ERR(hwmon->base))
>> + return PTR_ERR(hwmon->base);
>> +
>> + err = devm_add_action(&pdev->dev, s5_temp_disable, hwmon);
>> + if (err)
>> + return err;
>> +
>> + s5_temp_enable(hwmon);
>> +
>> + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
>> + "s5_temp",
>> + hwmon,
>> + &s5_chip_info,
>> + NULL);
>> +
>> + return PTR_ERR_OR_ZERO(hwmon_dev);
>> +}
>> +
>> +const struct of_device_id s5_temp_match[] = {
>> + { .compatible = "microchip,sparx5-temp" },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, s5_temp_match);
>> +
>> +static struct platform_driver s5_temp_driver = {
>> + .probe = s5_temp_probe,
>> + .driver = {
>> + .name = "sparx5-temp",
>> + .of_match_table = s5_temp_match,
>> + },
>> +};
>> +
>> +module_platform_driver(s5_temp_driver);
>> +
>> +MODULE_AUTHOR("Lars Povlsen <lars.povlsen@microchip.com>");
>> +MODULE_DESCRIPTION("Sparx5 SoC temperature sensor driver");
>> +MODULE_LICENSE("GPL");
>> --
>> 2.26.2
Thank you for your input, I will submit anew asap.
---Lars
--
Lars Povlsen,
Microchip
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* RE: [PATCH] dt-bindings: reset: Convert i.MX reset to json-schema
From: Anson Huang @ 2020-05-18 9:58 UTC (permalink / raw)
To: Philipp Zabel, robh+dt@kernel.org, shawnguo@kernel.org,
s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Cc: dl-linux-imx
In-Reply-To: <719ed778a36e7edfea080d42e9b837432e595d15.camel@pengutronix.de>
Hi, Philipp
> Subject: Re: [PATCH] dt-bindings: reset: Convert i.MX reset to json-schema
>
> Hi Anson,
>
> On Tue, 2020-05-12 at 10:23 +0800, Anson Huang wrote:
> > Convert the i.MX reset binding to DT schema format using json-schema.
> >
> > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
>
> thank you for the conversion.
>
> > ---
> > .../devicetree/bindings/reset/fsl,imx-src.txt | 49 ------------------
> > .../devicetree/bindings/reset/fsl,imx-src.yaml | 58
> ++++++++++++++++++++++
> > 2 files changed, 58 insertions(+), 49 deletions(-) delete mode
> > 100644 Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > create mode 100644
> > Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > b/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > deleted file mode 100644
> > index 6ed79e6..0000000
> > --- a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
> > +++ /dev/null
> > @@ -1,49 +0,0 @@
> > -Freescale i.MX System Reset Controller
> > -======================================
> > -
> > -Please also refer to reset.txt in this directory for common reset
> > -controller binding usage.
> > -
> > -Required properties:
> > -- compatible: Should be "fsl,<chip>-src"
> > -- reg: should be register base and length as documented in the
> > - datasheet
> > -- interrupts: Should contain SRC interrupt and CPU WDOG interrupt,
> > - in this order.
> > -- #reset-cells: 1, see below
> > -
> > -example:
> > -
> > -src: src@20d8000 {
> > - compatible = "fsl,imx6q-src";
> > - reg = <0x020d8000 0x4000>;
> > - interrupts = <0 91 0x04 0 96 0x04>;
> > - #reset-cells = <1>;
> > -};
> > -
> > -Specifying reset lines connected to IP modules
> > -==============================================
> > -
> > -The system reset controller can be used to reset the GPU, VPU, -IPU,
> > and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device -nodes
> > should specify the reset line on the SRC in their resets -property,
> > containing a phandle to the SRC device node and a -RESET_INDEX
> > specifying which module to reset, as described in -reset.txt
> > -
> > -example:
> > -
> > - ipu1: ipu@2400000 {
> > - resets = <&src 2>;
> > - };
> > - ipu2: ipu@2800000 {
> > - resets = <&src 4>;
> > - };
> > -
> > -The following RESET_INDEX values are valid for i.MX5:
> > -GPU_RESET 0
> > -VPU_RESET 1
> > -IPU1_RESET 2
> > -OPEN_VG_RESET 3
> > -The following additional RESET_INDEX value is valid for i.MX6:
> > -IPU2_RESET 4
> > diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> > b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> > new file mode 100644
> > index 0000000..276a533
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
> > @@ -0,0 +1,58 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2
> > +---
> > +$id:
> > +https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fdevi
> >
> +cetree.org%2Fschemas%2Freset%2Ffsl%2Cimx-src.yaml%23&data=02%
> 7C01
> >
> +%7Canson.huang%40nxp.com%7Cd622f3b31cca4b2e7b4808d7fb0fae99%7C
> 686ea1d
> >
> +3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C637253917082767416&s
> data=7e%2
> >
> +FAy3dz4goXW6rNH2tAl3z5oNQ%2FIZ6Nykn4uBZ7S20%3D&reserved=0
> > +$schema:
> > +https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fdevi
> >
> +cetree.org%2Fmeta-schemas%2Fcore.yaml%23&data=02%7C01%7Cans
> on.hua
> >
> +ng%40nxp.com%7Cd622f3b31cca4b2e7b4808d7fb0fae99%7C686ea1d3bc2b
> 4c6fa92
> >
> +cd99c5c301635%7C0%7C0%7C637253917082767416&sdata=U%2Fs60
> QKHaPc5Ar
> > +GxJP97FLL%2BsClgZkuSmeQWKofm4oQ%3D&reserved=0
> > +
> > +title: Freescale i.MX System Reset Controller
> > +
> > +maintainers:
> > + - Philipp Zabel <p.zabel@pengutronix.de>
> > +
> > +description: |
> > + The system reset controller can be used to reset the GPU, VPU,
> > + IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
> > + nodes should specify the reset line on the SRC in their resets
> > + property, containing a phandle to the SRC device node and a
> > + RESET_INDEX specifying which module to reset, as described in
> > + reset.txt
> > +
> > + The following RESET_INDEX values are valid for i.MX5:
> > + GPU_RESET 0
> > + VPU_RESET 1
> > + IPU1_RESET 2
> > + OPEN_VG_RESET 3
> > + The following additional RESET_INDEX value is valid for i.MX6:
> > + IPU2_RESET 4
> > +
> > +properties:
> > + compatible:
> > + items:
> > + - const: "fsl,imx51-src"
>
> "fsl,imx51-src" is the only compatible the driver matches on, but we have
> these combinations on compatible SRCs in the device trees:
>
> "fsl,imx50-src", "fsl,imx51-src"
> "fsl,imx51-src"
> "fsl,imx53-src", "fsl,imx51-src"
> "fsl,imx6q-src", "fsl,imx51-src"
> "fsl,imx6sl-src", "fsl,imx51-src"
> "fsl,imx6sll-src", "fsl,imx51-src"
> "fsl,imx6sx-src", "fsl,imx51-src"
> "fsl,imx6ul-src", "fsl,imx51-src"
>
> That could be described using oneOf and and an items list of const values per
> SoC like in the qcom bindings.
Yes, I noticed this and will add all of them in V2 per SoC, thanks.
Anson
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2 4/5] PCI: uniphier: Add iATU register support
From: Kunihiko Hayashi @ 2020-05-18 10:05 UTC (permalink / raw)
To: Gustavo Pimentel
Cc: devicetree@vger.kernel.org, Lorenzo Pieralisi, Masami Hiramatsu,
Jassi Brar, Jingoo Han, linux-kernel@vger.kernel.org,
Masahiro Yamada, Rob Herring, linux-pci@vger.kernel.org,
Bjorn Helgaas, linux-arm-kernel@lists.infradead.org
In-Reply-To: <DM5PR12MB1276D6181D86C8DF0F98427ADABD0@DM5PR12MB1276.namprd12.prod.outlook.com>
Hi Gustavo,
On 2020/05/15 22:16, Gustavo Pimentel wrote:
> Hi Kunihiko,
>
> On Fri, May 15, 2020 at 10:59:2, Kunihiko Hayashi
> <hayashi.kunihiko@socionext.com> wrote:
>
>> This gets iATU register area from reg property. In Synopsis DWC version
>
> s/Synopsis/Synopsys
> in all patches
Thank you for pointing out.
I'll fix and be careful about this.
Thank you,
---
Best Regards
Kunihiko Hayashi
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH V2] dt-bindings: reset: Convert i.MX reset to json-schema
From: Anson Huang @ 2020-05-18 10:03 UTC (permalink / raw)
To: p.zabel, robh+dt, shawnguo, s.hauer, kernel, festevam, devicetree,
linux-arm-kernel, linux-kernel
Cc: Linux-imx
Convert the i.MX reset binding to DT schema format using json-schema.
Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
Changes since V1:
- add the compatible strings per SoC;
- use "reset-controller" as node name instead of src;
- add interrupt items description.
---
.../devicetree/bindings/reset/fsl,imx-src.txt | 49 -----------
.../devicetree/bindings/reset/fsl,imx-src.yaml | 98 ++++++++++++++++++++++
2 files changed, 98 insertions(+), 49 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/reset/fsl,imx-src.txt
create mode 100644 Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt b/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
deleted file mode 100644
index 6ed79e6..0000000
--- a/Documentation/devicetree/bindings/reset/fsl,imx-src.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-Freescale i.MX System Reset Controller
-======================================
-
-Please also refer to reset.txt in this directory for common reset
-controller binding usage.
-
-Required properties:
-- compatible: Should be "fsl,<chip>-src"
-- reg: should be register base and length as documented in the
- datasheet
-- interrupts: Should contain SRC interrupt and CPU WDOG interrupt,
- in this order.
-- #reset-cells: 1, see below
-
-example:
-
-src: src@20d8000 {
- compatible = "fsl,imx6q-src";
- reg = <0x020d8000 0x4000>;
- interrupts = <0 91 0x04 0 96 0x04>;
- #reset-cells = <1>;
-};
-
-Specifying reset lines connected to IP modules
-==============================================
-
-The system reset controller can be used to reset the GPU, VPU,
-IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
-nodes should specify the reset line on the SRC in their resets
-property, containing a phandle to the SRC device node and a
-RESET_INDEX specifying which module to reset, as described in
-reset.txt
-
-example:
-
- ipu1: ipu@2400000 {
- resets = <&src 2>;
- };
- ipu2: ipu@2800000 {
- resets = <&src 4>;
- };
-
-The following RESET_INDEX values are valid for i.MX5:
-GPU_RESET 0
-VPU_RESET 1
-IPU1_RESET 2
-OPEN_VG_RESET 3
-The following additional RESET_INDEX value is valid for i.MX6:
-IPU2_RESET 4
diff --git a/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
new file mode 100644
index 0000000..7cd6095
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/fsl,imx-src.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reset/fsl,imx-src.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX System Reset Controller
+
+maintainers:
+ - Philipp Zabel <p.zabel@pengutronix.de>
+
+description: |
+ The system reset controller can be used to reset the GPU, VPU,
+ IPU, and OpenVG IP modules on i.MX5 and i.MX6 ICs. Those device
+ nodes should specify the reset line on the SRC in their resets
+ property, containing a phandle to the SRC device node and a
+ RESET_INDEX specifying which module to reset, as described in
+ reset.txt
+
+ The following RESET_INDEX values are valid for i.MX5:
+ GPU_RESET 0
+ VPU_RESET 1
+ IPU1_RESET 2
+ OPEN_VG_RESET 3
+ The following additional RESET_INDEX value is valid for i.MX6:
+ IPU2_RESET 4
+
+properties:
+ compatible:
+ oneOf:
+ - description: on i.MX51 the following compatible must be specified
+ items:
+ - const: "fsl,imx51-src"
+
+ - description: on i.MX50 the following compatibles must be specified
+ items:
+ - const: "fsl,imx50-src"
+ - const: "fsl,imx51-src"
+
+ - description: on i.MX53 the following compatibles must be specified
+ items:
+ - const: "fsl,imx53-src"
+ - const: "fsl,imx51-src"
+
+ - description: on i.MX6Q the following compatibles must be specified
+ items:
+ - const: "fsl,imx6q-src"
+ - const: "fsl,imx51-src"
+
+ - description: on i.MX6SX the following compatibles must be specified
+ items:
+ - const: "fsl,imx6sx-src"
+ - const: "fsl,imx51-src"
+
+ - description: on i.MX6SL the following compatibles must be specified
+ items:
+ - const: "fsl,imx6sl-src"
+ - const: "fsl,imx51-src"
+
+ - description: on i.MX6UL the following compatibles must be specified
+ items:
+ - const: "fsl,imx6ul-src"
+ - const: "fsl,imx51-src"
+
+ - description: on i.MX6SLL the following compatibles must be specified
+ items:
+ - const: "fsl,imx6sll-src"
+ - const: "fsl,imx51-src"
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: SRC interrupt
+ - description: CPU WDOG interrupts out of SRC
+ minItems: 1
+ maxItems: 2
+
+ '#reset-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - '#reset-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ reset-controller@73fd0000 {
+ compatible = "fsl,imx51-src";
+ reg = <0x73fd0000 0x4000>;
+ interrupts = <75>;
+ #reset-cells = <1>;
+ };
--
2.7.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v3 2/4] kasan: record and print the free track
From: Dmitry Vyukov @ 2020-05-18 10:18 UTC (permalink / raw)
To: Walter Wu
Cc: wsd_upstream, linux-mediatek, LKML, kasan-dev, Linux-MM,
Alexander Potapenko, Matthias Brugger, Andrey Ryabinin, Linux ARM
In-Reply-To: <20200518062730.4665-1-walter-zh.wu@mediatek.com>
On Mon, May 18, 2020 at 8:27 AM Walter Wu <walter-zh.wu@mediatek.com> wrote:
>
> Move free track from slub alloc meta-data to slub free meta-data in
> order to make struct kasan_free_meta size is 16 bytes. It is a good
> size because it is the minimal redzone size and a good number of
> alignment.
>
> For free track in generic KASAN, we do the modification in struct
> kasan_alloc_meta and kasan_free_meta:
> - remove free track from kasan_alloc_meta.
> - add free track into kasan_free_meta.
>
> [1]https://bugzilla.kernel.org/show_bug.cgi?id=198437
>
> Signed-off-by: Walter Wu <walter-zh.wu@mediatek.com>
> Suggested-by: Dmitry Vyukov <dvyukov@google.com>
> Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
> Cc: Dmitry Vyukov <dvyukov@google.com>
> Cc: Alexander Potapenko <glider@google.com>
> ---
> mm/kasan/common.c | 33 ++++++++++-----------------------
> mm/kasan/generic.c | 18 ++++++++++++++++++
> mm/kasan/kasan.h | 7 +++++++
> mm/kasan/report.c | 20 --------------------
> mm/kasan/tags.c | 37 +++++++++++++++++++++++++++++++++++++
> 5 files changed, 72 insertions(+), 43 deletions(-)
>
> diff --git a/mm/kasan/common.c b/mm/kasan/common.c
> index 8bc618289bb1..6500bc2bb70c 100644
> --- a/mm/kasan/common.c
> +++ b/mm/kasan/common.c
> @@ -51,7 +51,7 @@ depot_stack_handle_t kasan_save_stack(gfp_t flags)
> return stack_depot_save(entries, nr_entries, flags);
> }
>
> -static inline void set_track(struct kasan_track *track, gfp_t flags)
> +void kasan_set_track(struct kasan_track *track, gfp_t flags)
> {
> track->pid = current->pid;
> track->stack = kasan_save_stack(flags);
> @@ -249,9 +249,7 @@ void kasan_cache_create(struct kmem_cache *cache, unsigned int *size,
> *size += sizeof(struct kasan_alloc_meta);
>
> /* Add free meta. */
> - if (IS_ENABLED(CONFIG_KASAN_GENERIC) &&
> - (cache->flags & SLAB_TYPESAFE_BY_RCU || cache->ctor ||
> - cache->object_size < sizeof(struct kasan_free_meta))) {
> + if (IS_ENABLED(CONFIG_KASAN_GENERIC)) {
Why do we need to increase object size unconditionally?
We only store info in free track when the object is free, so I would
assume we still can generally overlap free track and the object
itself. We store free track at the same time we use the quarantine
link, and the quarantine link was overlapped with the object just
fine.
With this change we indeed increase object size, which we do not want
in general.
> cache->kasan_info.free_meta_offset = *size;
> *size += sizeof(struct kasan_free_meta);
> }
> @@ -299,24 +297,6 @@ struct kasan_free_meta *get_free_info(struct kmem_cache *cache,
> return (void *)object + cache->kasan_info.free_meta_offset;
> }
>
> -
> -static void kasan_set_free_info(struct kmem_cache *cache,
> - void *object, u8 tag)
> -{
> - struct kasan_alloc_meta *alloc_meta;
> - u8 idx = 0;
> -
> - alloc_meta = get_alloc_info(cache, object);
> -
> -#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> - idx = alloc_meta->free_track_idx;
> - alloc_meta->free_pointer_tag[idx] = tag;
> - alloc_meta->free_track_idx = (idx + 1) % KASAN_NR_FREE_STACKS;
> -#endif
> -
> - set_track(&alloc_meta->free_track[idx], GFP_NOWAIT);
> -}
> -
> void kasan_poison_slab(struct page *page)
> {
> unsigned long i;
> @@ -396,6 +376,13 @@ void * __must_check kasan_init_slab_obj(struct kmem_cache *cache,
> alloc_info = get_alloc_info(cache, object);
> __memset(alloc_info, 0, sizeof(*alloc_info));
>
> + if (IS_ENABLED(CONFIG_KASAN_GENERIC)) {
> + struct kasan_free_meta *free_info;
> +
> + free_info = get_free_info(cache, object);
> + __memset(free_info, 0, sizeof(*free_info));
If we overlap free track with object, this will not be needed as well, right?
> + }
> +
> if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
> object = set_tag(object,
> assign_tag(cache, object, true, false));
> @@ -492,7 +479,7 @@ static void *__kasan_kmalloc(struct kmem_cache *cache, const void *object,
> KASAN_KMALLOC_REDZONE);
>
> if (cache->flags & SLAB_KASAN)
> - set_track(&get_alloc_info(cache, object)->alloc_track, flags);
> + kasan_set_track(&get_alloc_info(cache, object)->alloc_track, flags);
>
> return set_tag(object, tag);
> }
> diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
> index 78d8e0a75a8a..988bc095b738 100644
> --- a/mm/kasan/generic.c
> +++ b/mm/kasan/generic.c
> @@ -345,3 +345,21 @@ void kasan_record_aux_stack(void *addr)
> alloc_info->rcu_stack[1] = alloc_info->rcu_stack[0];
> alloc_info->rcu_stack[0] = kasan_save_stack(GFP_NOWAIT);
> }
> +
> +void kasan_set_free_info(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_free_meta *free_meta;
> +
> + free_meta = get_free_info(cache, object);
> + kasan_set_track(&free_meta->free_track, GFP_NOWAIT);
> +}
> +
> +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_free_meta *free_meta;
> +
> + free_meta = get_free_info(cache, object);
> + return &free_meta->free_track;
> +}
> diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
> index 870c5dd07756..87ee3626b8b0 100644
> --- a/mm/kasan/kasan.h
> +++ b/mm/kasan/kasan.h
> @@ -127,6 +127,9 @@ struct kasan_free_meta {
> * Otherwise it might be used for the allocator freelist.
> */
> struct qlist_node quarantine_link;
> +#ifdef CONFIG_KASAN_GENERIC
> + struct kasan_track free_track;
> +#endif
> };
>
> struct kasan_alloc_meta *get_alloc_info(struct kmem_cache *cache,
> @@ -168,6 +171,10 @@ void kasan_report_invalid_free(void *object, unsigned long ip);
> struct page *kasan_addr_to_page(const void *addr);
>
> depot_stack_handle_t kasan_save_stack(gfp_t flags);
> +void kasan_set_track(struct kasan_track *track, gfp_t flags);
> +void kasan_set_free_info(struct kmem_cache *cache, void *object, u8 tag);
> +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> + void *object, u8 tag);
>
> #if defined(CONFIG_KASAN_GENERIC) && \
> (defined(CONFIG_SLAB) || defined(CONFIG_SLUB))
> diff --git a/mm/kasan/report.c b/mm/kasan/report.c
> index 5ee66cf7e27c..7e9f9f6d5e85 100644
> --- a/mm/kasan/report.c
> +++ b/mm/kasan/report.c
> @@ -159,26 +159,6 @@ static void describe_object_addr(struct kmem_cache *cache, void *object,
> (void *)(object_addr + cache->object_size));
> }
>
> -static struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> - void *object, u8 tag)
> -{
> - struct kasan_alloc_meta *alloc_meta;
> - int i = 0;
> -
> - alloc_meta = get_alloc_info(cache, object);
> -
> -#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> - for (i = 0; i < KASAN_NR_FREE_STACKS; i++) {
> - if (alloc_meta->free_pointer_tag[i] == tag)
> - break;
> - }
> - if (i == KASAN_NR_FREE_STACKS)
> - i = alloc_meta->free_track_idx;
> -#endif
> -
> - return &alloc_meta->free_track[i];
> -}
> -
> #ifdef CONFIG_KASAN_GENERIC
> static void print_stack(depot_stack_handle_t stack)
> {
> diff --git a/mm/kasan/tags.c b/mm/kasan/tags.c
> index 25b7734e7013..201dee5d6ae0 100644
> --- a/mm/kasan/tags.c
> +++ b/mm/kasan/tags.c
> @@ -162,3 +162,40 @@ void __hwasan_tag_memory(unsigned long addr, u8 tag, unsigned long size)
> kasan_poison_shadow((void *)addr, size, tag);
> }
> EXPORT_SYMBOL(__hwasan_tag_memory);
> +
> +void kasan_set_free_info(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_alloc_meta *alloc_meta;
> + u8 idx = 0;
> +
> + alloc_meta = get_alloc_info(cache, object);
> +
> +#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> + idx = alloc_meta->free_track_idx;
> + alloc_meta->free_pointer_tag[idx] = tag;
> + alloc_meta->free_track_idx = (idx + 1) % KASAN_NR_FREE_STACKS;
> +#endif
> +
> + kasan_set_track(&alloc_meta->free_track[idx], GFP_NOWAIT);
> +}
> +
> +struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> + void *object, u8 tag)
> +{
> + struct kasan_alloc_meta *alloc_meta;
> + int i = 0;
> +
> + alloc_meta = get_alloc_info(cache, object);
> +
> +#ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> + for (i = 0; i < KASAN_NR_FREE_STACKS; i++) {
> + if (alloc_meta->free_pointer_tag[i] == tag)
> + break;
> + }
> + if (i == KASAN_NR_FREE_STACKS)
> + i = alloc_meta->free_track_idx;
> +#endif
> +
> + return &alloc_meta->free_track[i];
> +}
> --
> 2.18.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v3 1/4] rcu/kasan: record and print call_rcu() call stack
From: Dmitry Vyukov @ 2020-05-18 10:21 UTC (permalink / raw)
To: Walter Wu
Cc: wsd_upstream, Paul E . McKenney, Linux-MM, Lai Jiangshan,
Josh Triplett, kasan-dev, LKML, Joel Fernandes, linux-mediatek,
Alexander Potapenko, Matthias Brugger, Andrey Ryabinin,
Andrew Morton, Linux ARM, Mathieu Desnoyers
In-Reply-To: <20200518062603.4570-1-walter-zh.wu@mediatek.com>
On Mon, May 18, 2020 at 8:26 AM Walter Wu <walter-zh.wu@mediatek.com> wrote:
>
> This feature will record the last two call_rcu() call stack and
> prints up to 2 call_rcu() call stacks in KASAN report.
>
> When call_rcu() is called, we store the call_rcu() call stack into
> slub alloc meta-data, so that the KASAN report can print rcu stack.
>
> [1]https://bugzilla.kernel.org/show_bug.cgi?id=198437
> [2]https://groups.google.com/forum/#!searchin/kasan-dev/better$20stack$20traces$20for$20rcu%7Csort:date/kasan-dev/KQsjT_88hDE/7rNUZprRBgAJ
>
> Signed-off-by: Walter Wu <walter-zh.wu@mediatek.com>
> Suggested-by: Dmitry Vyukov <dvyukov@google.com>
> Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
> Cc: Dmitry Vyukov <dvyukov@google.com>
> Cc: Alexander Potapenko <glider@google.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Paul E. McKenney <paulmck@kernel.org>
> Cc: Josh Triplett <josh@joshtriplett.org>
> Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Cc: Lai Jiangshan <jiangshanlai@gmail.com>
> Cc: Joel Fernandes <joel@joelfernandes.org>
> ---
> include/linux/kasan.h | 2 ++
> kernel/rcu/tree.c | 2 ++
> lib/Kconfig.kasan | 2 ++
> mm/kasan/common.c | 4 ++--
> mm/kasan/generic.c | 20 ++++++++++++++++++++
> mm/kasan/kasan.h | 10 ++++++++++
> mm/kasan/report.c | 24 ++++++++++++++++++++++++
> 7 files changed, 62 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/kasan.h b/include/linux/kasan.h
> index 31314ca7c635..23b7ee00572d 100644
> --- a/include/linux/kasan.h
> +++ b/include/linux/kasan.h
> @@ -174,11 +174,13 @@ static inline size_t kasan_metadata_size(struct kmem_cache *cache) { return 0; }
>
> void kasan_cache_shrink(struct kmem_cache *cache);
> void kasan_cache_shutdown(struct kmem_cache *cache);
> +void kasan_record_aux_stack(void *ptr);
>
> #else /* CONFIG_KASAN_GENERIC */
>
> static inline void kasan_cache_shrink(struct kmem_cache *cache) {}
> static inline void kasan_cache_shutdown(struct kmem_cache *cache) {}
> +static inline void kasan_record_aux_stack(void *ptr) {}
>
> #endif /* CONFIG_KASAN_GENERIC */
>
> diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
> index 06548e2ebb72..36a4ff7f320b 100644
> --- a/kernel/rcu/tree.c
> +++ b/kernel/rcu/tree.c
> @@ -57,6 +57,7 @@
> #include <linux/slab.h>
> #include <linux/sched/isolation.h>
> #include <linux/sched/clock.h>
> +#include <linux/kasan.h>
> #include "../time/tick-internal.h"
>
> #include "tree.h"
> @@ -2668,6 +2669,7 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func)
> head->func = func;
> head->next = NULL;
> local_irq_save(flags);
> + kasan_record_aux_stack(head);
> rdp = this_cpu_ptr(&rcu_data);
>
> /* Add the callback to our list. */
> diff --git a/lib/Kconfig.kasan b/lib/Kconfig.kasan
> index 81f5464ea9e1..4e83cf6e3caa 100644
> --- a/lib/Kconfig.kasan
> +++ b/lib/Kconfig.kasan
> @@ -58,6 +58,8 @@ config KASAN_GENERIC
> For better error detection enable CONFIG_STACKTRACE.
> Currently CONFIG_KASAN_GENERIC doesn't work with CONFIG_DEBUG_SLAB
> (the resulting kernel does not boot).
> + In generic mode KASAN prints the last two call_rcu() call stacks in
> + reports.
>
> config KASAN_SW_TAGS
> bool "Software tag-based mode"
> diff --git a/mm/kasan/common.c b/mm/kasan/common.c
> index 2906358e42f0..8bc618289bb1 100644
> --- a/mm/kasan/common.c
> +++ b/mm/kasan/common.c
> @@ -41,7 +41,7 @@
> #include "kasan.h"
> #include "../slab.h"
>
> -static inline depot_stack_handle_t save_stack(gfp_t flags)
> +depot_stack_handle_t kasan_save_stack(gfp_t flags)
> {
> unsigned long entries[KASAN_STACK_DEPTH];
> unsigned int nr_entries;
> @@ -54,7 +54,7 @@ static inline depot_stack_handle_t save_stack(gfp_t flags)
> static inline void set_track(struct kasan_track *track, gfp_t flags)
> {
> track->pid = current->pid;
> - track->stack = save_stack(flags);
> + track->stack = kasan_save_stack(flags);
> }
>
> void kasan_enable_current(void)
> diff --git a/mm/kasan/generic.c b/mm/kasan/generic.c
> index 56ff8885fe2e..78d8e0a75a8a 100644
> --- a/mm/kasan/generic.c
> +++ b/mm/kasan/generic.c
> @@ -325,3 +325,23 @@ DEFINE_ASAN_SET_SHADOW(f2);
> DEFINE_ASAN_SET_SHADOW(f3);
> DEFINE_ASAN_SET_SHADOW(f5);
> DEFINE_ASAN_SET_SHADOW(f8);
> +
> +void kasan_record_aux_stack(void *addr)
> +{
> + struct page *page = kasan_addr_to_page(addr);
> + struct kmem_cache *cache;
> + struct kasan_alloc_meta *alloc_info;
> + void *object;
> +
> + if (!(page && PageSlab(page)))
> + return;
> +
> + cache = page->slab_cache;
> + object = nearest_obj(cache, page, addr);
> + alloc_info = get_alloc_info(cache, object);
> +
> + /* record last two call_rcu() call stacks */
> + if (alloc_info->rcu_stack[0])
Do we need this if?
If we do "alloc_info->rcu_stack[1] = alloc_info->rcu_stack[0]"
unconditionally, then we will just move 0 from [0] to [1], which
should be 0 at this point anyway.
I think it will be more reasonable to rename rcu_stack to aux_stack,
the function that stores the stacks is kasan_record_aux_stack.
> + alloc_info->rcu_stack[1] = alloc_info->rcu_stack[0];
> + alloc_info->rcu_stack[0] = kasan_save_stack(GFP_NOWAIT);
> +}
> diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
> index e8f37199d885..870c5dd07756 100644
> --- a/mm/kasan/kasan.h
> +++ b/mm/kasan/kasan.h
> @@ -104,7 +104,15 @@ struct kasan_track {
>
> struct kasan_alloc_meta {
> struct kasan_track alloc_track;
> +#ifdef CONFIG_KASAN_GENERIC
> + /*
> + * call_rcu() call stack is stored into struct kasan_alloc_meta.
> + * The free stack is stored into struct kasan_free_meta.
> + */
> + depot_stack_handle_t rcu_stack[2];
> +#else
> struct kasan_track free_track[KASAN_NR_FREE_STACKS];
> +#endif
> #ifdef CONFIG_KASAN_SW_TAGS_IDENTIFY
> u8 free_pointer_tag[KASAN_NR_FREE_STACKS];
> u8 free_track_idx;
> @@ -159,6 +167,8 @@ void kasan_report_invalid_free(void *object, unsigned long ip);
>
> struct page *kasan_addr_to_page(const void *addr);
>
> +depot_stack_handle_t kasan_save_stack(gfp_t flags);
> +
> #if defined(CONFIG_KASAN_GENERIC) && \
> (defined(CONFIG_SLAB) || defined(CONFIG_SLUB))
> void quarantine_put(struct kasan_free_meta *info, struct kmem_cache *cache);
> diff --git a/mm/kasan/report.c b/mm/kasan/report.c
> index 80f23c9da6b0..5ee66cf7e27c 100644
> --- a/mm/kasan/report.c
> +++ b/mm/kasan/report.c
> @@ -179,6 +179,17 @@ static struct kasan_track *kasan_get_free_track(struct kmem_cache *cache,
> return &alloc_meta->free_track[i];
> }
>
> +#ifdef CONFIG_KASAN_GENERIC
> +static void print_stack(depot_stack_handle_t stack)
> +{
> + unsigned long *entries;
> + unsigned int nr_entries;
> +
> + nr_entries = stack_depot_fetch(stack, &entries);
> + stack_trace_print(entries, nr_entries, 0);
> +}
> +#endif
> +
> static void describe_object(struct kmem_cache *cache, void *object,
> const void *addr, u8 tag)
> {
> @@ -192,6 +203,19 @@ static void describe_object(struct kmem_cache *cache, void *object,
> free_track = kasan_get_free_track(cache, object, tag);
> print_track(free_track, "Freed");
> pr_err("\n");
> +
> +#ifdef CONFIG_KASAN_GENERIC
> + if (alloc_info->rcu_stack[0]) {
> + pr_err("Last one call_rcu() call stack:\n");
> + print_stack(alloc_info->rcu_stack[0]);
> + pr_err("\n");
> + }
> + if (alloc_info->rcu_stack[1]) {
> + pr_err("Second to last call_rcu() call stack:\n");
> + print_stack(alloc_info->rcu_stack[1]);
> + pr_err("\n");
> + }
> +#endif
> }
>
> describe_object_addr(cache, object, addr);
> --
> 2.18.0
>
> --
> You received this message because you are subscribed to the Google Groups "kasan-dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to kasan-dev+unsubscribe@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/kasan-dev/20200518062603.4570-1-walter-zh.wu%40mediatek.com.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 0/3] arm64: perf: Add support for Perf NMI interrupts
From: Mark Rutland @ 2020-05-18 10:45 UTC (permalink / raw)
To: Lecopzer Chen, alexandru.elisei
Cc: Sumit Garg, Jian-Lin Chen, Will Deacon, alexander.shishkin,
Catalin Marinas, yj.chiang, Linux Kernel Mailing List, acme,
Peter Zijlstra, mingo, linux-mediatek, linux-arm-kernel,
matthias.bgg, namhyung, jolsa, julien.thierry.kdev
In-Reply-To: <CANr2M19unLW8n0P2DiOYEZ=GZcaD-L2ygPht_5HNtNZ6e4h6xQ@mail.gmail.com>
Hi all,
On Mon, May 18, 2020 at 02:26:00PM +0800, Lecopzer Chen wrote:
> HI Sumit,
>
> Thanks for your information.
>
> I've already implemented IPI (same as you did [1], little difference
> in detail), hardlockup detector and perf in last year(2019) for
> debuggability.
> And now we tend to upstream to reduce kernel maintaining effort.
> I'm glad if someone in ARM can do this work :)
>
> Hi Julien,
>
> Does any Arm maintainers can proceed this action?
Alexandru (Cc'd) has been rebasing and reworking Julien's patches, which
is my preferred approach.
I understand that's not quite ready for posting since he's investigating
some of the nastier subtleties (e.g. mutual exclusion with the NMI), but
maybe we can put the work-in-progress patches somewhere in the mean
time.
Alexandru, do you have an idea of what needs to be done, and/or when you
expect you could post that?
Thanks,
Mark.
> This is really useful in debugging.
> Thank you!!
>
>
>
> [1] https://lkml.org/lkml/2020/4/24/328
>
>
> Lecopzer
>
> Sumit Garg <sumit.garg@linaro.org> 於 2020年5月18日 週一 下午1:46寫道:
> >
> > + Julien
> >
> > Hi Lecopzer,
> >
> > On Sat, 16 May 2020 at 18:20, Lecopzer Chen <lecopzer@gmail.com> wrote:
> > >
> > > These series implement Perf NMI funxtionality and depends on
> > > Pseudo NMI [1] which has been upstreamed.
> > >
> > > In arm64 with GICv3, Pseudo NMI was implemented for NMI-like interruts.
> > > That can be extended to Perf NMI which is the prerequisite for hard-lockup
> > > detector which had already a standard interface inside Linux.
> > >
> > > Thus the first step we need to implement perf NMI interface and make sure
> > > it works fine.
> > >
> >
> > This is something that is already implemented via Julien's patch-set
> > [1]. Its v4 has been floating since July, 2019 and I couldn't find any
> > major blocking comments but not sure why things haven't progressed
> > further.
> >
> > Maybe Julien or Arm maintainers can provide updates on existing
> > patch-set [1] and how we should proceed further with this interesting
> > feature.
> >
> > And regarding hard-lockup detection, I have been able to enable it
> > based on perf NMI events using Julien's perf patch-set [1]. Have a
> > look at the patch here [2].
> >
> > [1] https://patchwork.kernel.org/cover/11047407/
> > [2] http://lists.infradead.org/pipermail/linux-arm-kernel/2020-May/732227.html
> >
> > -Sumit
> >
> > > Perf NMI has been test by dd if=/dev/urandom of=/dev/null like the link [2]
> > > did.
> > >
> > > [1] https://lkml.org/lkml/2019/1/31/535
> > > [2] https://www.linaro.org/blog/debugging-arm-kernels-using-nmifiq
> > >
> > >
> > > Lecopzer Chen (3):
> > > arm_pmu: Add support for perf NMI interrupts registration
> > > arm64: perf: Support NMI context for perf event ISR
> > > arm64: Kconfig: Add support for the Perf NMI
> > >
> > > arch/arm64/Kconfig | 10 +++++++
> > > arch/arm64/kernel/perf_event.c | 36 ++++++++++++++++++------
> > > drivers/perf/arm_pmu.c | 51 ++++++++++++++++++++++++++++++----
> > > include/linux/perf/arm_pmu.h | 6 ++++
> > > 4 files changed, 88 insertions(+), 15 deletions(-)
> > >
> > > --
> > > 2.25.1
> > >
> > >
> > > _______________________________________________
> > > linux-arm-kernel mailing list
> > > linux-arm-kernel@lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCHv2 0/7] Support inhibiting input devices
From: Andrzej Pietrasiewicz @ 2020-05-18 10:48 UTC (permalink / raw)
To: Hans de Goede, linux-input, linux-acpi, linux-iio,
linux-arm-kernel, linux-samsung-soc, linux-tegra, patches,
ibm-acpi-devel, platform-driver-x86
Cc: Nick Dyer, Benjamin Tissoires, Laxman Dewangan,
Peter Meerwald-Stadler, kernel, Fabio Estevam, Lars-Peter Clausen,
Krzysztof Kozlowski, Jonathan Hunter, Kukjin Kim, NXP Linux Team,
Sylvain Lemieux, Len Brown, Peter Hutterer, Michael Hennerich,
Sascha Hauer, Henrique de Moraes Holschuh, Vladimir Zapolskiy,
Barry Song, Ferruh Yigit, Dmitry Torokhov, Rafael J . Wysocki,
Thierry Reding, Sangwon Jee, Pengutronix Kernel Team,
Hartmut Knaack, Shawn Guo, Jonathan Cameron
In-Reply-To: <842b95bb-8391-5806-fe65-be64b02de122@redhat.com>
Hi Hans,
W dniu 15.05.2020 o 20:19, Hans de Goede pisze:
> Hi Andrezj,
>
> On 5/15/20 6:49 PM, Andrzej Pietrasiewicz wrote:
>> Userspace might want to implement a policy to temporarily disregard input
>> from certain devices, including not treating them as wakeup sources.
>>
>> An example use case is a laptop, whose keyboard can be folded under the
>> screen to create tablet-like experience. The user then must hold the laptop
>> in such a way that it is difficult to avoid pressing the keyboard keys. It
>> is therefore desirable to temporarily disregard input from the keyboard,
>> until it is folded back. This obviously is a policy which should be kept
>> out of the kernel, but the kernel must provide suitable means to implement
>> such a policy.
>
> Actually libinput already binds together (inside libinput) SW_TABLET_MODE
> generating evdev nodes and e.g. internal keyboards on devices with 360°
> hinges for this reason. libinput simply closes the /dev/input/event#
> node when folded and re-opens it when the keyboard should become active
> again. Thus not only suppresses events but allows e.g. touchpads to
> enter runtime suspend mode which saves power. Typically closing the
> /dev/input/event# node will also disable the device as wakeup source.
>
> So I wonder what this series actually adds for functionality for
> userspace which can not already be achieved this way?
>
> I also noticed that you keep the device open (do not call the
> input_device's close callback) when inhibited and just throw away
I'm not sure if I understand you correctly, it is called:
+static inline void input_stop(struct input_dev *dev)
+{
+ if (dev->poller)
+ input_dev_poller_stop(dev->poller);
+ if (dev->close)
+ dev->close(dev);
^^^^^^^^^^^^^^^^
+static int input_inhibit(struct input_dev *dev)
+{
+ int ret = 0;
+
+ mutex_lock(&dev->mutex);
+
+ if (dev->inhibited)
+ goto out;
+
+ if (dev->users) {
+ if (dev->inhibit) {
+ ret = dev->inhibit(dev);
+ if (ret)
+ goto out;
+ }
+ input_stop(dev);
^^^^^^^^^^^^^^^^
It will not be called when dev->users is zero, but if it is zero,
then nobody has opened the device yet so there is nothing to close.
Andrzej
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox