[PATCH 6/8] soc: qcom: Add PMIC secure register write helpers

From: Yassine Oudjana
Date: Mon Aug 08 2022 - 03:37:27 EST


From: Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx>

Some peripheral blocks in Qualcomm PMICs such as some in the
switch-mode battery charger and fuel gauge of PMI8994 and later
have secure registers that need to be unlocked first before being
written to. Add some helper functions to do what is needed to
write to those registers.

Signed-off-by: Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx>
---
MAINTAINERS | 6 +++
drivers/soc/qcom/Kconfig | 4 ++
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/pmic-sec-write.c | 82 +++++++++++++++++++++++++++++++
include/soc/qcom/pmic-sec-write.h | 9 ++++
5 files changed, 102 insertions(+)
create mode 100644 drivers/soc/qcom/pmic-sec-write.c
create mode 100644 include/soc/qcom/pmic-sec-write.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 7e9f5893c0eb..f6cf3a27d132 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16942,6 +16942,12 @@ S: Maintained
F: Documentation/devicetree/bindings/mtd/qcom,nandc.yaml
F: drivers/mtd/nand/raw/qcom_nandc.c

+QUALCOMM PMIC SECURE WRITE HELPERS
+M: Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx>
+L: linux-arm-msm@xxxxxxxxxxxxxxx
+S: Maintained
+F: drivers/soc/qcom/pmic-sec-write.c
+
QUALCOMM RMNET DRIVER
M: Subash Abhinov Kasiviswanathan <quic_subashab@xxxxxxxxxxx>
M: Sean Tranchetti <quic_stranche@xxxxxxxxxxx>
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index e0d7a5459562..ecc3aeb75ba7 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -91,6 +91,10 @@ config QCOM_PDR_HELPERS
tristate
select QCOM_QMI_HELPERS

+config QCOM_PMIC_SEC_WRITE
+ tristate
+ depends on REGMAP
+
config QCOM_QMI_HELPERS
tristate
depends on NET
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index d66604aff2b0..058f499820cd 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o
obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o
+obj-$(CONFIG_QCOM_PMIC_SEC_WRITE) += pmic-sec-write.o
diff --git a/drivers/soc/qcom/pmic-sec-write.c b/drivers/soc/qcom/pmic-sec-write.c
new file mode 100644
index 000000000000..8072c6115f2e
--- /dev/null
+++ b/drivers/soc/qcom/pmic-sec-write.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Helper functions for secure register writes on Qualcomm PMICs
+ */
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#define SEC_ACCESS_ADDR 0x00d0
+#define SEC_ACCESS_VALUE 0x00a5
+#define PERPH_BASE_MASK 0xff00
+
+static DEFINE_SPINLOCK(sec_access_lock);
+
+/**
+ * @brief qcom_pmic_sec_write() - Write to a secure register
+ *
+ * @param regmap Pointer to regmap
+ * @param addr Address of register to write into
+ * @param val Buffer to write bytes from
+ * @param len Length of register in bytes
+ * @return 0 on success, -errno on failure
+ *
+ * @details: Some blocks have registers that need to be unlocked first before
+ * being written to. This function unlocks secure registers in the peripheral
+ * block of a given register then writes a given value to the register.
+ */
+int qcom_pmic_sec_write(struct regmap *regmap, u16 addr, u8 *val, int len)
+{
+ unsigned long flags;
+ unsigned int perph_base;
+ int ret;
+
+ spin_lock_irqsave(&sec_access_lock, flags);
+
+ /* Get peripheral base of given register */
+ perph_base = (addr & PERPH_BASE_MASK);
+
+ ret = regmap_write(regmap, perph_base + SEC_ACCESS_ADDR,
+ SEC_ACCESS_VALUE);
+ if (ret)
+ goto out;
+
+ ret = regmap_bulk_write(regmap, addr, val, len);
+out:
+ spin_unlock_irqrestore(&sec_access_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(qcom_pmic_sec_write);
+
+/**
+ * @brief qcom_pmic_sec_masked_write() - Masked write to secure registers
+ *
+ * @param regmap Pointer to regmap
+ * @param addr Address of register to write into
+ * @param mask Mask to apply on value
+ * @param val value to be written
+ * @return 0 on success, -errno on failure
+ *
+ * @details: Masked version of smbchg_sec_write().
+ */
+int qcom_pmic_sec_masked_write(struct regmap *regmap, u16 addr, u8 mask, u8 val)
+{
+ int reg;
+ int ret;
+
+ ret = regmap_read(regmap, addr, &reg);
+ if (ret)
+ return ret;
+
+ reg &= ~mask;
+ reg |= val & mask;
+
+ return qcom_pmic_sec_write(regmap, addr, (u8 *)&reg, 1);
+}
+EXPORT_SYMBOL_GPL(qcom_pmic_sec_masked_write);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Qualcomm PMIC secure write helpers");
+MODULE_LICENSE("GPL");
diff --git a/include/soc/qcom/pmic-sec-write.h b/include/soc/qcom/pmic-sec-write.h
new file mode 100644
index 000000000000..cc2229620ca4
--- /dev/null
+++ b/include/soc/qcom/pmic-sec-write.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __SOC_ARCH_QCOM_PMIC_SEC_WRITE_H__
+#define __SOC_ARCH_QCOM_PMIC_SEC_WRITE_H__
+
+int qcom_pmic_sec_write(struct regmap *regmap, u16 addr, u8 *val, int len);
+int qcom_pmic_sec_masked_write(struct regmap *regmap, u16 addr, u8 mask, u8 val);
+
+#endif
--
2.37.1