Skip to contentSkip to conversation

regulator: add QCOM RPMh regulator driver

PATCH Superseded David Collins 6 participants 25 messages Apr 14, 2018 – May 3, 2018

Subsystems (3)

v2 of 9 Superseded Latest is v9 →

lore
Share & tools
lore
#0 lore raw

v2 cover regulator: add QCOM RPMh regulator driver

Hello,

This patch series adds a driver and device tree binding documentation for PMIC regulator control via Resource Power Manager-hardened (RPMh) on some Qualcomm Technologies, Inc. SoCs such as SDM845. RPMh is a hardware block which contains several accelerators which are used to manage various hardware resources that are shared between the processors of the SoC. The final hardware state of a regulator is determined within RPMh by performing max aggregation of the requests made by all of the processors.

The RPMh regulator driver depends upon the RPMh driver [1] and command DB driver [2] which are both still undergoing review.

Changes since v1 [3]:
- Addressed review feedback from Doug, Mark, and Stephen
- Replaced set_voltage()/get_voltage() callbacks with set_voltage_sel()/ get_voltage_sel()
- Added set_bypass()/get_bypass() callbacks for BOB pass-through mode control
- Removed top-level PMIC data structures
- Removed initialization variables from structs and passed them as function parameters
- Removed various comments and error messages
- Simplified mode handling
- Refactored per-PMIC rpmh-regulator data specification
- Simplified probe function
- Moved header into DT patch
- Removed redundant property listings from DT binding documentation

Thanks,
David

[1]: https://lkml.org/lkml/2018/4/5/480
[2]: https://lkml.org/lkml/2018/4/10/714
[3]: https://lkml.org/lkml/2018/3/16/1431

David Collins (2):
regulator: dt-bindings: add QCOM RPMh regulator bindings
regulator: add QCOM RPMh regulator driver

 .../bindings/regulator/qcom,rpmh-regulator.txt     | 207 +++++
 drivers/regulator/Kconfig                          |   9 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/qcom_rpmh-regulator.c            | 910 +++++++++++++++++++++
 .../dt-bindings/regulator/qcom,rpmh-regulator.h    |  36 +
 5 files changed, 1163 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
 create mode 100644 drivers/regulator/qcom_rpmh-regulator.c
 create mode 100644 include/dt-bindings/regulator/qcom,rpmh-regulator.h
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#1 lore raw

PATCH v2 1/2 regulator: dt-bindings: add QCOM RPMh regulator bindings

Introduce bindings for RPMh regulator devices found on some Qualcomm Technlogies, Inc. SoCs. These devices allow a given processor within the SoC to make PMIC regulator requests which are aggregated within the RPMh hardware block along with requests from other processors in the SoC to determine the final PMIC regulator hardware state.

---
 .../bindings/regulator/qcom,rpmh-regulator.txt     | 207 +++++++++++++++++++++
 .../dt-bindings/regulator/qcom,rpmh-regulator.h    |  36 ++++
 2 files changed, 243 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
 create mode 100644 include/dt-bindings/regulator/qcom,rpmh-regulator.h
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +207 -0
new file mode 100644
index 0000000..69748ea
@@ -0,0 +1,207 @@
+Qualcomm Technologies, Inc. RPMh Regulators
+
+rpmh-regulator devices support PMIC regulator management via the Voltage
+Regulator Manager (VRM) and Oscillator Buffer (XOB) RPMh accelerators.  The APPS
+processor communicates with these hardware blocks via a Resource State
+Coordinator (RSC) using command packets.  The VRM allows changing four
+parameters for a given regulator: enable state, output voltage, operating mode,
+and minimum headroom voltage.  The XOB allows changing only a single parameter
+for a given regulator: its enable state.  Despite its name, the XOB is capable
+of controlling the enable state of any PMIC peripheral.  It is used for clock
+buffers, low-voltage switches, and LDO/SMPS regulators which have a fixed
+voltage and mode.
+
+=======================
+Required Node Structure
+=======================
+
+RPMh regulators must be described in two levels of device nodes.  The first
+level describes the PMIC containing the regulators and must reside within an
+RPMh device node.  The second level describes each regulator within the PMIC
+which is to be used on the board.  Each of these regulators maps to a single
+RPMh resource.
+
+The names used for regulator nodes must match those supported by a given PMIC.
+Supported regulator node names:
+	PM8998:		smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
+	PMI8998:	bob
+	PM8005:		smps1 - smps4
+
+========================
+First Level Nodes - PMIC
+========================
+
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: Must be one of: "qcom,pm8998-rpmh-regulators",
+		    "qcom,pmi8998-rpmh-regulators" or
+		    "qcom,pm8005-rpmh-regulators".
+
+- qcom,pmic-id
+	Usage:      required
+	Value type: <string>
+	Definition: RPMh resource name suffix used for the regulators found on
+		    this PMIC.  Typical values: "a", "b", "c", "d", "e", "f".
+
+- vdd_s1-supply
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply
+- vdd_s5-supply
+- vdd_s6-supply
+- vdd_s7-supply
+- vdd_s8-supply
+- vdd_s9-supply
+- vdd_s10-supply
+- vdd_s11-supply
+- vdd_s12-supply
+- vdd_s13-supply
+- vdd_l1_l27-supply
+- vdd_l2_l8_l17-supply
+- vdd_l3_l11-supply
+- vdd_l4_l5-supply
+- vdd_l6-supply
+- vdd_l7_l12_l14_l15-supply
+- vdd_l9-supply
+- vdd_l10_l23_l25-supply
+- vdd_l13_l19_l21-supply
+- vdd_l16_l28-supply
+- vdd_l18_l22-supply
+- vdd_l20_l24-supply
+- vdd_l26-supply
+- vdd_lvs1_lvs2-supply
+- vdd_lvs1_lvs2-supply
+	Usage:      optional (PM8998 only)
+	Value type: <phandle>
+	Definition: phandle of the parent supply regulator of one or more of the
+		    regulators for this PMIC.
+
+- vdd_bob-supply
+	Usage:      optional (PMI8998 only)
+	Value type: <phandle>
+	Definition: BOB regulator parent supply phandle
+
+- vdd_s1-supply
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply
+	Usage:      optional (PM8005 only)
+	Value type: <phandle>
+	Definition: phandle of the parent supply regulator of one or more of the
+		    regulators for this PMIC.
+
+===============================
+Second Level Nodes - Regulators
+===============================
+
+- qcom,regulator-initial-voltage
+	Usage:      optional; VRM regulators only
+	Value type: <u32>
+	Definition: Specifies the initial voltage in microvolts to request for a
+		    VRM regulator.
+
+- regulator-initial-mode
+	Usage:      optional; VRM regulators only
+	Value type: <u32>
+	Definition: Specifies the initial mode to request for a VRM regulator.
+		    Supported values are RPMH_REGULATOR_MODE_* which are defined
+		    in [1] (i.e. 0 to 3).  This property may be specified even
+		    if the regulator-allow-set-load property is not specified.
+
+- qcom,allowed-drms-modes
+	Usage:      required if regulator-allow-set-load is specified;
+		    VRM regulators only
+	Value type: <prop-encoded-array>
+	Definition: A list of integers specifying the PMIC regulator modes which
+		    can be configured at runtime based upon consumer load needs.
+		    Supported values are RPMH_REGULATOR_MODE_* which are defined
+		    in [1] (i.e. 0 to 3).
+
+- qcom,drms-mode-threshold-currents
+	Usage:      required if regulator-allow-set-load is specified;
+		    VRM regulators only
+	Value type: <prop-encoded-array>
+	Definition: A list of integers specifying the maximum allowed load
+		    current in microamps for each of the modes listed in
+		    qcom,allowed-drms-modes (matched 1-to-1 in order).  Elements
+		    must be specified in order from lowest to highest value.
+
+- qcom,headroom-voltage
+	Usage:      optional; VRM regulators only
+	Value type: <u32>
+	Definition: Specifies the headroom voltage in microvolts to request for
+		    a VRM regulator.  RPMh hardware automatically ensures that
+		    the parent of this regulator outputs a voltage high enough
+		    to satisfy the requested headroom.  Supported values are
+		    0 to 511000.
+
+- qcom,always-wait-for-ack
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the application processor
+		    must wait for an ACK or a NACK from RPMh for every request
+		    sent for this regulator including those which are for a
+		    strictly lower power state.
+
+Other properties defined in Documentation/devicetree/bindings/regulator.txt
+may also be used.
+
+[1] include/dt-bindings/regulator/qcom,rpmh-regulator.h
+
+========
+Examples
+========
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+&apps_rsc {
+	pm8998-rpmh-regulators {
+		compatible = "qcom,pm8998-rpmh-regulators";
+		qcom,pmic-id = "a";
+
+		vdd_l7_l12_l14_l15-supply = <&pm8998_s5>;
+
+		smps2 {
+			regulator-min-microvolt = <1100000>;
+			regulator-max-microvolt = <1100000>;
+			qcom,regulator-initial-voltage = <1100000>;
+		};
+
+		pm8998_s5: smps5 {
+			regulator-min-microvolt = <1904000>;
+			regulator-max-microvolt = <2040000>;
+			qcom,regulator-initial-voltage = <1904000>;
+		};
+
+		ldo7 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			qcom,regulator-initial-voltage = <1800000>;
+			qcom,headroom-voltage = <56000>;
+			regulator-initial-mode = <RPMH_REGULATOR_MODE_LPM>;
+			regulator-allow-set-load;
+			qcom,allowed-drms-modes =
+				<RPMH_REGULATOR_MODE_LPM
+				 RPMH_REGULATOR_MODE_HPM>;
+			qcom,drms-mode-threshold-currents = <10000 1000000>;
+		};
+
+		lvs1 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+		};
+	};
+
+	pmi8998-rpmh-regulators {
+		compatible = "qcom,pmi8998-rpmh-regulators";
+		qcom,pmic-id = "b";
+
+		bob {
+			regulator-min-microvolt = <3312000>;
+			regulator-max-microvolt = <3600000>;
+			qcom,regulator-initial-voltage = <3312000>;
+			regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>;
+		};
+	};
+};
include/dt-bindings/regulator/qcom,rpmh-regulator.h new file +36 -0
new file mode 100644
index 0000000..4378c4b
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#ifndef __QCOM_RPMH_REGULATOR_H
+#define __QCOM_RPMH_REGULATOR_H
+
+/*
+ * These mode constants may be used for regulator-initial-mode and
+ * qcom,allowed-drms-modes properties of an RPMh regulator device tree node.
+ * Each type of regulator supports a subset of the possible modes.
+ *
+ * %RPMH_REGULATOR_MODE_RET:	Retention mode in which only an extremely small
+ *				load current is allowed.  This mode is supported
+ *				by LDO and SMPS type regulators.
+ * %RPMH_REGULATOR_MODE_LPM:	Low power mode in which a small load current is
+ *				allowed.  This mode corresponds to PFM for SMPS
+ *				and BOB type regulators.  This mode is supported
+ *				by LDO, HFSMPS, BOB, and PMIC4 FTSMPS type
+ *				regulators.
+ * %RPMH_REGULATOR_MODE_AUTO:	Auto mode in which the regulator hardware
+ *				automatically switches between LPM and HPM based
+ *				upon the real-time load current.  This mode is
+ *				supported by HFSMPS, BOB, and PMIC4 FTSMPS type
+ *				regulators.
+ * %RPMH_REGULATOR_MODE_HPM:	High power mode in which the full rated current
+ *				of the regulator is allowed.  This mode
+ *				corresponds to PWM for SMPS and BOB type
+ *				regulators.  This mode is supported by all types
+ *				of regulators.
+ */
+#define RPMH_REGULATOR_MODE_RET		0
+#define RPMH_REGULATOR_MODE_LPM		1
+#define RPMH_REGULATOR_MODE_AUTO	2
+#define RPMH_REGULATOR_MODE_HPM		3
+
+#endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
maintainer
#2 lore raw

Re: [PATCH v2 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings

Quoted 58 lines · David Collins

Introduce bindings for RPMh regulator devices found on some Qualcomm Technlogies, Inc. SoCs. These devices allow a given processor within the SoC to make PMIC regulator requests which are aggregated within the RPMh hardware block along with requests from other processors in the SoC to determine the final PMIC regulator hardware state.

---
 .../bindings/regulator/qcom,rpmh-regulator.txt     | 207 +++++++++++++++++++++
 .../dt-bindings/regulator/qcom,rpmh-regulator.h    |  36 ++++
 2 files changed, 243 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
 create mode 100644 include/dt-bindings/regulator/qcom,rpmh-regulator.h
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +37 -0
new file mode 100644
index 0000000..69748ea
@@ -0,0 +1,207 @@
+Qualcomm Technologies, Inc. RPMh Regulators
+
+rpmh-regulator devices support PMIC regulator management via the Voltage
+Regulator Manager (VRM) and Oscillator Buffer (XOB) RPMh accelerators.  The APPS
+processor communicates with these hardware blocks via a Resource State
+Coordinator (RSC) using command packets.  The VRM allows changing four
+parameters for a given regulator: enable state, output voltage, operating mode,
+and minimum headroom voltage.  The XOB allows changing only a single parameter
+for a given regulator: its enable state.  Despite its name, the XOB is capable
+of controlling the enable state of any PMIC peripheral.  It is used for clock
+buffers, low-voltage switches, and LDO/SMPS regulators which have a fixed
+voltage and mode.
+
+=======================
+Required Node Structure
+=======================
+
+RPMh regulators must be described in two levels of device nodes.  The first
+level describes the PMIC containing the regulators and must reside within an
+RPMh device node.  The second level describes each regulator within the PMIC
+which is to be used on the board.  Each of these regulators maps to a single
+RPMh resource.
+
+The names used for regulator nodes must match those supported by a given PMIC.
+Supported regulator node names:
+	PM8998:		smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
+	PMI8998:	bob
+	PM8005:		smps1 - smps4
+
+========================
+First Level Nodes - PMIC
+========================
+
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: Must be one of: "qcom,pm8998-rpmh-regulators",

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 47

Quoted 10 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +10 -0
+		    "qcom,pmi8998-rpmh-regulators" or
+		    "qcom,pm8005-rpmh-regulators".
+
+- qcom,pmic-id
+	Usage:      required
+	Value type: <string>
+	Definition: RPMh resource name suffix used for the regulators found on
+		    this PMIC.  Typical values: "a", "b", "c", "d", "e", "f".
+
+- vdd_s1-supply

Use '-' rather than '_' on all these.

Quoted 31 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +31 -0
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply
+- vdd_s5-supply
+- vdd_s6-supply
+- vdd_s7-supply
+- vdd_s8-supply
+- vdd_s9-supply
+- vdd_s10-supply
+- vdd_s11-supply
+- vdd_s12-supply
+- vdd_s13-supply
+- vdd_l1_l27-supply
+- vdd_l2_l8_l17-supply
+- vdd_l3_l11-supply
+- vdd_l4_l5-supply
+- vdd_l6-supply
+- vdd_l7_l12_l14_l15-supply
+- vdd_l9-supply
+- vdd_l10_l23_l25-supply
+- vdd_l13_l19_l21-supply
+- vdd_l16_l28-supply
+- vdd_l18_l22-supply
+- vdd_l20_l24-supply
+- vdd_l26-supply
+- vdd_lvs1_lvs2-supply
+- vdd_lvs1_lvs2-supply
+	Usage:      optional (PM8998 only)
+	Value type: <phandle>
+	Definition: phandle of the parent supply regulator of one or more of the
+		    regulators for this PMIC.

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 88

Quoted 10 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +10 -0
+
+- vdd_bob-supply
+	Usage:      optional (PMI8998 only)
+	Value type: <phandle>
+	Definition: BOB regulator parent supply phandle
+
+- vdd_s1-supply
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply

Listed twice?

Quoted 6 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +6 -0
+	Usage:      optional (PM8005 only)
+	Value type: <phandle>
+	Definition: phandle of the parent supply regulator of one or more of the
+		    regulators for this PMIC.
+
+===============================

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 104

Quoted 10 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +10 -0
+Second Level Nodes - Regulators
+===============================
+
+- qcom,regulator-initial-voltage
+	Usage:      optional; VRM regulators only
+	Value type: <u32>
+	Definition: Specifies the initial voltage in microvolts to request for a
+		    VRM regulator.
+
+- regulator-initial-mode

Vendor prefix?

Quoted 149 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +103 -0
+	Usage:      optional; VRM regulators only
+	Value type: <u32>
+	Definition: Specifies the initial mode to request for a VRM regulator.
+		    Supported values are RPMH_REGULATOR_MODE_* which are defined
+		    in [1] (i.e. 0 to 3).  This property may be specified even
+		    if the regulator-allow-set-load property is not specified.
+
+- qcom,allowed-drms-modes
+	Usage:      required if regulator-allow-set-load is specified;
+		    VRM regulators only
+	Value type: <prop-encoded-array>
+	Definition: A list of integers specifying the PMIC regulator modes which
+		    can be configured at runtime based upon consumer load needs.
+		    Supported values are RPMH_REGULATOR_MODE_* which are defined
+		    in [1] (i.e. 0 to 3).
+
+- qcom,drms-mode-threshold-currents
+	Usage:      required if regulator-allow-set-load is specified;
+		    VRM regulators only
+	Value type: <prop-encoded-array>
+	Definition: A list of integers specifying the maximum allowed load
+		    current in microamps for each of the modes listed in
+		    qcom,allowed-drms-modes (matched 1-to-1 in order).  Elements
+		    must be specified in order from lowest to highest value.
+
+- qcom,headroom-voltage
+	Usage:      optional; VRM regulators only
+	Value type: <u32>
+	Definition: Specifies the headroom voltage in microvolts to request for
+		    a VRM regulator.  RPMh hardware automatically ensures that
+		    the parent of this regulator outputs a voltage high enough
+		    to satisfy the requested headroom.  Supported values are
+		    0 to 511000.
+
+- qcom,always-wait-for-ack
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the application processor
+		    must wait for an ACK or a NACK from RPMh for every request
+		    sent for this regulator including those which are for a
+		    strictly lower power state.
+
+Other properties defined in Documentation/devicetree/bindings/regulator.txt
+may also be used.
+
+[1] include/dt-bindings/regulator/qcom,rpmh-regulator.h
+
+========
+Examples
+========
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+&apps_rsc {
+	pm8998-rpmh-regulators {
+		compatible = "qcom,pm8998-rpmh-regulators";
+		qcom,pmic-id = "a";
+
+		vdd_l7_l12_l14_l15-supply = <&pm8998_s5>;
+
+		smps2 {
+			regulator-min-microvolt = <1100000>;
+			regulator-max-microvolt = <1100000>;
+			qcom,regulator-initial-voltage = <1100000>;
+		};
+
+		pm8998_s5: smps5 {
+			regulator-min-microvolt = <1904000>;
+			regulator-max-microvolt = <2040000>;
+			qcom,regulator-initial-voltage = <1904000>;
+		};
+
+		ldo7 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			qcom,regulator-initial-voltage = <1800000>;
+			qcom,headroom-voltage = <56000>;
+			regulator-initial-mode = <RPMH_REGULATOR_MODE_LPM>;
+			regulator-allow-set-load;
+			qcom,allowed-drms-modes =
+				<RPMH_REGULATOR_MODE_LPM
+				 RPMH_REGULATOR_MODE_HPM>;
+			qcom,drms-mode-threshold-currents = <10000 1000000>;
+		};
+
+		lvs1 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+		};
+	};
+
+	pmi8998-rpmh-regulators {
+		compatible = "qcom,pmi8998-rpmh-regulators";
+		qcom,pmic-id = "b";
+
+		bob {
+			regulator-min-microvolt = <3312000>;
+			regulator-max-microvolt = <3600000>;
+			qcom,regulator-initial-voltage = <3312000>;
+			regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>;
+		};
+	};
+};
include/dt-bindings/regulator/qcom,rpmh-regulator.h new file +36 -0
new file mode 100644
index 0000000..4378c4b
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#ifndef __QCOM_RPMH_REGULATOR_H
+#define __QCOM_RPMH_REGULATOR_H
+
+/*
+ * These mode constants may be used for regulator-initial-mode and
+ * qcom,allowed-drms-modes properties of an RPMh regulator device tree node.
+ * Each type of regulator supports a subset of the possible modes.
+ *
+ * %RPMH_REGULATOR_MODE_RET:	Retention mode in which only an extremely small
+ *				load current is allowed.  This mode is supported
+ *				by LDO and SMPS type regulators.
+ * %RPMH_REGULATOR_MODE_LPM:	Low power mode in which a small load current is
+ *				allowed.  This mode corresponds to PFM for SMPS
+ *				and BOB type regulators.  This mode is supported
+ *				by LDO, HFSMPS, BOB, and PMIC4 FTSMPS type
+ *				regulators.
+ * %RPMH_REGULATOR_MODE_AUTO:	Auto mode in which the regulator hardware
+ *				automatically switches between LPM and HPM based
+ *				upon the real-time load current.  This mode is
+ *				supported by HFSMPS, BOB, and PMIC4 FTSMPS type
+ *				regulators.
+ * %RPMH_REGULATOR_MODE_HPM:	High power mode in which the full rated current
+ *				of the regulator is allowed.  This mode
+ *				corresponds to PWM for SMPS and BOB type
+ *				regulators.  This mode is supported by all types
+ *				of regulators.
+ */
+#define RPMH_REGULATOR_MODE_RET		0
+#define RPMH_REGULATOR_MODE_LPM		1
+#define RPMH_REGULATOR_MODE_AUTO	2
+#define RPMH_REGULATOR_MODE_HPM		3
+
+#endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#3 lore raw

Re: [PATCH v2 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings

Hello Rob,

Quoted 8 lines · Rob Herring
Quoted 7 lines · David Collins

Introduce bindings for RPMh regulator devices found on some Qualcomm Technlogies, Inc. SoCs. These devices allow a given processor within the SoC to make PMIC regulator requests which are aggregated within the RPMh hardware block along with requests from other processors in the SoC to determine the final PMIC regulator hardware state.

Quoted 10 lines
Quoted 8 lines
---
 .../bindings/regulator/qcom,rpmh-regulator.txt     | 207 +++++++++++++++++++++
 .../dt-bindings/regulator/qcom,rpmh-regulator.h    |  36 ++++

[...]
+++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt [...]
+- vdd_s1-supply

Use '-' rather than '_' on all these.

I will change this on the next patch set.

Quoted 44 lines
Quoted 31 lines
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply
+- vdd_s5-supply
+- vdd_s6-supply
+- vdd_s7-supply
+- vdd_s8-supply
+- vdd_s9-supply
+- vdd_s10-supply
+- vdd_s11-supply
+- vdd_s12-supply
+- vdd_s13-supply
+- vdd_l1_l27-supply
+- vdd_l2_l8_l17-supply
+- vdd_l3_l11-supply
+- vdd_l4_l5-supply
+- vdd_l6-supply
+- vdd_l7_l12_l14_l15-supply
+- vdd_l9-supply
+- vdd_l10_l23_l25-supply
+- vdd_l13_l19_l21-supply
+- vdd_l16_l28-supply
+- vdd_l18_l22-supply
+- vdd_l20_l24-supply
+- vdd_l26-supply
+- vdd_lvs1_lvs2-supply
+- vdd_lvs1_lvs2-supply
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+	Usage:      optional (PM8998 only)
+	Value type: <phandle>
+	Definition: phandle of the parent supply regulator of one or more of the
+		    regulators for this PMIC.

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 88

Quoted 10 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +10 -0
+
+- vdd_bob-supply
+	Usage:      optional (PMI8998 only)
+	Value type: <phandle>
+	Definition: BOB regulator parent supply phandle
+
+- vdd_s1-supply
+- vdd_s2-supply
+- vdd_s3-supply
+- vdd_s4-supply

Listed twice?

Quoted 4 lines

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 92

Quoted 4 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+	Usage:      optional (PM8005 only)
+	Value type: <phandle>
+	Definition: phandle of the parent supply regulator of one or more of the
+		    regulators for this PMIC.

I listed vdd_s1-supply to vdd_s4-supply here twice because I wanted to group together the supplies supported by each PMIC. PM8005 only supports these 4 regulator supplies. PM8998 supports these 4 along with many others. How else would suggest that I capture this per-PMIC support information?

Quoted 5 lines
Quoted 5 lines

+===============================

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+Second Level Nodes - Regulators
+===============================
+
+- qcom,regulator-initial-voltage
Quoted 8 lines

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 104

Quoted 6 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +6 -0
+	Usage:      optional; VRM regulators only
+	Value type: <u32>
+	Definition: Specifies the initial voltage in microvolts to request for a
+		    VRM regulator.
+
+- regulator-initial-mode

Vendor prefix?

No, this does not need a vendor prefix. The regulator-initial-mode property is already defined for all regulator devices [1]:

regulator-initial-mode: initial operating mode. The set of possible operating modes depends on the capabilities of every hardware so each device binding documentation explains which values the regulator supports.

The values supported by the property are hardware specific and thus must be listed in device specific binding files like this one. Here is a previously merged example: [2].

Thanks,
David

[1]:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/regulator/regulator.txt?h=v4.17-rc1#n59
[2]:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/regulator/twl-regulator.txt?h=v4.17-rc1#n60

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#4 lore raw

Re: [PATCH v2 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings

In reply to David Collins

Hi,

Quoted 7 lines · David Collins

Introduce bindings for RPMh regulator devices found on some Qualcomm Technlogies, Inc. SoCs. These devices allow a given processor within the SoC to make PMIC regulator requests which are aggregated within the RPMh hardware block along with requests from other processors in the SoC to determine the final PMIC regulator hardware state.

Quoted 5 lines
---
 .../bindings/regulator/qcom,rpmh-regulator.txt     | 207 +++++++++++++++++++++
 .../dt-bindings/regulator/qcom,rpmh-regulator.h    |  36 ++++
 2 files changed, 243 insertions(+)

I noticed that "qcom,rpmh-resource-type" is now gone. Not needed anymore? Oh, I see. Stephen said to add it when it's needed. OK, fine.

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 98

Quoted 5 lines

+===============================

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+Second Level Nodes - Regulators
+===============================
+
+- qcom,regulator-initial-voltage

nit: regulator framework tends to include "microvolt" in the name to make it really obvious in the device tree what the units are. Can you do that too?

Quoted 1 line

+- qcom,drms-mode-threshold-currents

Could use microamp in the name of the property too...

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 187

Quoted 4 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+                       qcom,allowed-drms-modes =
+                               <RPMH_REGULATOR_MODE_LPM
+                                RPMH_REGULATOR_MODE_HPM>;
+                       qcom,drms-mode-threshold-currents = <10000 1000000>;

optional nit: to make it match downstream drivers, does it make sense to change this to:

<9999 999999>

...so if a driver used to request exactly 10000 mA that it will end up with the same mode (no idea if drivers actually do that).

-Doug

#5 lore raw

Re: [PATCH v2 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings

Quoted 6 lines · Doug Anderson

[...]

Quoted 1 line

+- qcom,regulator-initial-voltage

nit: regulator framework tends to include "microvolt" in the name to make it really obvious in the device tree what the units are. Can you do that too?

Sure, I'll change the name to be: qcom,regulator-initial-microvolt.

Quoted 3 lines
Quoted 1 line

+- qcom,drms-mode-threshold-currents

Could use microamp in the name of the property too...

Ok, I'll change the name to be: qcom,drms-mode-max-microamps.

Quoted 5 lines

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 187

Quoted 4 lines
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+                       qcom,allowed-drms-modes =
+                               <RPMH_REGULATOR_MODE_LPM
+                                RPMH_REGULATOR_MODE_HPM>;
+                       qcom,drms-mode-threshold-currents = <10000 1000000>;
Quoted 7 lines

optional nit: to make it match downstream drivers, does it make sense to change this to:

<9999 999999>

...so if a driver used to request exactly 10000 mA that it will end up with the same mode (no idea if drivers actually do that).

I'd prefer to leave the example with <10000 1000000> as it looks cleaner to me and the example numbers are arbitrary. It would also be good to use <10000 1000000> in actual board DT files. We can address consumers expecting legacy behavior for 10000 uA requests as needed.

Take care,
David

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#6 lore raw

Re: [PATCH v2 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings

In reply to David Collins

Hi,

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 78

Quoted 7 lines · David Collins
+- vdd_l26-supply
+- vdd_lvs1_lvs2-supply
+- vdd_lvs1_lvs2-supply
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+       Usage:      optional (PM8998 only)
+       Value type: <phandle>
+       Definition: phandle of the parent supply regulator of one or more of the
+                   regulators for this PMIC.

One small additional nit here is that "vdd_lvs1_lvs2-supply" is listed twice.

Also on the schematics (and in the PM8998 datasheet) I have this is "VIN_LVS_1_2". It seems like you should be consistent here and call this "vin-lvs-1-2-supply".

-Doug

#7 lore raw

Re: [PATCH v2 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings

Quoted 10 lines · Doug Anderson

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 78

Quoted 7 lines · David Collins
+- vdd_l26-supply
+- vdd_lvs1_lvs2-supply
+- vdd_lvs1_lvs2-supply
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+       Usage:      optional (PM8998 only)
+       Value type: <phandle>
+       Definition: phandle of the parent supply regulator of one or more of the
+                   regulators for this PMIC.

One small additional nit here is that "vdd_lvs1_lvs2-supply" is listed twice.

I'll remove the duplicate.

Quoted 3 lines

Also on the schematics (and in the PM8998 datasheet) I have this is "VIN_LVS_1_2". It seems like you should be consistent here and call this "vin-lvs-1-2-supply".

I was trying to keep the naming consistent within device tree binding documentation for LVS vs LDO and SMPS (e.g. 'vdd' vs 'vin' prefix). I suppose that I can change this to match the hardware documentation pin name. I can also change '_' to '-' in the supply names if that is preferred.

Take care,
David

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#8 lore raw

Re: [PATCH v2 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings

Hi,

Quoted 15 lines · David Collins
Quoted 10 lines · Doug Anderson

Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt line 78

Quoted 7 lines · David Collins
+- vdd_l26-supply
+- vdd_lvs1_lvs2-supply
+- vdd_lvs1_lvs2-supply
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file +4 -0
+       Usage:      optional (PM8998 only)
+       Value type: <phandle>
+       Definition: phandle of the parent supply regulator of one or more of the
+                   regulators for this PMIC.

One small additional nit here is that "vdd_lvs1_lvs2-supply" is listed twice.

I'll remove the duplicate.

Quoted 8 lines
Quoted 3 lines

Also on the schematics (and in the PM8998 datasheet) I have this is "VIN_LVS_1_2". It seems like you should be consistent here and call this "vin-lvs-1-2-supply".

I was trying to keep the naming consistent within device tree binding documentation for LVS vs LDO and SMPS (e.g. 'vdd' vs 'vin' prefix). I suppose that I can change this to match the hardware documentation pin name. I can also change '_' to '-' in the supply names if that is preferred.

I'd rather it match the docs. I personally have no idea for why the writer of the docs used "vdd" vs. "vin", but even if they had no good reason matching the docs makes this searchable.

As far as the "_" to "-", Rob asked for that earlier in this thread and you says "I will change this on the next patch set." In general the desire to convert "_" to "-" makes this less searchable (since the docs use "_"), but that's the way device tree guys want it so so c'est la vie. Luckily it's easily to mentally change the "-"s back to "_"s when searching...

-Doug

#9 lore raw

PATCH v2 2/2 regulator: add QCOM RPMh regulator driver

In reply to David Collins

Add the QCOM RPMh regulator driver to manage PMIC regulators which are controlled via RPMh on some Qualcomm Technologies, Inc. SoCs. RPMh is a hardware block which contains several accelerators which are used to manage various hardware resources that are shared between the processors of the SoC. The final hardware state of a regulator is determined within RPMh by performing max aggregation of the requests made by all of the processors.

Add support for PMIC regulator control via the voltage regulator manager (VRM) and oscillator buffer (XOB) RPMh accelerators. VRM supports manipulation of enable state, voltage, mode, and headroom voltage. XOB supports manipulation of enable state.

---
 drivers/regulator/Kconfig               |   9 +
 drivers/regulator/Makefile              |   1 +
 drivers/regulator/qcom_rpmh-regulator.c | 910 ++++++++++++++++++++++++++++++++
 3 files changed, 920 insertions(+)
 create mode 100644 drivers/regulator/qcom_rpmh-regulator.c
drivers/regulator/Kconfig +9 -0
index 097f617..e0ecd0a 100644
@@ -671,6 +671,15 @@ config REGULATOR_QCOM_RPM
 	  Qualcomm RPM as a module. The module will be named
 	  "qcom_rpm-regulator".
 
+config REGULATOR_QCOM_RPMH
+	tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
+	depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+	help
+	  This driver supports control of PMIC regulators via the RPMh hardware
+	  block found on Qualcomm Technologies Inc. SoCs.  RPMh regulator
+	  control allows for voting on regulator state between multiple
+	  processors within the SoC.
+
 config REGULATOR_QCOM_SMD_RPM
 	tristate "Qualcomm SMD based RPM regulator driver"
 	depends on QCOM_SMD_RPM
drivers/regulator/Makefile +1 -0
index 590674f..c2274dd 100644
@@ -77,6 +77,7 @@ obj-$(CONFIG_REGULATOR_MT6323)	+= mt6323-regulator.o
 obj-$(CONFIG_REGULATOR_MT6380)	+= mt6380-regulator.o
 obj-$(CONFIG_REGULATOR_MT6397)	+= mt6397-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom_rpmh-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
drivers/regulator/qcom_rpmh-regulator.c new file +910 -0

910 lines not on this page · raw message

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#10 lore raw
Quoted 112 lines · David Collins

Add the QCOM RPMh regulator driver to manage PMIC regulators which are controlled via RPMh on some Qualcomm Technologies, Inc. SoCs. RPMh is a hardware block which contains several accelerators which are used to manage various hardware resources that are shared between the processors of the SoC. The final hardware state of a regulator is determined within RPMh by performing max aggregation of the requests made by all of the processors.

Add support for PMIC regulator control via the voltage regulator manager (VRM) and oscillator buffer (XOB) RPMh accelerators. VRM supports manipulation of enable state, voltage, mode, and headroom voltage. XOB supports manipulation of enable state.

---
 drivers/regulator/Kconfig               |   9 +
 drivers/regulator/Makefile              |   1 +
 drivers/regulator/qcom_rpmh-regulator.c | 910 ++++++++++++++++++++++++++++++++
 3 files changed, 920 insertions(+)
 create mode 100644 drivers/regulator/qcom_rpmh-regulator.c
drivers/regulator/Kconfig +9 -0
index 097f617..e0ecd0a 100644
@@ -671,6 +671,15 @@ config REGULATOR_QCOM_RPM
 	  Qualcomm RPM as a module. The module will be named
 	  "qcom_rpm-regulator".
 
+config REGULATOR_QCOM_RPMH
+	tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
+	depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+	help
+	  This driver supports control of PMIC regulators via the RPMh hardware
+	  block found on Qualcomm Technologies Inc. SoCs.  RPMh regulator
+	  control allows for voting on regulator state between multiple
+	  processors within the SoC.
+
 config REGULATOR_QCOM_SMD_RPM
 	tristate "Qualcomm SMD based RPM regulator driver"
 	depends on QCOM_SMD_RPM
drivers/regulator/Makefile +1 -0
index 590674f..c2274dd 100644
@@ -77,6 +77,7 @@ obj-$(CONFIG_REGULATOR_MT6323)	+= mt6323-regulator.o
 obj-$(CONFIG_REGULATOR_MT6380)	+= mt6380-regulator.o
 obj-$(CONFIG_REGULATOR_MT6397)	+= mt6397-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom_rpmh-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
drivers/regulator/qcom_rpmh-regulator.c new file +52 -0
new file mode 100644
index 0000000..03b1301
@@ -0,0 +1,910 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+/**
+ * enum rpmh_regulator_type - supported RPMh accelerator types
+ * %VRM:	RPMh VRM accelerator which supports voting on enable, voltage,
+ *		mode, and headroom voltage of LDO, SMPS, and BOB type PMIC
+ *		regulators.
+ * %XOB:	RPMh XOB accelerator which supports voting on the enable state
+ *		of PMIC regulators.
+ */
+enum rpmh_regulator_type {
+	VRM,
+	XOB,
+};
+
+#define RPMH_VRM_HEADROOM_MAX_UV		511000
+
+#define RPMH_REGULATOR_REG_VRM_VOLTAGE		0x0
+#define RPMH_REGULATOR_REG_ENABLE		0x4
+#define RPMH_REGULATOR_REG_VRM_MODE		0x8
+#define RPMH_REGULATOR_REG_VRM_HEADROOM		0xC
+
+#define RPMH_REGULATOR_DISABLE			0x0
+#define RPMH_REGULATOR_ENABLE			0x1
+
+#define RPMH_REGULATOR_MODE_COUNT		4
+
+/**
+ * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations
+ * @regulator_type:		RPMh accelerator type used to manage this
+ *				regulator
+ * @ops:			Pointer to regulator ops callback structure

drivers/regulator/qcom_rpmh-regulator.c line 62

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+ * @voltage_range:		The single range of voltages supported by this
+ *				PMIC regulator type
+ * @n_voltages:			The number of unique voltage set points defined
+ *				by voltage_range
+ * @pmic_mode_map:		Array indexed by regulator framework mode
+ *				containing PMIC hardware modes.  Must be large
+ *				enough to index all framework modes supported
+ *				by this regulator hardware type.
+ * @of_map_mode:		Maps an RPMH_REGULATOR_MODE_* mode value defined
+ *				in device tree to a regulator framework mode

The name of the field is a bit misleading, this is a map of RPMh mode to regulator framework mode, the device tree just happens to be the place where this mapping is defined.

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+ * @bypass_mode:		VRM PMIC mode value corresponding to bypass
+ *				(pass-through) for this regulator.  Only used
+ *				by BOB type via VRM.
+ */
+struct rpmh_vreg_hw_data {
+	enum rpmh_regulator_type		regulator_type;
+	const struct regulator_ops		*ops;
+	const struct regulator_linear_range	voltage_range;

drivers/regulator/qcom_rpmh-regulator.c line 80

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+	int					n_voltages;
+	const u32				*pmic_mode_map;
+	unsigned int			      (*of_map_mode)(unsigned int mode);
+	u32					bypass_mode;
+};
+
+/**
+ * struct rpmh_vreg - individual rpmh regulator data structure encapsulating a
+ *		single regulator device
+ * @rpmh_client:		Handle used for rpmh communications

nit: RPMh

Quoted 24 lines
drivers/regulator/qcom_rpmh-regulator.c new file +24 -0
+ * @addr:			Base address of the regulator resource within
+ *				an RPMh accelerator
+ * @rdesc:			Regulator descriptor
+ * @hw_data:			PMIC regulator configuration data for this RPMh
+ *				regulator
+ * @regulator_type:		RPMh accelerator type for this regulator
+ *				resource
+ * @always_wait_for_ack:	Boolean flag indicating if a request must always
+ *				wait for an ACK from RPMh before continuing even
+ *				if it corresponds to a strictly lower power
+ *				state (e.g. enabled --> disabled).
+ * @drms_mode:			Array of regulator framework modes which can
+ *				be configured dynamically for this regulator
+ *				via the set_load() callback.
+ * @drms_mode_max_uA:		Array of maximum load currents in microamps
+ *				supported by the corresponding modes in
+ *				drms_mode.  Elements must be specified in
+ *				strictly increasing order.
+ * @drms_mode_count:		The number of elements in drms_mode array.
+ * @enabled:			Boolean indicating if the regulator is enabled
+ *				or not
+ * @voltage_selector:		Selector used for get_voltage_sel() and
+ *				set_voltage_sel() callbacks
+ * @mode:			RPMh VRM regulator current framework mode

drivers/regulator/qcom_rpmh-regulator.c line 114

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+ * @bypassed:			Boolean indicating if the regulator is in
+ *				bypass (pass-through) mode or not.  This is
+ *				only used by BOB rpmh-regulator resources.
+ */
+struct rpmh_vreg {
+	struct rpmh_client		*rpmh_client;
+	u32				addr;
+	struct regulator_desc		rdesc;
+	const struct rpmh_vreg_hw_data	*hw_data;
+	enum rpmh_regulator_type	regulator_type;

This value is already available via rpmh_vreg->hw_data->regulator_type, why duplicate it? The field is assigned in rpmh_regulator_init_vreg() and only read once in the same function, there seems to be no need for it, not even to improve readability.

Quoted 159 lines
drivers/regulator/qcom_rpmh-regulator.c new file +159 -0
+	bool				always_wait_for_ack;
+	unsigned int			*drms_mode;
+	int				*drms_mode_max_uA;
+	size_t				drms_mode_count;
+
+	bool				enabled;
+	int				voltage_selector;
+	unsigned int			mode;
+	bool				bypassed;
+};
+
+/**
+ * struct rpmh_vreg_init_data - initialization data for an RPMh regulator
+ * @name:			Name for the regulator which also corresponds
+ *				to the device tree subnode name of the regulator
+ * @resource_name:		RPMh regulator resource name format string.
+ *				This must include exactly one field: '%s' which
+ *				is filled at run-time with the PMIC ID provided
+ *				by device tree property qcom,pmic-id.  Example:
+ *				"ldo%s1" for RPMh resource "ldoa1".
+ * @supply_name:		Parent supply regulator name
+ * @hw_data:			Configuration data for this PMIC regulator type
+ */
+struct rpmh_vreg_init_data {
+	const char			*name;
+	const char			*resource_name;
+	const char			*supply_name;
+	const struct rpmh_vreg_hw_data	*hw_data;
+};
+
+/**
+ * rpmh_regulator_send_request() - send the request to RPMh
+ * @vreg:		Pointer to the RPMh regulator
+ * @cmd:		RPMh commands to send
+ * @count:		Size of cmd array
+ * @wait_for_ack:	Boolean indicating if execution must wait until the
+ *			request has been acknowledged as complete
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_send_request(struct rpmh_vreg *vreg,
+			struct tcs_cmd *cmd, int count, bool wait_for_ack)
+{
+	int ret;
+
+	if (wait_for_ack || vreg->always_wait_for_ack)
+		ret = rpmh_write(vreg->rpmh_client,
+				RPMH_ACTIVE_ONLY_STATE, cmd, count);
+	else
+		ret = rpmh_write_async(vreg->rpmh_client,
+				RPMH_ACTIVE_ONLY_STATE, cmd, count);
+
+	return ret;
+}
+
+static int rpmh_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->enabled;
+}
+
+static int rpmh_regulator_enable(struct regulator_dev *rdev)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	struct tcs_cmd cmd = {
+		.addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+		.data = RPMH_REGULATOR_ENABLE,
+	};
+	int ret;
+
+	ret = rpmh_regulator_send_request(vreg, &cmd, 1, true);
+
+	if (!ret)
+		vreg->enabled = true;
+
+	return ret;
+}
+
+static int rpmh_regulator_disable(struct regulator_dev *rdev)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	struct tcs_cmd cmd = {
+		.addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+		.data = RPMH_REGULATOR_DISABLE,
+	};
+	int ret;
+
+	ret = rpmh_regulator_send_request(vreg, &cmd, 1, false);
+
+	if (!ret)
+		vreg->enabled = false;
+
+	return ret;
+}
+
+static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+				unsigned int selector)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	struct tcs_cmd cmd = {
+		.addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
+	};
+	int ret;
+
+	/* VRM voltage control register is set with voltage in millivolts. */
+	cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev,
+							selector), 1000);
+
+	ret = rpmh_regulator_send_request(vreg, &cmd, 1,
+					  selector > vreg->voltage_selector);
+	if (!ret)
+		vreg->voltage_selector = selector;
+
+	return 0;
+}
+
+static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->voltage_selector;
+}
+
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+					unsigned int mode, bool bypassed)
+{
+	struct tcs_cmd cmd = {
+		.addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+	};
+
+	cmd.data = bypassed ? vreg->hw_data->bypass_mode
+			    : vreg->hw_data->pmic_mode_map[mode];
+
+	return rpmh_regulator_send_request(vreg, &cmd, 1, true);
+}
+
+static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev,
+					unsigned int mode)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	int ret;
+
+	if (mode == vreg->mode)
+		return 0;
+	else if (mode > REGULATOR_MODE_STANDBY)
+		return -EINVAL;
+
+	ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
+	if (!ret)
+		vreg->mode = mode;
+
+	return ret;
+}
+
+static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+

drivers/regulator/qcom_rpmh-regulator.c line 283

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+	return vreg->mode;
+}
+
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	int i;
+
+	for (i = 0; i < vreg->drms_mode_count - 1; i++)
+		if (load_uA < vreg->drms_mode_max_uA[i])

Shouldn't this be '<='?

nit: IMO 'vreg->drms_mode_max_uA[i] >= load_uA' would be more readable.

Quoted 332 lines
drivers/regulator/qcom_rpmh-regulator.c new file +332 -0
+			break;
+
+	return rpmh_regulator_vrm_set_mode(rdev, vreg->drms_mode[i]);
+}
+
+static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev,
+				bool enable)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	int ret;
+
+	if (vreg->bypassed == enable)
+		return 0;
+
+	ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, enable);
+	if (!ret)
+		vreg->bypassed = enable;
+
+	return ret;
+}
+
+static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev,
+				bool *enable)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+	*enable = vreg->bypassed;
+
+	return 0;
+}
+
+static const struct regulator_ops rpmh_regulator_vrm_ops = {
+	.enable			= rpmh_regulator_enable,
+	.disable		= rpmh_regulator_disable,
+	.is_enabled		= rpmh_regulator_is_enabled,
+	.set_voltage_sel	= rpmh_regulator_vrm_set_voltage_sel,
+	.get_voltage_sel	= rpmh_regulator_vrm_get_voltage_sel,
+	.list_voltage		= regulator_list_voltage_linear_range,
+	.set_mode		= rpmh_regulator_vrm_set_mode,
+	.get_mode		= rpmh_regulator_vrm_get_mode,
+	.set_load		= rpmh_regulator_vrm_set_load,
+};
+
+static const struct regulator_ops rpmh_regulator_vrm_bypass_ops = {
+	.enable			= rpmh_regulator_enable,
+	.disable		= rpmh_regulator_disable,
+	.is_enabled		= rpmh_regulator_is_enabled,
+	.set_voltage_sel	= rpmh_regulator_vrm_set_voltage_sel,
+	.get_voltage_sel	= rpmh_regulator_vrm_get_voltage_sel,
+	.list_voltage		= regulator_list_voltage_linear_range,
+	.set_mode		= rpmh_regulator_vrm_set_mode,
+	.get_mode		= rpmh_regulator_vrm_get_mode,
+	.set_load		= rpmh_regulator_vrm_set_load,
+	.set_bypass		= rpmh_regulator_vrm_set_bypass,
+	.get_bypass		= rpmh_regulator_vrm_get_bypass,
+};
+
+static const struct regulator_ops rpmh_regulator_xob_ops = {
+	.enable			= rpmh_regulator_enable,
+	.disable		= rpmh_regulator_disable,
+	.is_enabled		= rpmh_regulator_is_enabled,
+};
+
+/**
+ * rpmh_regulator_parse_vrm_modes() - parse the supported mode configurations
+ *		for a VRM RPMh resource from device tree
+ * vreg:		Pointer to the individual rpmh-regulator resource
+ * dev:			Pointer to the top level rpmh-regulator PMIC device
+ * node:		Pointer to the individual rpmh-regulator resource
+ *			device node
+ *
+ * This function initializes the drms_mode[] and drms_mode_max_uA[] arrays of
+ * vreg based upon the values of optional device tree properties.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_parse_vrm_modes(struct rpmh_vreg *vreg,
+				struct device *dev, struct device_node *node)
+{
+	const char *prop;
+	int i, len, ret, mode;
+	u32 *buf;
+
+	/* qcom,allowed-drms-modes is optional */
+	prop = "qcom,allowed-drms-modes";
+	len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+	if (len < 0)
+		return 0;
+
+	vreg->drms_mode = devm_kcalloc(dev, len, sizeof(*vreg->drms_mode),
+					GFP_KERNEL);
+	vreg->drms_mode_max_uA = devm_kcalloc(dev, len,
+				   sizeof(*vreg->drms_mode_max_uA), GFP_KERNEL);
+	if (!vreg->drms_mode || !vreg->drms_mode_max_uA)
+		return -ENOMEM;
+	vreg->drms_mode_count = len;
+
+	buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = of_property_read_u32_array(node, prop, buf, len);
+	if (ret < 0) {
+		dev_err(dev, "%s: unable to read %s, ret=%d\n",
+			node->name, prop, ret);
+		goto done;
+	}
+
+	for (i = 0; i < len; i++) {
+		mode = vreg->hw_data->of_map_mode(buf[i]);
+		if (mode <= 0) {
+			dev_err(dev, "%s: element %d of %s = %u is invalid for this regulator\n",
+				node->name, i, prop, buf[i]);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		vreg->drms_mode[i] = mode;
+	}
+
+	prop = "qcom,drms-mode-threshold-currents";
+	len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+	if (len != vreg->drms_mode_count) {
+		dev_err(dev, "%s: invalid element count=%d for %s\n",
+			node->name, len, prop);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = of_property_read_u32_array(node, prop, buf, len);
+	if (ret < 0) {
+		dev_err(dev, "%s: unable to read %s, ret=%d\n",
+			node->name, prop, ret);
+		goto done;
+	}
+
+	for (i = 0; i < len; i++) {
+		vreg->drms_mode_max_uA[i] = buf[i];
+
+		if (i > 0 && vreg->drms_mode_max_uA[i]
+				<= vreg->drms_mode_max_uA[i - 1]) {
+			dev_err(dev, "%s: %s elements are not in ascending order\n",
+				node->name, prop);
+			ret = -EINVAL;
+			goto done;
+		}
+	}
+
+done:
+	kfree(buf);
+	return ret;
+}
+
+/**
+ * rpmh_regulator_load_default_parameters() - initialize the RPMh resource
+ *		request for this regulator based on optional device tree
+ *		properties
+ * vreg:		Pointer to the individual rpmh-regulator resource
+ * dev:			Pointer to the top level rpmh-regulator PMIC device
+ * node:		Pointer to the individual rpmh-regulator resource
+ *			device node
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_load_default_parameters(struct rpmh_vreg *vreg,
+				struct device *dev, struct device_node *node)
+{
+	struct tcs_cmd cmd[2] = {};
+	const struct regulator_linear_range *range;
+	const char *prop;
+	int cmd_count = 0;
+	int ret, selector;
+	u32 uV;
+
+	if (vreg->hw_data->regulator_type == VRM) {
+		prop = "qcom,headroom-voltage";
+		ret = of_property_read_u32(node, prop, &uV);
+		if (!ret) {
+			if (uV > RPMH_VRM_HEADROOM_MAX_UV) {
+				dev_err(dev, "%s: %s=%u is invalid\n",
+					node->name, prop, uV);
+				return -EINVAL;
+			}
+
+			cmd[cmd_count].addr
+				= vreg->addr + RPMH_REGULATOR_REG_VRM_HEADROOM;
+			cmd[cmd_count++].data = DIV_ROUND_UP(uV, 1000);
+		}
+
+		prop = "qcom,regulator-initial-voltage";
+		ret = of_property_read_u32(node, prop, &uV);
+		if (!ret) {
+			range = &vreg->hw_data->voltage_range;
+			selector = DIV_ROUND_UP(uV - range->min_uV,
+					range->uV_step) + range->min_sel;
+			if (uV < range->min_uV || selector > range->max_sel) {
+				dev_err(dev, "%s: %s=%u is invalid\n",
+					node->name, prop, uV);
+				return -EINVAL;
+			}
+
+			vreg->voltage_selector = selector;
+
+			cmd[cmd_count].addr
+				= vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE;
+			cmd[cmd_count++].data
+				= DIV_ROUND_UP(selector * range->uV_step
+						+ range->min_uV, 1000);
+		}
+	}
+
+	if (cmd_count) {
+		ret = rpmh_regulator_send_request(vreg, cmd, cmd_count, true);
+		if (ret < 0) {
+			dev_err(dev, "%s: could not send default config, ret=%d\n",
+				node->name, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator
+ * vreg:		Pointer to the individual rpmh-regulator resource
+ * dev:			Pointer to the top level rpmh-regulator PMIC device
+ * node:		Pointer to the individual rpmh-regulator resource
+ *			device node
+ * pmic_id:		String used to identify the top level rpmh-regulator
+ *			PMIC device on the board
+ * rpmh_data:		Pointer to a null-terminated array of rpmh-regulator
+ *			resources defined for the top level PMIC device
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
+				struct device_node *node, const char *pmic_id,
+				const struct rpmh_vreg_init_data *rpmh_data)
+{
+	struct regulator_config reg_config = {};
+	char rpmh_resource_name[20] = "";
+	struct regulator_dev *rdev;
+	enum rpmh_regulator_type type;
+	struct regulator_init_data *init_data;
+	unsigned int mode;
+	int i, ret;
+
+	for (; rpmh_data->name; rpmh_data++)
+		if (!strcmp(rpmh_data->name, node->name))
+			break;
+
+	if (!rpmh_data->name) {
+		dev_err(dev, "Unknown regulator %s\n", node->name);
+		return -EINVAL;
+	}
+
+	scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name),
+		rpmh_data->resource_name, pmic_id);
+
+	vreg->addr = cmd_db_read_addr(rpmh_resource_name);
+	if (!vreg->addr) {
+		dev_err(dev, "%s: could not find RPMh address for resource %s\n",
+			node->name, rpmh_resource_name);
+		return -ENODEV;
+	}
+
+	vreg->rdesc.name = rpmh_data->name;
+	vreg->rdesc.supply_name = rpmh_data->supply_name;
+	vreg->regulator_type = rpmh_data->hw_data->regulator_type;
+	vreg->hw_data = rpmh_data->hw_data;
+
+	if (rpmh_data->hw_data->n_voltages) {
+		vreg->rdesc.linear_ranges = &rpmh_data->hw_data->voltage_range;
+		vreg->rdesc.n_linear_ranges = 1;
+		vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages;
+	}
+
+	type = vreg->regulator_type;
+
+	if (type == VRM) {
+		ret = rpmh_regulator_parse_vrm_modes(vreg, dev, node);
+		if (ret < 0)
+			return ret;
+	}
+
+	vreg->always_wait_for_ack = of_property_read_bool(node,
+						"qcom,always-wait-for-ack");
+
+	vreg->rdesc.owner	= THIS_MODULE;
+	vreg->rdesc.type	= REGULATOR_VOLTAGE;
+	vreg->rdesc.ops		= vreg->hw_data->ops;
+	vreg->rdesc.of_map_mode	= vreg->hw_data->of_map_mode;
+
+	init_data = of_get_regulator_init_data(dev, node, &vreg->rdesc);
+	if (!init_data)
+		return -ENOMEM;
+
+	if (type == XOB && init_data->constraints.min_uV) {
+		vreg->rdesc.fixed_uV = init_data->constraints.min_uV;
+		vreg->rdesc.n_voltages = 1;
+	}
+
+	if (vreg->hw_data->of_map_mode) {
+		init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
+		for (i = 0; i < RPMH_REGULATOR_MODE_COUNT; i++) {
+			mode = vreg->hw_data->of_map_mode(i);
+			if (mode > 0)
+				init_data->constraints.valid_modes_mask |= mode;
+		}
+	}
+
+	reg_config.dev		= dev;
+	reg_config.init_data	= init_data;
+	reg_config.of_node	= node;
+	reg_config.driver_data	= vreg;
+
+	ret = rpmh_regulator_load_default_parameters(vreg, dev, node);
+	if (ret < 0)
+		return ret;
+
+	rdev = devm_regulator_register(dev, &vreg->rdesc, &reg_config);
+	if (IS_ERR(rdev)) {
+		ret = PTR_ERR(rdev);
+		rdev = NULL;
+		dev_err(dev, "%s: devm_regulator_register() failed, ret=%d\n",
+			node->name, ret);
+		return ret;
+	}
+
+	dev_dbg(dev, "%s regulator registered for RPMh resource %s @ 0x%05X\n",
+		node->name, rpmh_resource_name, vreg->addr);

drivers/regulator/qcom_rpmh-regulator.c line 625

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+
+	return ret;
+}
+
+static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
+	[REGULATOR_MODE_STANDBY] = 4,
+	[REGULATOR_MODE_IDLE]    = 5,
+	[REGULATOR_MODE_NORMAL]  = -EINVAL,
+	[REGULATOR_MODE_FAST]    = 7,
+};

Define constants for the modes on the PMIC4 side?

Quoted 285 lines
drivers/regulator/qcom_rpmh-regulator.c new file +285 -0
+
+static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int mode)
+{
+	static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+		[RPMH_REGULATOR_MODE_RET]  = REGULATOR_MODE_STANDBY,
+		[RPMH_REGULATOR_MODE_LPM]  = REGULATOR_MODE_IDLE,
+		[RPMH_REGULATOR_MODE_AUTO] = -EINVAL,
+		[RPMH_REGULATOR_MODE_HPM]  = REGULATOR_MODE_FAST,
+	};
+
+	if (mode >= RPMH_REGULATOR_MODE_COUNT)
+		return -EINVAL;
+
+	return of_mode_map[mode];
+}
+
+static const u32 pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = {
+	[REGULATOR_MODE_STANDBY] = 4,
+	[REGULATOR_MODE_IDLE]    = 5,
+	[REGULATOR_MODE_NORMAL]  = 6,
+	[REGULATOR_MODE_FAST]    = 7,
+};
+
+static unsigned int rpmh_regulator_pmic4_smps_of_map_mode(unsigned int mode)
+{
+	static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+		[RPMH_REGULATOR_MODE_RET]  = REGULATOR_MODE_STANDBY,
+		[RPMH_REGULATOR_MODE_LPM]  = REGULATOR_MODE_IDLE,
+		[RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+		[RPMH_REGULATOR_MODE_HPM]  = REGULATOR_MODE_FAST,
+	};
+
+	if (mode >= RPMH_REGULATOR_MODE_COUNT)
+		return -EINVAL;
+
+	return of_mode_map[mode];
+}
+
+static const u32 pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = {
+	[REGULATOR_MODE_STANDBY] = -EINVAL,
+	[REGULATOR_MODE_IDLE]    = 1,
+	[REGULATOR_MODE_NORMAL]  = 2,
+	[REGULATOR_MODE_FAST]    = 3,
+};
+
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int mode)
+{
+	static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+		[RPMH_REGULATOR_MODE_RET]  = -EINVAL,
+		[RPMH_REGULATOR_MODE_LPM]  = REGULATOR_MODE_IDLE,
+		[RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+		[RPMH_REGULATOR_MODE_HPM]  = REGULATOR_MODE_FAST,
+	};
+
+	if (mode >= RPMH_REGULATOR_MODE_COUNT)
+		return -EINVAL;
+
+	return of_mode_map[mode];
+}
+
+static const struct rpmh_vreg_hw_data pmic4_pldo = {
+	.regulator_type = VRM,
+	.ops = &rpmh_regulator_vrm_ops,
+	.voltage_range = REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
+	.n_voltages = 256,
+	.pmic_mode_map = pmic_mode_map_pmic4_ldo,
+	.of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_pldo_lv = {
+	.regulator_type = VRM,
+	.ops = &rpmh_regulator_vrm_ops,
+	.voltage_range = REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
+	.n_voltages = 128,
+	.pmic_mode_map = pmic_mode_map_pmic4_ldo,
+	.of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_nldo = {
+	.regulator_type = VRM,
+	.ops = &rpmh_regulator_vrm_ops,
+	.voltage_range = REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
+	.n_voltages = 128,
+	.pmic_mode_map = pmic_mode_map_pmic4_ldo,
+	.of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_hfsmps3 = {
+	.regulator_type = VRM,
+	.ops = &rpmh_regulator_vrm_ops,
+	.voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
+	.n_voltages = 216,
+	.pmic_mode_map = pmic_mode_map_pmic4_smps,
+	.of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_ftsmps426 = {
+	.regulator_type = VRM,
+	.ops = &rpmh_regulator_vrm_ops,
+	.voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000),
+	.n_voltages = 259,
+	.pmic_mode_map = pmic_mode_map_pmic4_smps,
+	.of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_bob = {
+	.regulator_type = VRM,
+	.ops = &rpmh_regulator_vrm_bypass_ops,
+	.voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+	.n_voltages = 84,
+	.pmic_mode_map = pmic_mode_map_pmic4_bob,
+	.of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
+	.bypass_mode = 0,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_lvs = {
+	.regulator_type = XOB,
+	.ops = &rpmh_regulator_xob_ops,
+	/* LVS hardware does not support voltage or mode configuration. */
+};
+
+#define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \
+{ \
+	.name		= _name, \
+	.resource_name	= _resource_name, \
+	.hw_data	= _hw_data, \
+	.supply_name	= _supply_name, \
+}
+
+static const struct rpmh_vreg_init_data pm8998_vreg_data[] = {
+	RPMH_VREG("smps1",  "smp%s1",  &pmic4_ftsmps426, "vdd_s1"),
+	RPMH_VREG("smps2",  "smp%s2",  &pmic4_ftsmps426, "vdd_s2"),
+	RPMH_VREG("smps3",  "smp%s3",  &pmic4_hfsmps3,   "vdd_s3"),
+	RPMH_VREG("smps4",  "smp%s4",  &pmic4_hfsmps3,   "vdd_s4"),
+	RPMH_VREG("smps5",  "smp%s5",  &pmic4_hfsmps3,   "vdd_s5"),
+	RPMH_VREG("smps6",  "smp%s6",  &pmic4_ftsmps426, "vdd_s6"),
+	RPMH_VREG("smps7",  "smp%s7",  &pmic4_ftsmps426, "vdd_s7"),
+	RPMH_VREG("smps8",  "smp%s8",  &pmic4_ftsmps426, "vdd_s8"),
+	RPMH_VREG("smps9",  "smp%s9",  &pmic4_ftsmps426, "vdd_s9"),
+	RPMH_VREG("smps10", "smp%s10", &pmic4_ftsmps426, "vdd_s10"),
+	RPMH_VREG("smps11", "smp%s11", &pmic4_ftsmps426, "vdd_s11"),
+	RPMH_VREG("smps12", "smp%s12", &pmic4_ftsmps426, "vdd_s12"),
+	RPMH_VREG("smps13", "smp%s13", &pmic4_ftsmps426, "vdd_s13"),
+	RPMH_VREG("ldo1",   "ldo%s1",  &pmic4_nldo,      "vdd_l1_l27"),
+	RPMH_VREG("ldo2",   "ldo%s2",  &pmic4_nldo,      "vdd_l2_l8_l17"),
+	RPMH_VREG("ldo3",   "ldo%s3",  &pmic4_nldo,      "vdd_l3_l11"),
+	RPMH_VREG("ldo4",   "ldo%s4",  &pmic4_nldo,      "vdd_l4_l5"),
+	RPMH_VREG("ldo5",   "ldo%s5",  &pmic4_nldo,      "vdd_l4_l5"),
+	RPMH_VREG("ldo6",   "ldo%s6",  &pmic4_pldo,      "vdd_l6"),
+	RPMH_VREG("ldo7",   "ldo%s7",  &pmic4_pldo_lv,   "vdd_l7_l12_l14_l15"),
+	RPMH_VREG("ldo8",   "ldo%s8",  &pmic4_nldo,      "vdd_l2_l8_l17"),
+	RPMH_VREG("ldo9",   "ldo%s9",  &pmic4_pldo,      "vdd_l9"),
+	RPMH_VREG("ldo10",  "ldo%s10", &pmic4_pldo,      "vdd_l10_l23_l25"),
+	RPMH_VREG("ldo11",  "ldo%s11", &pmic4_nldo,      "vdd_l3_l11"),
+	RPMH_VREG("ldo12",  "ldo%s12", &pmic4_pldo_lv,   "vdd_l7_l12_l14_l15"),
+	RPMH_VREG("ldo13",  "ldo%s13", &pmic4_pldo,      "vdd_l13_l19_l21"),
+	RPMH_VREG("ldo14",  "ldo%s14", &pmic4_pldo_lv,   "vdd_l7_l12_l14_l15"),
+	RPMH_VREG("ldo15",  "ldo%s15", &pmic4_pldo_lv,   "vdd_l7_l12_l14_l15"),
+	RPMH_VREG("ldo16",  "ldo%s16", &pmic4_pldo,      "vdd_l16_l28"),
+	RPMH_VREG("ldo17",  "ldo%s17", &pmic4_nldo,      "vdd_l2_l8_l17"),
+	RPMH_VREG("ldo18",  "ldo%s18", &pmic4_pldo,      "vdd_l18_l22"),
+	RPMH_VREG("ldo19",  "ldo%s19", &pmic4_pldo,      "vdd_l13_l19_l21"),
+	RPMH_VREG("ldo20",  "ldo%s20", &pmic4_pldo,      "vdd_l20_l24"),
+	RPMH_VREG("ldo21",  "ldo%s21", &pmic4_pldo,      "vdd_l13_l19_l21"),
+	RPMH_VREG("ldo22",  "ldo%s22", &pmic4_pldo,      "vdd_l18_l22"),
+	RPMH_VREG("ldo23",  "ldo%s23", &pmic4_pldo,      "vdd_l10_l23_l25"),
+	RPMH_VREG("ldo24",  "ldo%s24", &pmic4_pldo,      "vdd_l20_l24"),
+	RPMH_VREG("ldo25",  "ldo%s25", &pmic4_pldo,      "vdd_l10_l23_l25"),
+	RPMH_VREG("ldo26",  "ldo%s26", &pmic4_nldo,      "vdd_l26"),
+	RPMH_VREG("ldo27",  "ldo%s27", &pmic4_nldo,      "vdd_l1_l27"),
+	RPMH_VREG("ldo28",  "ldo%s28", &pmic4_pldo,      "vdd_l16_l28"),
+	RPMH_VREG("lvs1",   "vs%s1",   &pmic4_lvs,       "vdd_lvs1_lvs2"),
+	RPMH_VREG("lvs2",   "vs%s2",   &pmic4_lvs,       "vdd_lvs1_lvs2"),
+	{},
+};
+
+static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = {
+	RPMH_VREG("bob",    "bob%s1",  &pmic4_bob,       "vdd_bob"),
+	{},
+};
+
+static const struct rpmh_vreg_init_data pm8005_vreg_data[] = {
+	RPMH_VREG("smps1",  "smp%s1",  &pmic4_ftsmps426, "vdd_s1"),
+	RPMH_VREG("smps2",  "smp%s2",  &pmic4_ftsmps426, "vdd_s2"),
+	RPMH_VREG("smps3",  "smp%s3",  &pmic4_ftsmps426, "vdd_s3"),
+	RPMH_VREG("smps4",  "smp%s4",  &pmic4_ftsmps426, "vdd_s4"),
+	{},
+};
+
+static int rpmh_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct rpmh_vreg_init_data *vreg_data;
+	struct rpmh_client *rpmh_client;
+	struct device_node *node;
+	struct rpmh_vreg *vreg;
+	const char *pmic_id;
+	int ret;
+
+	ret = cmd_db_ready();
+	if (ret < 0) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "Command DB not available, ret=%d\n", ret);
+		return ret;
+	}
+
+	vreg_data = of_device_get_match_data(dev);
+	if (!vreg_data)
+		return -ENODEV;
+
+	ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id);
+	if (ret < 0) {
+		dev_err(dev, "qcom,pmic-id missing in DT node\n");
+		return ret;
+	}
+
+	rpmh_client = rpmh_get_client(pdev);
+	if (IS_ERR(rpmh_client))
+		return PTR_ERR(rpmh_client);
+	platform_set_drvdata(pdev, rpmh_client);
+
+	for_each_available_child_of_node(dev->of_node, node) {
+		vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+		if (!vreg) {
+			ret = -ENOMEM;
+			of_node_put(node);
+			goto cleanup;
+		}
+
+		vreg->rpmh_client = rpmh_client;
+
+		ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id,
+						vreg_data);
+		if (ret < 0) {
+			of_node_put(node);
+			goto cleanup;
+		}
+	}
+
+	return 0;
+
+cleanup:
+	rpmh_release(rpmh_client);
+
+	return ret;
+}
+
+static int rpmh_regulator_remove(struct platform_device *pdev)
+{
+	struct rpmh_client *rpmh_client = platform_get_drvdata(pdev);
+
+	rpmh_release(rpmh_client);
+
+	return 0;
+}
+
+static const struct of_device_id rpmh_regulator_match_table[] = {
+	{
+		.compatible = "qcom,pm8998-rpmh-regulators",
+		.data = pm8998_vreg_data,
+	},
+	{
+		.compatible = "qcom,pmi8998-rpmh-regulators",
+		.data = pmi8998_vreg_data,
+	},
+	{
+		.compatible = "qcom,pm8005-rpmh-regulators",
+		.data = pm8005_vreg_data,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
+
+static struct platform_driver rpmh_regulator_driver = {
+	.driver		= {
+		.name		= "qcom-rpmh-regulator",
+		.of_match_table	= of_match_ptr(rpmh_regulator_match_table),
+	},
+	.probe		= rpmh_regulator_probe,
+	.remove		= rpmh_regulator_remove,
+};
+module_platform_driver(rpmh_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm RPMh regulator driver");
+MODULE_LICENSE("GPL v2");
#11 lore raw
Quoted 26 lines · Matthias Kaehlcke
Quoted 16 lines · David Collins

Add the QCOM RPMh regulator driver to manage PMIC regulators which are controlled via RPMh on some Qualcomm Technologies, Inc. SoCs. RPMh is a hardware block which contains several accelerators which are used to manage various hardware resources that are shared between the processors of the SoC. The final hardware state of a regulator is determined within RPMh by performing max aggregation of the requests made by all of the processors.
[...]
+/**
+ * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations
+ * @regulator_type: RPMh accelerator type used to manage this
+ * regulator
+ * @ops: Pointer to regulator ops callback structure
+ * @voltage_range: The single range of voltages supported by this
+ * PMIC regulator type

Quoted 8 lines

+ * @n_voltages: The number of unique voltage set points defined
+ * by voltage_range
+ * @pmic_mode_map: Array indexed by regulator framework mode
+ * containing PMIC hardware modes. Must be large
+ * enough to index all framework modes supported
+ * by this regulator hardware type.
+ * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined
+ * in device tree to a regulator framework mode

Quoted 3 lines

The name of the field is a bit misleading, this is a map of RPMh mode to regulator framework mode, the device tree just happens to be the place where this mapping is defined.

of_map_mode name is used here to match the struct regulator_desc field by the same name that it is assigned to [1]. Do you think that the name should be changed to something else?

Quoted 6 lines
Quoted 4 lines

+/**
+ * struct rpmh_vreg - individual rpmh regulator data structure encapsulating a
+ * single regulator device
+ * @rpmh_client: Handle used for rpmh communications

nit: RPMh

I'll change this.

Quoted 11 lines

drivers/regulator/qcom_rpmh-regulator.c line 114

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+struct rpmh_vreg {
+	struct rpmh_client		*rpmh_client;
+	u32				addr;
+	struct regulator_desc		rdesc;
+	const struct rpmh_vreg_hw_data	*hw_data;
+	enum rpmh_regulator_type	regulator_type;

This value is already available via rpmh_vreg->hw_data->regulator_type, why duplicate it? The field is assigned in rpmh_regulator_init_vreg() and only read once in the same function, there seems to be no need for it, not even to improve readability.

This is present to specifically allow for a future change to support overriding the regulator_type value from device tree in order to force RPMh resources to be handled via XOB instead of VRM in a board-specific manner. I included support of the property qcom,rpmh-resource-type in the first version of this patch. I removed this property from the second version of the patch based upon review feedback since SDM845 does not explicitly need it (though an upcoming chip will).

I'll remove regulator_type from struct rpmh_vreg. It shouldn't be particularly painful to add it back in when needed for XOB override support.

Quoted 11 lines

drivers/regulator/qcom_rpmh-regulator.c line 283

Quoted 7 lines
drivers/regulator/qcom_rpmh-regulator.c new file +7 -0
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	int i;
+
+	for (i = 0; i < vreg->drms_mode_count - 1; i++)
+		if (load_uA < vreg->drms_mode_max_uA[i])

Shouldn't this be '<='?

nit: IMO 'vreg->drms_mode_max_uA[i] >= load_uA' would be more readable.

I chose to use '<' here in order to maintain the non-inclusive limit semantics of the downstream RPMh regulator driver. E.g. with an LPM threshold of 10000 uA, load_uA == 10000 would result in a request for HPM instead of LPM.

I suppose that I can change this to '<=' to be more logically consistent.

Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 625

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
+	[REGULATOR_MODE_STANDBY] = 4,
+	[REGULATOR_MODE_IDLE]    = 5,
+	[REGULATOR_MODE_NORMAL]  = -EINVAL,
+	[REGULATOR_MODE_FAST]    = 7,
+};

Define constants for the modes on the PMIC4 side?

Are you suggesting something like this?

#define PMIC4_LDO_MODE_RETENTION 4
#define PMIC4_LDO_MODE_LPM 5
#define PMIC4_LDO_MODE_HPM 7

static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
[REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION,
[REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM,
[REGULATOR_MODE_NORMAL] = -EINVAL,
[REGULATOR_MODE_FAST] = PMIC4_LDO_MODE_HPM,
};

#define PMIC4_SMPS_MODE_RETENTION 4
#define PMIC4_SMPS_MODE_PFM 5
#define PMIC4_SMPS_MODE_AUTO 6
#define PMIC4_SMPS_MODE_PWM 7

static const u32 pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = {
[REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION,
[REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM,
[REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO,
[REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM,
};

I considered using this approach, but it didn't seem like it increased readability and did increase the line count. Each of the constants would only be used once. Would you prefer this style (or something else)?

Thanks,
David

[1]:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/regulator/driver.h?h=v4.17-rc1#n370

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#12 lore raw
Quoted 27 lines · David Collins
Quoted 26 lines · Matthias Kaehlcke
Quoted 24 lines · David Collins

Add the QCOM RPMh regulator driver to manage PMIC regulators which are controlled via RPMh on some Qualcomm Technologies, Inc. SoCs. RPMh is a hardware block which contains several accelerators which are used to manage various hardware resources that are shared between the processors of the SoC. The final hardware state of a regulator is determined within RPMh by performing max aggregation of the requests made by all of the processors.
[...]
+/**
+ * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations
+ * @regulator_type: RPMh accelerator type used to manage this
+ * regulator
+ * @ops: Pointer to regulator ops callback structure
+ * @voltage_range: The single range of voltages supported by this
+ * PMIC regulator type
+ * @n_voltages: The number of unique voltage set points defined
+ * by voltage_range
+ * @pmic_mode_map: Array indexed by regulator framework mode
+ * containing PMIC hardware modes. Must be large
+ * enough to index all framework modes supported
+ * by this regulator hardware type.
+ * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined
+ * in device tree to a regulator framework mode

Quoted 7 lines
Quoted 3 lines

The name of the field is a bit misleading, this is a map of RPMh mode to regulator framework mode, the device tree just happens to be the place where this mapping is defined.

of_map_mode name is used here to match the struct regulator_desc field by the same name that it is assigned to [1]. Do you think that the name should be changed to something else?

Thanks, I missed that it's the name of the field in struct regulator_desc field, in that case it certainly makes sense to use the same name.

Quoted 88 lines
Quoted 6 lines
Quoted 4 lines

+/**
+ * struct rpmh_vreg - individual rpmh regulator data structure encapsulating a
+ * single regulator device
+ * @rpmh_client: Handle used for rpmh communications

nit: RPMh

I'll change this.

Quoted 11 lines

drivers/regulator/qcom_rpmh-regulator.c line 114

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+struct rpmh_vreg {
+	struct rpmh_client		*rpmh_client;
+	u32				addr;
+	struct regulator_desc		rdesc;
+	const struct rpmh_vreg_hw_data	*hw_data;
+	enum rpmh_regulator_type	regulator_type;

This value is already available via rpmh_vreg->hw_data->regulator_type, why duplicate it? The field is assigned in rpmh_regulator_init_vreg() and only read once in the same function, there seems to be no need for it, not even to improve readability.

This is present to specifically allow for a future change to support overriding the regulator_type value from device tree in order to force RPMh resources to be handled via XOB instead of VRM in a board-specific manner. I included support of the property qcom,rpmh-resource-type in the first version of this patch. I removed this property from the second version of the patch based upon review feedback since SDM845 does not explicitly need it (though an upcoming chip will).

I'll remove regulator_type from struct rpmh_vreg. It shouldn't be particularly painful to add it back in when needed for XOB override support.

Quoted 11 lines

drivers/regulator/qcom_rpmh-regulator.c line 283

Quoted 7 lines
drivers/regulator/qcom_rpmh-regulator.c new file +7 -0
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+	struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+	int i;
+
+	for (i = 0; i < vreg->drms_mode_count - 1; i++)
+		if (load_uA < vreg->drms_mode_max_uA[i])

Shouldn't this be '<='?

nit: IMO 'vreg->drms_mode_max_uA[i] >= load_uA' would be more readable.

I chose to use '<' here in order to maintain the non-inclusive limit semantics of the downstream RPMh regulator driver. E.g. with an LPM threshold of 10000 uA, load_uA == 10000 would result in a request for HPM instead of LPM.

I suppose that I can change this to '<=' to be more logically consistent.

Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 625

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
+	[REGULATOR_MODE_STANDBY] = 4,
+	[REGULATOR_MODE_IDLE]    = 5,
+	[REGULATOR_MODE_NORMAL]  = -EINVAL,
+	[REGULATOR_MODE_FAST]    = 7,
+};

Define constants for the modes on the PMIC4 side?

Are you suggesting something like this?

#define PMIC4_LDO_MODE_RETENTION 4
#define PMIC4_LDO_MODE_LPM 5
#define PMIC4_LDO_MODE_HPM 7

static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
[REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION,
[REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM,
[REGULATOR_MODE_NORMAL] = -EINVAL,
[REGULATOR_MODE_FAST] = PMIC4_LDO_MODE_HPM,
};

#define PMIC4_SMPS_MODE_RETENTION 4
#define PMIC4_SMPS_MODE_PFM 5
#define PMIC4_SMPS_MODE_AUTO 6
#define PMIC4_SMPS_MODE_PWM 7

static const u32 pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = {
[REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION,
[REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM,
[REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO,
[REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM,
};

Quoted 3 lines

I considered using this approach, but it didn't seem like it increased readability and did increase the line count. Each of the constants would only be used once. Would you prefer this style (or something else)?

Personally I prefer this style, since the constants are more expressive than the literals. I agree that the 'inline' constant definition is a bit noisy, perhaps it would be better to move the definitions to the top of the file or group them before the definition of pmic_mode_map_pmic4_ldo. Alteratively you could create a drivers/regulator/qcom_rpmh-regulator.h and also move the definitions of struct struct rpmh_vreg_hw_data, rpmh_vreg, ... there.

Thanks

Matthias

#13 lore raw
Quoted 42 lines · Matthias Kaehlcke
Quoted 37 lines · David Collins
Quoted 10 lines · Matthias Kaehlcke

drivers/regulator/qcom_rpmh-regulator.c line 625

Quoted 7 lines · David Collins

[...]

drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
+	[REGULATOR_MODE_STANDBY] = 4,
+	[REGULATOR_MODE_IDLE]    = 5,
+	[REGULATOR_MODE_NORMAL]  = -EINVAL,
+	[REGULATOR_MODE_FAST]    = 7,
+};

Define constants for the modes on the PMIC4 side?

Are you suggesting something like this?

#define PMIC4_LDO_MODE_RETENTION 4
#define PMIC4_LDO_MODE_LPM 5
#define PMIC4_LDO_MODE_HPM 7

static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
[REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION,
[REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM,
[REGULATOR_MODE_NORMAL] = -EINVAL,
[REGULATOR_MODE_FAST] = PMIC4_LDO_MODE_HPM,
};

#define PMIC4_SMPS_MODE_RETENTION 4
#define PMIC4_SMPS_MODE_PFM 5
#define PMIC4_SMPS_MODE_AUTO 6
#define PMIC4_SMPS_MODE_PWM 7

static const u32 pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = {
[REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION,
[REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM,
[REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO,
[REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM,
};

Quoted 3 lines

I considered using this approach, but it didn't seem like it increased readability and did increase the line count. Each of the constants would only be used once. Would you prefer this style (or something else)?

Quoted 7 lines

Personally I prefer this style, since the constants are more expressive than the literals. I agree that the 'inline' constant definition is a bit noisy, perhaps it would be better to move the definitions to the top of the file or group them before the definition of pmic_mode_map_pmic4_ldo. Alteratively you could create a drivers/regulator/qcom_rpmh-regulator.h and also move the definitions of struct struct rpmh_vreg_hw_data, rpmh_vreg, ... there.

I'll add constants for the per-type regulator modes at the top of the file. We'll see if other reviewers like them.

Take care,
David

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
maintainer
#14 lore raw
In reply to Matthias Kaehlcke
Quoted 4 lines · Matthias Kaehlcke
Quoted 3 lines · David Collins

Add the QCOM RPMh regulator driver to manage PMIC regulators which are controlled via RPMh on some Qualcomm Technologies, Inc. SoCs. RPMh is a hardware block which contains several

Please delete unneeded context from mails when replying. Doing this
makes it much easier to find your reply in the message, helping ensure
it won't be missed by people scrolling through the irrelevant quoted
material.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180418/221bb544/attachment.sig>

#15 lore raw
In reply to David Collins

Hi,

drivers/regulator/qcom_rpmh-regulator.c line 44

Quoted 2 lines · David Collins
drivers/regulator/qcom_rpmh-regulator.c new file +2 -0
+#define RPMH_REGULATOR_DISABLE                 0x0
+#define RPMH_REGULATOR_ENABLE                  0x1

In the last version Stephen said he didn't like the above two #defines and you said you'd remove them, yet they are still here. Explanation?

Quoted 3 lines
+ * @drms_mode:                 Array of regulator framework modes which can
+ *                             be configured dynamically for this regulator
+ *                             via the set_load() callback.

Using the singular for something that is an array is confusing. Why not "drms_modes" or "drms_mode_arr"? In the past review you said 'Perhaps something along the lines of "drms_modes"'.

Quoted 5 lines
drivers/regulator/qcom_rpmh-regulator.c new file +5 -0
+struct rpmh_vreg {
+       struct rpmh_client              *rpmh_client;
+       u32                             addr;
+       struct regulator_desc           rdesc;
+       const struct rpmh_vreg_hw_data  *hw_data;

drivers/regulator/qcom_rpmh-regulator.c line 123

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+       enum rpmh_regulator_type        regulator_type;
+       bool                            always_wait_for_ack;
+       unsigned int                    *drms_mode;
+       int                             *drms_mode_max_uA;
+       size_t                          drms_mode_count;
+
+       bool                            enabled;
+       int                             voltage_selector;
+       unsigned int                    mode;
+       bool                            bypassed;

nit: stick next to "enabled" to make slightly better structure packing...

Quoted 5 lines
drivers/regulator/qcom_rpmh-regulator.c new file +5 -0
+static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+                               unsigned int selector)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       struct tcs_cmd cmd = {

drivers/regulator/qcom_rpmh-regulator.c line 225

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+               .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
+       };
+       int ret;
+
+       /* VRM voltage control register is set with voltage in millivolts. */
+       cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev,
+                                                       selector), 1000);
+
+       ret = rpmh_regulator_send_request(vreg, &cmd, 1,
+                                         selector > vreg->voltage_selector);

If you init vreg->voltage_selector to an error as I suggest in rpmh_regulator_load_default_parameters() you'll need to handle it here. See below.

drivers/regulator/qcom_rpmh-regulator.c line 244

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+                                       unsigned int mode, bool bypassed)
+{
+       struct tcs_cmd cmd = {
+               .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+       };

Please add:

if (mode & ~(REGULATOR_MODE_STANDBY |
REGULATOR_MODE_IDLE |
REGULATOR_MODE_NORMAL |
REGULATOR_MODE_FAST))
return -EINVAL;

That way if someone adds a new mode you don't index off the end of your array. Ah, I see, you have this in rpmh_regulator_vrm_set_mode by checking if mode > REGULATOR_MODE_STANDBY. That works. Can you move it here so it's closer to where the array access is?

drivers/regulator/qcom_rpmh-regulator.c line 284

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       int i;
+
+       for (i = 0; i < vreg->drms_mode_count - 1; i++)
+               if (load_uA < vreg->drms_mode_max_uA[i])
+                       break;

Can you put a warning here if the requested load_uA is larger than the largest supported, and/or perhaps consider it an error case?

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+       return rpmh_regulator_vrm_set_mode(rdev, vreg->drms_mode[i]);

Might not hurt to have a comment saying that this calls rpmh_regulator_vrm_set_mode() instead of calling rpmh_regulator_vrm_set_mode_bypass() directly because this is supposed to change the mode returned by a future call to get_mode().

drivers/regulator/qcom_rpmh-regulator.c line 296

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev,
+                               bool enable)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       int ret;
+
+       if (vreg->bypassed == enable)
+               return 0;

Just like for enable, remove this check. The regulator core does it for you.

drivers/regulator/qcom_rpmh-regulator.c line 312

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev,
+                               bool *enable)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+       *enable = vreg->bypassed;
+
+       return 0;

Should you return an error code if nobody has ever called set_bypass? ...or is it OK to just return "not bypassed"? Please document this in the code.

Quoted 25 lines
drivers/regulator/qcom_rpmh-regulator.c new file +25 -0
+static int rpmh_regulator_parse_vrm_modes(struct rpmh_vreg *vreg,
+                               struct device *dev, struct device_node *node)
+{
+       const char *prop;
+       int i, len, ret, mode;
+       u32 *buf;
+
+       /* qcom,allowed-drms-modes is optional */
+       prop = "qcom,allowed-drms-modes";
+       len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+       if (len < 0)
+               return 0;
+
+       vreg->drms_mode = devm_kcalloc(dev, len, sizeof(*vreg->drms_mode),
+                                       GFP_KERNEL);
+       vreg->drms_mode_max_uA = devm_kcalloc(dev, len,
+                                  sizeof(*vreg->drms_mode_max_uA), GFP_KERNEL);
+       if (!vreg->drms_mode || !vreg->drms_mode_max_uA)
+               return -ENOMEM;
+       vreg->drms_mode_count = len;
+
+       buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+

drivers/regulator/qcom_rpmh-regulator.c line 394

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+       ret = of_property_read_u32_array(node, prop, buf, len);
+       if (ret < 0) {
+               dev_err(dev, "%s: unable to read %s, ret=%d\n",
+                       node->name, prop, ret);
+               goto done;
+       }
+
+       for (i = 0; i < len; i++) {
+               mode = vreg->hw_data->of_map_mode(buf[i]);
+               if (mode <= 0) {

Should be < 0, not <= 0 right? Unless we take Javier's suggestion (see <https://patchwork.kernel.org/patch/10346081/>) and make 0 be invalid...

Quoted 46 lines
drivers/regulator/qcom_rpmh-regulator.c new file +46 -0
+/**
+ * rpmh_regulator_load_default_parameters() - initialize the RPMh resource
+ *             request for this regulator based on optional device tree
+ *             properties
+ * vreg:               Pointer to the individual rpmh-regulator resource
+ * dev:                        Pointer to the top level rpmh-regulator PMIC device
+ * node:               Pointer to the individual rpmh-regulator resource
+ *                     device node
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_load_default_parameters(struct rpmh_vreg *vreg,
+                               struct device *dev, struct device_node *node)
+{
+       struct tcs_cmd cmd[2] = {};
+       const struct regulator_linear_range *range;
+       const char *prop;
+       int cmd_count = 0;
+       int ret, selector;
+       u32 uV;
+
+       if (vreg->hw_data->regulator_type == VRM) {
+               prop = "qcom,headroom-voltage";
+               ret = of_property_read_u32(node, prop, &uV);
+               if (!ret) {
+                       if (uV > RPMH_VRM_HEADROOM_MAX_UV) {
+                               dev_err(dev, "%s: %s=%u is invalid\n",
+                                       node->name, prop, uV);
+                               return -EINVAL;
+                       }
+
+                       cmd[cmd_count].addr
+                               = vreg->addr + RPMH_REGULATOR_REG_VRM_HEADROOM;
+                       cmd[cmd_count++].data = DIV_ROUND_UP(uV, 1000);
+               }
+
+               prop = "qcom,regulator-initial-voltage";
+               ret = of_property_read_u32(node, prop, &uV);
+               if (!ret) {
+                       range = &vreg->hw_data->voltage_range;
+                       selector = DIV_ROUND_UP(uV - range->min_uV,
+                                       range->uV_step) + range->min_sel;
+                       if (uV < range->min_uV || selector > range->max_sel) {
+                               dev_err(dev, "%s: %s=%u is invalid\n",
+                                       node->name, prop, uV);
+                               return -EINVAL;

drivers/regulator/qcom_rpmh-regulator.c line 492

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+                       }
+
+                       vreg->voltage_selector = selector;
+
+                       cmd[cmd_count].addr
+                               = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE;
+                       cmd[cmd_count++].data
+                               = DIV_ROUND_UP(selector * range->uV_step
+                                               + range->min_uV, 1000);
+               }

Seems like you want an "else { vreg->voltage_selector = -EINVAL; }". Otherwise "get_voltage_sel" will return selector 0 before the first set, right?

Previously Mark said: "If the driver can't read values it should return an appropriate error code."
...and previously you said: "I'll try this out and see if the regulator framework complains during regulator registration."

Quoted 26 lines
drivers/regulator/qcom_rpmh-regulator.c new file +26 -0
+/**
+ * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator
+ * vreg:               Pointer to the individual rpmh-regulator resource
+ * dev:                        Pointer to the top level rpmh-regulator PMIC device
+ * node:               Pointer to the individual rpmh-regulator resource
+ *                     device node
+ * pmic_id:            String used to identify the top level rpmh-regulator
+ *                     PMIC device on the board
+ * rpmh_data:          Pointer to a null-terminated array of rpmh-regulator
+ *                     resources defined for the top level PMIC device
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
+                               struct device_node *node, const char *pmic_id,
+                               const struct rpmh_vreg_init_data *rpmh_data)
+{
+       struct regulator_config reg_config = {};
+       char rpmh_resource_name[20] = "";
+       struct regulator_dev *rdev;
+       enum rpmh_regulator_type type;
+       struct regulator_init_data *init_data;
+       unsigned int mode;
+       int i, ret;
+
+       for (; rpmh_data->name; rpmh_data++)

drivers/regulator/qcom_rpmh-regulator.c line 542

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+               if (!strcmp(rpmh_data->name, node->name))
+                       break;
+
+       if (!rpmh_data->name) {
+               dev_err(dev, "Unknown regulator %s\n", node->name);
+               return -EINVAL;
+       }
+
+       scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name),
+               rpmh_data->resource_name, pmic_id);

If the resulting string is exactly 20 characters then rpmh_resource_name won't be zero terminated. Please handle this properly.

Quoted 1 line

+static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {

I may have suggested using this as an array that could be used as a "map" (index by regulator framework mode and get the PMIC mode), but now that I see it it doesn't seem super appealing since the regulator framework mode is not 0, 1, 2, 3 but is actually 1, 2, 4, 8. ...but I guess it's not too bad--you're allocating 9 ints to map 4 elements and I guess that's about as efficient as you're going to get even if it feels a bit ugly.

...but still a few improvements:

* Don't specify the size of the array as "REGULATOR_MODE_STANDBY + 1". Let the compiler handle this. It should do the right thing. Then if someone ever changes the order of the #defines things will just work, I think.

* Make sure that users of these arrays check that the mode is one of the mode you know about. That way if someone does add a new mode you don't index off your array. I'll put a comment in the user.

Also: the type of this array is 'u32' but you're assigning -EINVAL in some cases. Please fix to be signed. Here and on other similar arrays.

drivers/regulator/qcom_rpmh-regulator.c line 678

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int mode)
+{
+       static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+               [RPMH_REGULATOR_MODE_RET]  = -EINVAL,
+               [RPMH_REGULATOR_MODE_LPM]  = REGULATOR_MODE_IDLE,
+               [RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+               [RPMH_REGULATOR_MODE_HPM]  = REGULATOR_MODE_FAST,
+       };

You're sticking a negative value in an array of unsigned inits. Here and in other similar functions.

I know, I know. The function is defined to return an unsigned int. It's wrong. of_regulator.c clearly puts the return code in a signed int. First attempt at fixing this is at
<https://patchwork.kernel.org/patch/10346081/>.

drivers/regulator/qcom_rpmh-regulator.c line 738

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static const struct rpmh_vreg_hw_data pmic4_bob = {
+       .regulator_type = VRM,
+       .ops = &rpmh_regulator_vrm_bypass_ops,
+       .voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+       .n_voltages = 84,
+       .pmic_mode_map = pmic_mode_map_pmic4_bob,
+       .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
+       .bypass_mode = 0,

Remove .bypass_mode from the structure and just change rpmh_regulator_vrm_set_mode_bypass() to set 0 if we're in bypass. Right now 100% of PMICs that support bypass use 0 as the bypass mode. If you ever have a future PMIC that uses a non-zero mode for bypass then we can always add this back. ...and if no future PMICs ever use a non-zero bypass mode then we don't need the complexity of having another field in this struct.

If you don't do this you might get arguments from some people saying that they don't like seeing inits of "= 0" in static structures (Linux conventions seem to like you to just assume that structs are zero-initted).

Quoted 16 lines
drivers/regulator/qcom_rpmh-regulator.c new file +16 -0
+static int rpmh_regulator_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct rpmh_vreg_init_data *vreg_data;
+       struct rpmh_client *rpmh_client;
+       struct device_node *node;
+       struct rpmh_vreg *vreg;
+       const char *pmic_id;
+       int ret;
+
+       ret = cmd_db_ready();
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Command DB not available, ret=%d\n", ret);
+               return ret;
+       }
Quoted 3 lines · In the last patch Stephen

We should just make rpmh parent device call cmd_db_ready() so that these devices aren't even populated until then and so that cmd_db_ready() is only in one place. Lina?

Any news here?

Quoted 28 lines
drivers/regulator/qcom_rpmh-regulator.c new file +28 -0
+
+       vreg_data = of_device_get_match_data(dev);
+       if (!vreg_data)
+               return -ENODEV;
+
+       ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id);
+       if (ret < 0) {
+               dev_err(dev, "qcom,pmic-id missing in DT node\n");
+               return ret;
+       }
+
+       rpmh_client = rpmh_get_client(pdev);
+       if (IS_ERR(rpmh_client))
+               return PTR_ERR(rpmh_client);
+       platform_set_drvdata(pdev, rpmh_client);
+
+       for_each_available_child_of_node(dev->of_node, node) {
+               vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+               if (!vreg) {
+                       ret = -ENOMEM;
+                       of_node_put(node);
+                       goto cleanup;
+               }
+
+               vreg->rpmh_client = rpmh_client;
+
+               ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id,
+                                               vreg_data);

drivers/regulator/qcom_rpmh-regulator.c line 868

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+               if (ret < 0) {
+                       of_node_put(node);
+                       goto cleanup;
+               }
+       }
+
+       return 0;
+
+cleanup:
+       rpmh_release(rpmh_client);

Still no devm_rpmh_get_client()? If Lina is too busy spinning her patch series for other stuff, just add it to RPMH as a patch in your series. I believe it's just this (untested):

---

int devm_rpmh_release(struct device *dev, void *res) {
struct platform_device *pdev = to_platform_device(dev); rpmh_release(pdev);
}

int devm_rpmh_get_client(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev); void *dr;
int rc;

  dr = devres_alloc(devm_rpmh_release, 0, GFP_KERNEL);
  if (!dr)
    return -ENOMEM;

  rc = rpmh_get_client(pdev);
  if (!rc)
    devres_add(dev, dr);
  else
    devres_free(dr);

return rc;
}

---

Note that you'll get rid of the error handling in probe, the whole remove function, and the need to do platform_set_drvdata().

-Doug

#16 lore raw
Quoted 6 lines · Doug Anderson

drivers/regulator/qcom_rpmh-regulator.c line 44

Quoted 2 lines · David Collins
drivers/regulator/qcom_rpmh-regulator.c new file +2 -0
+#define RPMH_REGULATOR_DISABLE                 0x0
+#define RPMH_REGULATOR_ENABLE                  0x1

In the last version Stephen said he didn't like the above two #defines and you said you'd remove them, yet they are still here. Explanation?

I thought that he was referring to the comments above the constants since his review comment was immediately following the second of two comments as opposed to these constants. I'll let him follow-up on this point if necessary.

Quoted 7 lines
Quoted 3 lines
+ * @drms_mode:                 Array of regulator framework modes which can
+ *                             be configured dynamically for this regulator
+ *                             via the set_load() callback.

Using the singular for something that is an array is confusing. Why not "drms_modes" or "drms_mode_arr"? In the past review you said 'Perhaps something along the lines of "drms_modes"'.

It seems awkward to me to use a plural for arrays as it leads to indexing like this: vreg->drms_modes[i]. "mode i" seems better than "modes i". However, I'm willing to change this to be drms_modes and drms_mode_max_uAs if that style is preferred.

Quoted 9 lines
Quoted 9 lines
drivers/regulator/qcom_rpmh-regulator.c new file +9 -0
+struct rpmh_vreg {
+       struct rpmh_client              *rpmh_client;
+       u32                             addr;
+       struct regulator_desc           rdesc;
+       const struct rpmh_vreg_hw_data  *hw_data;
+       enum rpmh_regulator_type        regulator_type;
+       bool                            always_wait_for_ack;
+       unsigned int                    *drms_mode;
+       int                             *drms_mode_max_uA;
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 123

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+       size_t                          drms_mode_count;
+
+       bool                            enabled;
+       int                             voltage_selector;
+       unsigned int                    mode;
+       bool                            bypassed;

nit: stick next to "enabled" to make slightly better structure packing...

Ok

Quoted 11 lines
Quoted 11 lines
drivers/regulator/qcom_rpmh-regulator.c new file +11 -0
+static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+                               unsigned int selector)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       struct tcs_cmd cmd = {
+               .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
+       };
+       int ret;
+
+       /* VRM voltage control register is set with voltage in millivolts. */
+       cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev,
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 225

Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+                                                       selector), 1000);
+
+       ret = rpmh_regulator_send_request(vreg, &cmd, 1,
+                                         selector > vreg->voltage_selector);

If you init vreg->voltage_selector to an error as I suggest in rpmh_regulator_load_default_parameters() you'll need to handle it here. See below.

Ok

Quoted 15 lines

drivers/regulator/qcom_rpmh-regulator.c line 244

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+                                       unsigned int mode, bool bypassed)
+{
+       struct tcs_cmd cmd = {
+               .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+       };

Please add:

if (mode & ~(REGULATOR_MODE_STANDBY |
REGULATOR_MODE_IDLE |
REGULATOR_MODE_NORMAL |
REGULATOR_MODE_FAST))
return -EINVAL;

Quoted 4 lines

That way if someone adds a new mode you don't index off the end of your array. Ah, I see, you have this in rpmh_regulator_vrm_set_mode by checking if mode > REGULATOR_MODE_STANDBY. That works. Can you move it here so it's closer to where the array access is?

Theoretically, the (mode > REGULATOR_MODE_STANDBY) check in rpmh_regulator_vrm_set_mode() shouldn't be necessary at all. I felt safer leaving it in though. The framework ensures that no mode values may be passed into rpmh_regulator_vrm_set_mode() which is not identified in constraints.valid_modes_mask. Similar sanitization happens for internal rpmh_regulator_vrm_set_mode() calls.

I'll move the (mode > REGULATOR_MODE_STANDBY) check into rpmh_regulator_vrm_set_mode_bypass().

Quoted 11 lines

drivers/regulator/qcom_rpmh-regulator.c line 284

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       int i;
+
+       for (i = 0; i < vreg->drms_mode_count - 1; i++)
+               if (load_uA < vreg->drms_mode_max_uA[i])
+                       break;

Can you put a warning here if the requested load_uA is larger than the largest supported, and/or perhaps consider it an error case?

I'll add a warning.

Quoted 7 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+       return rpmh_regulator_vrm_set_mode(rdev, vreg->drms_mode[i]);

Might not hurt to have a comment saying that this calls rpmh_regulator_vrm_set_mode() instead of calling rpmh_regulator_vrm_set_mode_bypass() directly because this is supposed to change the mode returned by a future call to get_mode().

This seems pretty clear on inspection of the very closely spaced functions. I don't see the need for a comment about it.

Quoted 10 lines

drivers/regulator/qcom_rpmh-regulator.c line 296

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev,
+                               bool enable)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       int ret;
+
+       if (vreg->bypassed == enable)
+               return 0;

Just like for enable, remove this check. The regulator core does it for you.

Ok

Quoted 4 lines
Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev,
+                               bool *enable)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 312

Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+
+       *enable = vreg->bypassed;
+
+       return 0;

Should you return an error code if nobody has ever called set_bypass? ...or is it OK to just return "not bypassed"? Please document this in the code.

I think it is fine to return "not bypassed" by default if there is no configuration in place to enable bypassing. How are you suggesting that this be documented in the code?

Quoted 31 lines
Quoted 31 lines
drivers/regulator/qcom_rpmh-regulator.c new file +31 -0
+static int rpmh_regulator_parse_vrm_modes(struct rpmh_vreg *vreg,
+                               struct device *dev, struct device_node *node)
+{
+       const char *prop;
+       int i, len, ret, mode;
+       u32 *buf;
+
+       /* qcom,allowed-drms-modes is optional */
+       prop = "qcom,allowed-drms-modes";
+       len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+       if (len < 0)
+               return 0;
+
+       vreg->drms_mode = devm_kcalloc(dev, len, sizeof(*vreg->drms_mode),
+                                       GFP_KERNEL);
+       vreg->drms_mode_max_uA = devm_kcalloc(dev, len,
+                                  sizeof(*vreg->drms_mode_max_uA), GFP_KERNEL);
+       if (!vreg->drms_mode || !vreg->drms_mode_max_uA)
+               return -ENOMEM;
+       vreg->drms_mode_count = len;
+
+       buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = of_property_read_u32_array(node, prop, buf, len);
+       if (ret < 0) {
+               dev_err(dev, "%s: unable to read %s, ret=%d\n",
+                       node->name, prop, ret);
+               goto done;
+       }
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 394

Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+
+       for (i = 0; i < len; i++) {
+               mode = vreg->hw_data->of_map_mode(buf[i]);
+               if (mode <= 0) {

Should be < 0, not <= 0 right? Unless we take Javier's suggestion (see <https://patchwork.kernel.org/patch/10346081/>) and make 0 be invalid...

It looks like the way forward is REGULATOR_MODE_INVALID == 0 so '<= 0' is fine here. I suppose that it could be changed to '== REGULATOR_MODE_INVALID' as well.

Quoted 25 lines
Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+               prop = "qcom,regulator-initial-voltage";
+               ret = of_property_read_u32(node, prop, &uV);
+               if (!ret) {
+                       range = &vreg->hw_data->voltage_range;
+                       selector = DIV_ROUND_UP(uV - range->min_uV,
+                                       range->uV_step) + range->min_sel;
+                       if (uV < range->min_uV || selector > range->max_sel) {
+                               dev_err(dev, "%s: %s=%u is invalid\n",
+                                       node->name, prop, uV);
+                               return -EINVAL;

drivers/regulator/qcom_rpmh-regulator.c line 492

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+                       }
+
+                       vreg->voltage_selector = selector;
+
+                       cmd[cmd_count].addr
+                               = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE;
+                       cmd[cmd_count++].data
+                               = DIV_ROUND_UP(selector * range->uV_step
+                                               + range->min_uV, 1000);
+               }

Seems like you want an "else { vreg->voltage_selector = -EINVAL; }". Otherwise "get_voltage_sel" will return selector 0 before the first set, right?

Quoted 4 lines

Previously Mark said: "If the driver can't read values it should return an appropriate error code."
...and previously you said: "I'll try this out and see if the regulator framework complains during regulator registration."

I tested out what happens when vreg->voltage_selector = -EINVAL is set when qcom,regulator-initial-voltage is not present. This results in devm_regulator_register() failing and subsequently causing the qcom_rpmh-regulator probe to fail. The error happens in machine_constraints_voltage() [1].

This leaves two courses of action:
1. (current patch set) allow voltage_selector to stay 0 if uninitialized
2. Set voltage_selector = -EINVAL by default and specify in DT binding documentation that qcom,regulator-initial-voltage is required for VRM managed RPMh regulator resources which have regulator-min-microvolt and regulator-max-microvolt specified.

Are you ok with the DT implications of option #2?

Quoted 19 lines
Quoted 19 lines
drivers/regulator/qcom_rpmh-regulator.c new file +19 -0
+static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
+                               struct device_node *node, const char *pmic_id,
+                               const struct rpmh_vreg_init_data *rpmh_data)
+{
+       struct regulator_config reg_config = {};
+       char rpmh_resource_name[20] = "";
+       struct regulator_dev *rdev;
+       enum rpmh_regulator_type type;
+       struct regulator_init_data *init_data;
+       unsigned int mode;
+       int i, ret;
+
+       for (; rpmh_data->name; rpmh_data++)
+               if (!strcmp(rpmh_data->name, node->name))
+                       break;
+
+       if (!rpmh_data->name) {
+               dev_err(dev, "Unknown regulator %s\n", node->name);
+               return -EINVAL;
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 542

Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+       }
+
+       scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name),
+               rpmh_data->resource_name, pmic_id);

If the resulting string is exactly 20 characters then rpmh_resource_name won't be zero terminated. Please handle this properly.

The output of scnprintf() is always null-terminated; therefore, no other check is needed. Also, RPMh resource names stored in SMEM command DB data structure are at most 8 bytes (<= 7 char + '\0' or 8 char and no '\0') so using 20 chars in the buffer is overkill anyway.

Quoted 9 lines
Quoted 1 line

+static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {

I may have suggested using this as an array that could be used as a "map" (index by regulator framework mode and get the PMIC mode), but now that I see it it doesn't seem super appealing since the regulator framework mode is not 0, 1, 2, 3 but is actually 1, 2, 4, 8. ...but I guess it's not too bad--you're allocating 9 ints to map 4 elements and I guess that's about as efficient as you're going to get even if it feels a bit ugly.

I'm ok with the sparse mapping as it makes indexing as simple as possible and the extra space used is insignificant.

Quoted 10 lines

...but still a few improvements:

* Don't specify the size of the array as "REGULATOR_MODE_STANDBY + 1". Let the compiler handle this. It should do the right thing. Then if someone ever changes the order of the #defines things will just work, I think.

* Make sure that users of these arrays check that the mode is one of the mode you know about. That way if someone does add a new mode you don't index off your array. I'll put a comment in the user.

Even if a new mode was added, the relative ordering of the existing modes should not change and valid_modes_mask will only allow modes currently supported by the driver. I'd like to keep the bound checks as simple as possible. By explicitly sizing the arrays and then only checking for mode

Quoted 1 line

REGULATOR_MODE_STANDBY when indexing into the array we can be sure that

no out-of-bound access can ever occur. Also, if one of the existing mode value was made larger than REGULATOR_MODE_STANDBY it would be easy to catch as it would cause a compilation error.

Thus, I'd prefer to keep the array sizing and index checking as-in unless there is a major objection.

Quoted 3 lines

Also: the type of this array is 'u32' but you're assigning -EINVAL in some cases. Please fix to be signed. Here and on other similar arrays.

Ok.

Quoted 12 lines

drivers/regulator/qcom_rpmh-regulator.c line 678

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int mode)
+{
+       static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+               [RPMH_REGULATOR_MODE_RET]  = -EINVAL,
+               [RPMH_REGULATOR_MODE_LPM]  = REGULATOR_MODE_IDLE,
+               [RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+               [RPMH_REGULATOR_MODE_HPM]  = REGULATOR_MODE_FAST,
+       };

You're sticking a negative value in an array of unsigned inits. Here and in other similar functions.

Quoted 4 lines

I know, I know. The function is defined to return an unsigned int. It's wrong. of_regulator.c clearly puts the return code in a signed int. First attempt at fixing this is at
<https://patchwork.kernel.org/patch/10346081/>.

I can change the error cases to use REGULATOR_MODE_INVALID which is added by this change still under review [2].

Quoted 17 lines

drivers/regulator/qcom_rpmh-regulator.c line 738

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static const struct rpmh_vreg_hw_data pmic4_bob = {
+       .regulator_type = VRM,
+       .ops = &rpmh_regulator_vrm_bypass_ops,
+       .voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+       .n_voltages = 84,
+       .pmic_mode_map = pmic_mode_map_pmic4_bob,
+       .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
+       .bypass_mode = 0,

Remove .bypass_mode from the structure and just change rpmh_regulator_vrm_set_mode_bypass() to set 0 if we're in bypass. Right now 100% of PMICs that support bypass use 0 as the bypass mode. If you ever have a future PMIC that uses a non-zero mode for bypass then we can always add this back. ...and if no future PMICs ever use a non-zero bypass mode then we don't need the complexity of having another field in this struct.

Quoted 4 lines

If you don't do this you might get arguments from some people saying that they don't like seeing inits of "= 0" in static structures (Linux conventions seem to like you to just assume that structs are zero-initted).

Upcoming PMICs use 2 for bypass mode instead of 0. That is why I left this in. I suppose that I can remove this member for now and add it back in when newer PMIC support is added.

Quoted 19 lines
Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+static int rpmh_regulator_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct rpmh_vreg_init_data *vreg_data;
+       struct rpmh_client *rpmh_client;
+       struct device_node *node;

drivers/regulator/qcom_rpmh-regulator.c line 830

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+       struct rpmh_vreg *vreg;
+       const char *pmic_id;
+       int ret;
+
+       ret = cmd_db_ready();
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Command DB not available, ret=%d\n", ret);
+               return ret;
+       }

In the last patch Stephen said:

Quoted 5 lines
Quoted 3 lines

We should just make rpmh parent device call cmd_db_ready() so that these devices aren't even populated until then and so that cmd_db_ready() is only in one place. Lina?

Any news here?

I don't think that Lina has tried to include this in her rpmh series yet.

Quoted 31 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+cleanup:
+       rpmh_release(rpmh_client);

Still no devm_rpmh_get_client()? If Lina is too busy spinning her patch series for other stuff, just add it to RPMH as a patch in your series. I believe it's just this (untested):

---

int devm_rpmh_release(struct device *dev, void *res) {
struct platform_device *pdev = to_platform_device(dev); rpmh_release(pdev);
}

int devm_rpmh_get_client(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev); void *dr;
int rc;

  dr = devres_alloc(devm_rpmh_release, 0, GFP_KERNEL);
  if (!dr)
    return -ENOMEM;

  rc = rpmh_get_client(pdev);
  if (!rc)
    devres_add(dev, dr);
  else
    devres_free(dr);
Quoted 7 lines

return rc;
}

---

Note that you'll get rid of the error handling in probe, the whole remove function, and the need to do platform_set_drvdata().

My understanding is that Lina is going to remove both rpmh_get_client() and rpmh_release(). In their place, rpmh functions will use the consumer device pointer as a handle and manage any necessary state internally [3]. I'll update this patch once she uploads a new series with this interface modification.

Thanks,
David

[1]:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/regulator/core.c?h=v4.17-rc1#n888
[2]: https://patchwork.kernel.org/patch/10348631/
[3]: https://lkml.org/lkml/2018/4/10/1273

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#17 lore raw

Quoting David Collins (2018-04-18 16:30:26)

Quoted 8 lines · Doug Anderson
Quoted 6 lines · David Collins

drivers/regulator/qcom_rpmh-regulator.c line 44

Quoted 2 lines
drivers/regulator/qcom_rpmh-regulator.c new file +2 -0
+#define RPMH_REGULATOR_DISABLE                 0x0
+#define RPMH_REGULATOR_ENABLE                  0x1

In the last version Stephen said he didn't like the above two #defines and you said you'd remove them, yet they are still here. Explanation?

Quoted 5 lines

I thought that he was referring to the comments above the constants since his review comment was immediately following the second of two comments as opposed to these constants. I'll let him follow-up on this point if necessary.

I think I was talking about the comments. The defines don't look super useful either though.

#18 lore raw
In reply to David Collins

Hi,

Quoted 4 lines · David Collins
Quoted 4 lines
Quoted 3 lines
+ * @drms_mode:                 Array of regulator framework modes which can
+ *                             be configured dynamically for this regulator
+ *                             via the set_load() callback.
Quoted 8 lines
Quoted 3 lines

Using the singular for something that is an array is confusing. Why not "drms_modes" or "drms_mode_arr"? In the past review you said 'Perhaps something along the lines of "drms_modes"'.

It seems awkward to me to use a plural for arrays as it leads to indexing like this: vreg->drms_modes[i]. "mode i" seems better than "modes i". However, I'm willing to change this to be drms_modes and drms_mode_max_uAs if that style is preferred.

I'd very much like a plural here.

Quoted 27 lines
Quoted 15 lines

drivers/regulator/qcom_rpmh-regulator.c line 244

Quoted 6 lines
drivers/regulator/qcom_rpmh-regulator.c new file +6 -0
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+                                       unsigned int mode, bool bypassed)
+{
+       struct tcs_cmd cmd = {
+               .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+       };

Please add:

if (mode & ~(REGULATOR_MODE_STANDBY |
REGULATOR_MODE_IDLE |
REGULATOR_MODE_NORMAL |
REGULATOR_MODE_FAST))
return -EINVAL;

Quoted 4 lines

That way if someone adds a new mode you don't index off the end of your array. Ah, I see, you have this in rpmh_regulator_vrm_set_mode by checking if mode > REGULATOR_MODE_STANDBY. That works. Can you move it here so it's closer to where the array access is?

Theoretically, the (mode > REGULATOR_MODE_STANDBY) check in rpmh_regulator_vrm_set_mode() shouldn't be necessary at all. I felt safer leaving it in though. The framework ensures that no mode values may be passed into rpmh_regulator_vrm_set_mode() which is not identified in constraints.valid_modes_mask. Similar sanitization happens for internal rpmh_regulator_vrm_set_mode() calls.

Quoted 2 lines

I'll move the (mode > REGULATOR_MODE_STANDBY) check into rpmh_regulator_vrm_set_mode_bypass().

Ah, good point about the valid_modes_mask! I'm happy with moving the test here. I wouldn't mind a comment saying that the check is probably overkill because the framework already checks valid_modes_mask but shouldn't hurt.

Quoted 10 lines
Quoted 7 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+
+       return rpmh_regulator_vrm_set_mode(rdev, vreg->drms_mode[i]);

Might not hurt to have a comment saying that this calls rpmh_regulator_vrm_set_mode() instead of calling rpmh_regulator_vrm_set_mode_bypass() directly because this is supposed to change the mode returned by a future call to get_mode().

This seems pretty clear on inspection of the very closely spaced functions. I don't see the need for a comment about it.

It wasn't clear to me--I thought it might have just been because you didn't want to manually pass in the current bypass state. Then I looked at the function and thought there might have been a bug because it was saving the mode. Then I looked back at the regulator framework and finally came to the conclusion that set_load() is supposed to change the mode (AKA: you'd expect that calling get_mode() after set_load() would show you the mode you ended up at).

I guess this is all perhaps obvious to any regulator framework experts, but since I spent 5 minutes digging through all that it seemed like it deserved a comment to save the next person the 5 minutes. ...but I won't insist.

Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev,
+                               bool *enable)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 312

Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+
+       *enable = vreg->bypassed;
+
+       return 0;

Should you return an error code if nobody has ever called set_bypass? ...or is it OK to just return "not bypassed"? Please document this in the code.

Quoted 3 lines

I think it is fine to return "not bypassed" by default if there is no configuration in place to enable bypassing. How are you suggesting that this be documented in the code?

I guess I wish the function had comments and then it could be documented there. ...but none of the functions in this file do...

I guess you're right that it's clear enough without a comment, so let's just leave it as is.

Quoted 40 lines
Quoted 31 lines
Quoted 31 lines
drivers/regulator/qcom_rpmh-regulator.c new file +31 -0
+static int rpmh_regulator_parse_vrm_modes(struct rpmh_vreg *vreg,
+                               struct device *dev, struct device_node *node)
+{
+       const char *prop;
+       int i, len, ret, mode;
+       u32 *buf;
+
+       /* qcom,allowed-drms-modes is optional */
+       prop = "qcom,allowed-drms-modes";
+       len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+       if (len < 0)
+               return 0;
+
+       vreg->drms_mode = devm_kcalloc(dev, len, sizeof(*vreg->drms_mode),
+                                       GFP_KERNEL);
+       vreg->drms_mode_max_uA = devm_kcalloc(dev, len,
+                                  sizeof(*vreg->drms_mode_max_uA), GFP_KERNEL);
+       if (!vreg->drms_mode || !vreg->drms_mode_max_uA)
+               return -ENOMEM;
+       vreg->drms_mode_count = len;
+
+       buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = of_property_read_u32_array(node, prop, buf, len);
+       if (ret < 0) {
+               dev_err(dev, "%s: unable to read %s, ret=%d\n",
+                       node->name, prop, ret);
+               goto done;
+       }
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 394

Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+
+       for (i = 0; i < len; i++) {
+               mode = vreg->hw_data->of_map_mode(buf[i]);
+               if (mode <= 0) {

Should be < 0, not <= 0 right? Unless we take Javier's suggestion (see <https://patchwork.kernel.org/patch/10346081/>) and make 0 be invalid...

Quoted 3 lines

It looks like the way forward is REGULATOR_MODE_INVALID == 0 so '<= 0' is fine here. I suppose that it could be changed to '== REGULATOR_MODE_INVALID' as well.

Yes, assuming my patch lands using "==" is better. Checking whether an unsigned value is <= 0 is confusing.

Quoted 36 lines
Quoted 25 lines
Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+               prop = "qcom,regulator-initial-voltage";
+               ret = of_property_read_u32(node, prop, &uV);
+               if (!ret) {
+                       range = &vreg->hw_data->voltage_range;
+                       selector = DIV_ROUND_UP(uV - range->min_uV,
+                                       range->uV_step) + range->min_sel;
+                       if (uV < range->min_uV || selector > range->max_sel) {
+                               dev_err(dev, "%s: %s=%u is invalid\n",
+                                       node->name, prop, uV);
+                               return -EINVAL;

drivers/regulator/qcom_rpmh-regulator.c line 492

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+                       }
+
+                       vreg->voltage_selector = selector;
+
+                       cmd[cmd_count].addr
+                               = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE;
+                       cmd[cmd_count++].data
+                               = DIV_ROUND_UP(selector * range->uV_step
+                                               + range->min_uV, 1000);
+               }

Seems like you want an "else { vreg->voltage_selector = -EINVAL; }". Otherwise "get_voltage_sel" will return selector 0 before the first set, right?

Quoted 4 lines

Previously Mark said: "If the driver can't read values it should return an appropriate error code."
...and previously you said: "I'll try this out and see if the regulator framework complains during regulator registration."

I tested out what happens when vreg->voltage_selector = -EINVAL is set when qcom,regulator-initial-voltage is not present. This results in devm_regulator_register() failing and subsequently causing the qcom_rpmh-regulator probe to fail. The error happens in machine_constraints_voltage() [1].

Quoted 8 lines

This leaves two courses of action:
1. (current patch set) allow voltage_selector to stay 0 if uninitialized
2. Set voltage_selector = -EINVAL by default and specify in DT binding documentation that qcom,regulator-initial-voltage is required for VRM managed RPMh regulator resources which have regulator-min-microvolt and regulator-max-microvolt specified.

Are you ok with the DT implications of option #2?

You'd need to ask Mark if he's OK with it, but a option #3 is to add a patch to your series fix the regulator framework to try setting the voltage if _regulator_get_voltage() fails. Presumably in machine_constraints_voltage() you'd now do something like:

  int target_min, target_max;
  int current_uV = _regulator_get_voltage(rdev);
  if (current_uV < 0) {
    /* Maybe this regulator's hardware can't be read and needs to be initted */
    _regulator_do_set_voltage(
      rdev, rdev->constraints->min_uV, rdev->constraints->min_uV);
    current_uV = _regulator_get_voltage(rdev);
  }
  if (current_uV < 0) {
    rdev_err(rdev,
      "failed to get the current voltage(%d)\n",
      current_uV);
      return current_uV;
  }

If Mark doesn't like that then I guess I'd be OK w/ initting it to 0 but this needs to be documented _somewhere_ (unlike for bypass it's not obvious, so you need to find someplace to put it). I'd rather not hack the DT to deal with our software limitations.

Quoted 28 lines
Quoted 19 lines
Quoted 19 lines
drivers/regulator/qcom_rpmh-regulator.c new file +19 -0
+static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
+                               struct device_node *node, const char *pmic_id,
+                               const struct rpmh_vreg_init_data *rpmh_data)
+{
+       struct regulator_config reg_config = {};
+       char rpmh_resource_name[20] = "";
+       struct regulator_dev *rdev;
+       enum rpmh_regulator_type type;
+       struct regulator_init_data *init_data;
+       unsigned int mode;
+       int i, ret;
+
+       for (; rpmh_data->name; rpmh_data++)
+               if (!strcmp(rpmh_data->name, node->name))
+                       break;
+
+       if (!rpmh_data->name) {
+               dev_err(dev, "Unknown regulator %s\n", node->name);
+               return -EINVAL;
Quoted 8 lines

drivers/regulator/qcom_rpmh-regulator.c line 542

Quoted 4 lines
drivers/regulator/qcom_rpmh-regulator.c new file +4 -0
+       }
+
+       scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name),
+               rpmh_data->resource_name, pmic_id);

If the resulting string is exactly 20 characters then rpmh_resource_name won't be zero terminated. Please handle this properly.

Quoted 4 lines

The output of scnprintf() is always null-terminated; therefore, no other check is needed. Also, RPMh resource names stored in SMEM command DB data structure are at most 8 bytes (<= 7 char + '\0' or 8 char and no '\0') so using 20 chars in the buffer is overkill anyway.

Wow, not sure where I looked to see that scnprintf() didn't always null-terminate. Sorry. Ignore this.

Quoted 29 lines
Quoted 9 lines
Quoted 1 line

+static const u32 pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {

I may have suggested using this as an array that could be used as a "map" (index by regulator framework mode and get the PMIC mode), but now that I see it it doesn't seem super appealing since the regulator framework mode is not 0, 1, 2, 3 but is actually 1, 2, 4, 8. ...but I guess it's not too bad--you're allocating 9 ints to map 4 elements and I guess that's about as efficient as you're going to get even if it feels a bit ugly.

I'm ok with the sparse mapping as it makes indexing as simple as possible and the extra space used is insignificant.

Quoted 10 lines

...but still a few improvements:

* Don't specify the size of the array as "REGULATOR_MODE_STANDBY + 1". Let the compiler handle this. It should do the right thing. Then if someone ever changes the order of the #defines things will just work, I think.

* Make sure that users of these arrays check that the mode is one of the mode you know about. That way if someone does add a new mode you don't index off your array. I'll put a comment in the user.

Even if a new mode was added, the relative ordering of the existing modes should not change and valid_modes_mask will only allow modes currently supported by the driver. I'd like to keep the bound checks as simple as possible. By explicitly sizing the arrays and then only checking for mode

Quoted 7 lines
Quoted 1 line

REGULATOR_MODE_STANDBY when indexing into the array we can be sure that

no out-of-bound access can ever occur. Also, if one of the existing mode value was made larger than REGULATOR_MODE_STANDBY it would be easy to catch as it would cause a compilation error.

Thus, I'd prefer to keep the array sizing and index checking as-in unless there is a major objection.

OK.

Quoted 12 lines
Quoted 12 lines

drivers/regulator/qcom_rpmh-regulator.c line 678

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int mode)
+{
+       static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+               [RPMH_REGULATOR_MODE_RET]  = -EINVAL,
+               [RPMH_REGULATOR_MODE_LPM]  = REGULATOR_MODE_IDLE,
+               [RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+               [RPMH_REGULATOR_MODE_HPM]  = REGULATOR_MODE_FAST,
+       };

You're sticking a negative value in an array of unsigned inits. Here and in other similar functions.

Quoted 7 lines
Quoted 4 lines

I know, I know. The function is defined to return an unsigned int. It's wrong. of_regulator.c clearly puts the return code in a signed int. First attempt at fixing this is at
<https://patchwork.kernel.org/patch/10346081/>.

I can change the error cases to use REGULATOR_MODE_INVALID which is added by this change still under review [2].

I haven't seen Mark NAK it (yet), so for lack of a better option I'd start using it in your patch and document in the commit message that it depends on my patch.

Quoted 17 lines
Quoted 17 lines

drivers/regulator/qcom_rpmh-regulator.c line 738

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static const struct rpmh_vreg_hw_data pmic4_bob = {
+       .regulator_type = VRM,
+       .ops = &rpmh_regulator_vrm_bypass_ops,
+       .voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+       .n_voltages = 84,
+       .pmic_mode_map = pmic_mode_map_pmic4_bob,
+       .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
+       .bypass_mode = 0,

Remove .bypass_mode from the structure and just change rpmh_regulator_vrm_set_mode_bypass() to set 0 if we're in bypass. Right now 100% of PMICs that support bypass use 0 as the bypass mode. If you ever have a future PMIC that uses a non-zero mode for bypass then we can always add this back. ...and if no future PMICs ever use a non-zero bypass mode then we don't need the complexity of having another field in this struct.

Quoted 8 lines
Quoted 4 lines

If you don't do this you might get arguments from some people saying that they don't like seeing inits of "= 0" in static structures (Linux conventions seem to like you to just assume that structs are zero-initted).

Upcoming PMICs use 2 for bypass mode instead of 0. That is why I left this in. I suppose that I can remove this member for now and add it back in when newer PMIC support is added.

I'm OK with keeping it as long as there is a real user coming up. IMHO with the #defines as suggested by Matthias this will look better anyway (it will be more obvious that this isn't a boolean, for instance).

Quoted 36 lines
Quoted 36 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+cleanup:
+       rpmh_release(rpmh_client);

Still no devm_rpmh_get_client()? If Lina is too busy spinning her patch series for other stuff, just add it to RPMH as a patch in your series. I believe it's just this (untested):

---

int devm_rpmh_release(struct device *dev, void *res) {
struct platform_device *pdev = to_platform_device(dev); rpmh_release(pdev);
}

int devm_rpmh_get_client(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev); void *dr;
int rc;

  dr = devres_alloc(devm_rpmh_release, 0, GFP_KERNEL);
  if (!dr)
    return -ENOMEM;

  rc = rpmh_get_client(pdev);
  if (!rc)
    devres_add(dev, dr);
  else
    devres_free(dr);

return rc;
}

---

Quoted 8 lines
Quoted 2 lines

Note that you'll get rid of the error handling in probe, the whole remove function, and the need to do platform_set_drvdata().

My understanding is that Lina is going to remove both rpmh_get_client() and rpmh_release(). In their place, rpmh functions will use the consumer device pointer as a handle and manage any necessary state internally [3]. I'll update this patch once she uploads a new series with this interface modification.

OK, sounds good. Information like this is nice to include somewhere in the cover letter or the patch description so it's more obvious that you wouldn't want this patch to land until that's sorted out.

-Doug

#19 lore raw
Quoted 9 lines · Doug Anderson
Quoted 8 lines · David Collins
Quoted 7 lines
Quoted 3 lines
+ * @drms_mode:                 Array of regulator framework modes which can
+ *                             be configured dynamically for this regulator
+ *                             via the set_load() callback.

Using the singular for something that is an array is confusing. Why not "drms_modes" or "drms_mode_arr"? In the past review you said 'Perhaps something along the lines of "drms_modes"'.

Quoted 6 lines
Quoted 4 lines

It seems awkward to me to use a plural for arrays as it leads to indexing like this: vreg->drms_modes[i]. "mode i" seems better than "modes i". However, I'm willing to change this to be drms_modes and drms_mode_max_uAs if that style is preferred.

I'd very much like a plural here.

Ok. I'll change this to be plural.

Quoted 65 lines
Quoted 36 lines
Quoted 25 lines
Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+               prop = "qcom,regulator-initial-voltage";
+               ret = of_property_read_u32(node, prop, &uV);
+               if (!ret) {
+                       range = &vreg->hw_data->voltage_range;
+                       selector = DIV_ROUND_UP(uV - range->min_uV,
+                                       range->uV_step) + range->min_sel;
+                       if (uV < range->min_uV || selector > range->max_sel) {
+                               dev_err(dev, "%s: %s=%u is invalid\n",
+                                       node->name, prop, uV);
+                               return -EINVAL;

drivers/regulator/qcom_rpmh-regulator.c line 492

Quoted 10 lines
drivers/regulator/qcom_rpmh-regulator.c new file +10 -0
+                       }
+
+                       vreg->voltage_selector = selector;
+
+                       cmd[cmd_count].addr
+                               = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE;
+                       cmd[cmd_count++].data
+                               = DIV_ROUND_UP(selector * range->uV_step
+                                               + range->min_uV, 1000);
+               }

Seems like you want an "else { vreg->voltage_selector = -EINVAL; }". Otherwise "get_voltage_sel" will return selector 0 before the first set, right?

Quoted 4 lines

Previously Mark said: "If the driver can't read values it should return an appropriate error code."
...and previously you said: "I'll try this out and see if the regulator framework complains during regulator registration."

I tested out what happens when vreg->voltage_selector = -EINVAL is set when qcom,regulator-initial-voltage is not present. This results in devm_regulator_register() failing and subsequently causing the qcom_rpmh-regulator probe to fail. The error happens in machine_constraints_voltage() [1].

Quoted 8 lines

This leaves two courses of action:
1. (current patch set) allow voltage_selector to stay 0 if uninitialized
2. Set voltage_selector = -EINVAL by default and specify in DT binding documentation that qcom,regulator-initial-voltage is required for VRM managed RPMh regulator resources which have regulator-min-microvolt and regulator-max-microvolt specified.

Are you ok with the DT implications of option #2?

You'd need to ask Mark if he's OK with it, but a option #3 is to add a patch to your series fix the regulator framework to try setting the voltage if _regulator_get_voltage() fails. Presumably in machine_constraints_voltage() you'd now do something like:

  int target_min, target_max;
  int current_uV = _regulator_get_voltage(rdev);
  if (current_uV < 0) {
    /* Maybe this regulator's hardware can't be read and needs to be initted */
    _regulator_do_set_voltage(
      rdev, rdev->constraints->min_uV, rdev->constraints->min_uV);
    current_uV = _regulator_get_voltage(rdev);
  }
  if (current_uV < 0) {
    rdev_err(rdev,
      "failed to get the current voltage(%d)\n",
      current_uV);
      return current_uV;
  }
Quoted 4 lines

If Mark doesn't like that then I guess I'd be OK w/ initting it to 0 but this needs to be documented _somewhere_ (unlike for bypass it's not obvious, so you need to find someplace to put it). I'd rather not hack the DT to deal with our software limitations.

I'm not opposed to your option #3 though it does seem a little hacky and tailored to the qcom_rpmh-regulator specific case. Note that I think it would be better to vote for min_uV to max_uV than min_uV to min_uV though.

Mark, what are your thoughts on the best way to handle this situation?

Quoted 17 lines
Quoted 17 lines
Quoted 16 lines

drivers/regulator/qcom_rpmh-regulator.c line 678

Quoted 8 lines
drivers/regulator/qcom_rpmh-regulator.c new file +8 -0
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int mode)
+{
+       static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+               [RPMH_REGULATOR_MODE_RET]  = -EINVAL,
+               [RPMH_REGULATOR_MODE_LPM]  = REGULATOR_MODE_IDLE,
+               [RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+               [RPMH_REGULATOR_MODE_HPM]  = REGULATOR_MODE_FAST,
+       };

You're sticking a negative value in an array of unsigned inits. Here and in other similar functions.

I know, I know. The function is defined to return an unsigned int. It's wrong. of_regulator.c clearly puts the return code in a signed int. First attempt at fixing this is at
<https://patchwork.kernel.org/patch/10346081/>.

Quoted 6 lines
Quoted 2 lines

I can change the error cases to use REGULATOR_MODE_INVALID which is added by this change still under review [2].

I haven't seen Mark NAK it (yet), so for lack of a better option I'd start using it in your patch and document in the commit message that it depends on my patch.

Your patch was accepted. I'll switch to using REGULATOR_MODE_INVALID.

Thanks,
David

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
maintainer
#20 lore raw
Quoted 2 lines · David Collins
Quoted 1 line · Doug Anderson

On Wed, Apr 18, 2018 at 4:30 PM, David Collins <[email protected]> wrote:

Please delete unneeded context from mails when replying. Doing this makes it much easier to find your reply in the message, helping ensure it won't be missed by people scrolling through the irrelevant quoted material.

Quoted 25 lines
Quoted 20 lines

You'd need to ask Mark if he's OK with it, but a option #3 is to add a patch to your series fix the regulator framework to try setting the voltage if _regulator_get_voltage() fails. Presumably in machine_constraints_voltage() you'd now do something like:

  int target_min, target_max;
  int current_uV = _regulator_get_voltage(rdev);
  if (current_uV < 0) {
    /* Maybe this regulator's hardware can't be read and needs to be initted */
    _regulator_do_set_voltage(
      rdev, rdev->constraints->min_uV, rdev->constraints->min_uV);
    current_uV = _regulator_get_voltage(rdev);
  }
  if (current_uV < 0) {
    rdev_err(rdev,
      "failed to get the current voltage(%d)\n",
      current_uV);
      return current_uV;
  }
Quoted 4 lines

If Mark doesn't like that then I guess I'd be OK w/ initting it to 0 but this needs to be documented _somewhere_ (unlike for bypass it's not obvious, so you need to find someplace to put it). I'd rather not hack the DT to deal with our software limitations.

Quoted 5 lines

I'm not opposed to your option #3 though it does seem a little hacky and tailored to the qcom_rpmh-regulator specific case. Note that I think it would be better to vote for min_uV to max_uV than min_uV to min_uV though.

Mark, what are your thoughts on the best way to handle this situation?

I think that's probably only OK if we have a specific error code for the
regulator being limited in this way otherwise our error handling for I/O
problems involves us trying to reconfigure supplies which seems like it
would be risky.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180424/09cd228d/attachment.sig>

#21 lore raw
Quoted 29 lines · Mark Brown
Quoted 28 lines
Quoted 20 lines

You'd need to ask Mark if he's OK with it, but a option #3 is to add a patch to your series fix the regulator framework to try setting the voltage if _regulator_get_voltage() fails. Presumably in machine_constraints_voltage() you'd now do something like:

  int target_min, target_max;
  int current_uV = _regulator_get_voltage(rdev);
  if (current_uV < 0) {
    /* Maybe this regulator's hardware can't be read and needs to be initted */
    _regulator_do_set_voltage(
      rdev, rdev->constraints->min_uV, rdev->constraints->min_uV);
    current_uV = _regulator_get_voltage(rdev);
  }
  if (current_uV < 0) {
    rdev_err(rdev,
      "failed to get the current voltage(%d)\n",
      current_uV);
      return current_uV;
  }
Quoted 4 lines

If Mark doesn't like that then I guess I'd be OK w/ initting it to 0 but this needs to be documented _somewhere_ (unlike for bypass it's not obvious, so you need to find someplace to put it). I'd rather not hack the DT to deal with our software limitations.

I'm not opposed to your option #3 though it does seem a little hacky and tailored to the qcom_rpmh-regulator specific case. Note that I think it would be better to vote for min_uV to max_uV than min_uV to min_uV though.

Quoted 6 lines
Quoted 1 line

Mark, what are your thoughts on the best way to handle this situation?

I think that's probably only OK if we have a specific error code for the regulator being limited in this way otherwise our error handling for I/O problems involves us trying to reconfigure supplies which seems like it would be risky.

Would you be ok with -EAGAIN being used for this purpose?

Thanks,
David

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
maintainer
#22 lore raw
Quoted 8 lines · David Collins
Quoted 4 lines · Mark Brown

I think that's probably only OK if we have a specific error code for the regulator being limited in this way otherwise our error handling for I/O problems involves us trying to reconfigure supplies which seems like it would be risky.

Would you be ok with -EAGAIN being used for this purpose?

Using -EAGAIN for "I can't ever read the configuration from this
regulator" doesn't seem right - it's not like any number of retries
will ever manage to read the value back.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180425/2373cdee/attachment.sig>

#23 lore raw
Quoted 10 lines
Quoted 6 lines
Quoted 4 lines

I think that's probably only OK if we have a specific error code for the regulator being limited in this way otherwise our error handling for I/O problems involves us trying to reconfigure supplies which seems like it would be risky.

Would you be ok with -EAGAIN being used for this purpose?

Using -EAGAIN for "I can't ever read the configuration from this regulator" doesn't seem right - it's not like any number of retries will ever manage to read the value back.

In this case, the _regulator_get_voltage() call can succeed, but only after a voltage is explicitly requested from the framework side. The intention here would then be to call _regulator_do_set_voltage() with the constraint min_uV to max_uV range. After that, subsequent _regulator_get_voltage() calls will be successful.

Here is the general idea:

drivers/regulator/core.c +0 -0
index 65f9b7c..e61983d 100644
@@ -910,6 +910,19 @@ static int machine_constraints_voltage(struct

regulator_dev *rdev,
rdev->constraints->min_uV && rdev->constraints->max_uV) {
int target_min, target_max;
int current_uV = _regulator_get_voltage(rdev);
+ if (current_uV == -EAGAIN) {
+ /*
+ * Regulator voltage cannot be read until after
+ * configuration; try setting constraint range.
+ */
+ rdev_info(rdev, "Setting %d-%duV\n",
+ rdev->constraints->min_uV,
+ rdev->constraints->max_uV);
+ _regulator_do_set_voltage(rdev,
+ rdev->constraints->min_uV,
+ rdev->constraints->max_uV);
+ current_uV = _regulator_get_voltage(rdev);
+ }
if (current_uV < 0) {
rdev_err(rdev,
"failed to get the current voltage(%d)\n",

Do you still have reservations about using -EAGAIN for this purpose? If so, which error code would you suggest using?

Thanks,
David

Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
maintainer
#24 lore raw
Quoted 6 lines · David Collins
Quoted 3 lines

Using -EAGAIN for "I can't ever read the configuration from this regulator" doesn't seem right - it's not like any number of retries will ever manage to read the value back.

In this case, the _regulator_get_voltage() call can succeed, but only after a voltage is explicitly requested from the framework side. The

...

Quoted 2 lines

Do you still have reservations about using -EAGAIN for this purpose? If so, which error code would you suggest using?

Yes, that's clearly a problem - -EAGAIN is more for situations where you
can just immediately retry like signal interruptions. If the caller
repetedly sits and tries to read the voltage it'll never succeed unless
something else comes along and sets something.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180502/c3175675/attachment.sig>