Merge tag 'regulator-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 14 Aug 2018 19:04:49 +0000 (12:04 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 14 Aug 2018 19:04:49 +0000 (12:04 -0700)
Pull regulator updates from Mark Brown:
 "The biggest set of changes in here is the addition of the Qualcomm
  RPMH driver. As well as the regualtor driver itself being quite large
  due to the usual involved Qualcomm regulator stuff there's also some
  code shared with the arm-soc tree, a bus driver required to
  communicate with the hardware that actually winds up being much larger
  than the regulator driver itself and a LLCC driver that was part of
  the same signed tag used with the arm-soc tree.

  Other than that it's a fairly standard and quiet release, highlights
  include:

   - Addition of device links from regulator consumers to their
     regulators, helping the core avoid dependency issues during
     suspend.

   - Support for the entertainingly innovative suspend implementation in
     the BD9571MWV.

   - Support for switch regulators on the PFUZE100, this required two
     goes due to backwards compatibility issues with old DTs that were
     discovered.

   - Support for Freescale PFUZE3001 and SocioNext UniPhier.

   - The aforementioned Qualcomm RPMH driver together with the driver
     changes required to support it"

* tag 'regulator-v4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (52 commits)
  regulator: add QCOM RPMh regulator driver
  regulator: dt-bindings: add QCOM RPMh regulator bindings
  regulator: samsung: Add SPDX license identifiers
  regulator: maxim: Add SPDX license identifiers
  regulator: bd71837: adobt MFD changes to regulator driver
  regulator: tps65217: Fix NULL pointer dereference on probe
  regulator: Add support for CPCAP regulators on Motorola Xoom devices.
  regulator: Add sw2_sw4 voltage table to cpcap regulator.
  regulator: bd9571mwv: Make symbol 'dev_attr_backup_mode' static
  regulator: pfuze100: add support to en-/disable switch regulators
  regulator: pfuze100: add optional disable switch-regulators binding
  soc: qcom: rmtfs-mem: fix memleak in probe error paths
  soc: qcom: llc-slice: Add missing MODULE_LICENSE()
  drivers: qcom: rpmh: fix unwanted error check for get_tcs_of_type()
  drivers: qcom: rpmh-rsc: fix the loop index check in get_req_from_tcs
  firmware: qcom: scm: add a dummy qcom_scm_assign_mem()
  drivers: qcom: rpmh-rsc: Check cmd_db_ready() to help children
  drivers: qcom: rpmh-rsc: allow active requests from wake TCS
  drivers: qcom: rpmh: add support for batch RPMH request
  drivers: qcom: rpmh: allow requests to be sent asynchronously
  ...

50 files changed:
Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator [new file with mode: 0644]
Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/cpcap-regulator.txt
Documentation/devicetree/bindings/regulator/pfuze100.txt
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt
Documentation/devicetree/bindings/regulator/uniphier-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt [new file with mode: 0644]
drivers/base/core.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/arizona-ldo1.c
drivers/regulator/bd71837-regulator.c
drivers/regulator/bd9571mwv-regulator.c
drivers/regulator/core.c
drivers/regulator/cpcap-regulator.c
drivers/regulator/max14577-regulator.c
drivers/regulator/max77686-regulator.c
drivers/regulator/max77693-regulator.c
drivers/regulator/max77802-regulator.c
drivers/regulator/max8997-regulator.c
drivers/regulator/max8998.c
drivers/regulator/pfuze100-regulator.c
drivers/regulator/qcom-rpmh-regulator.c [new file with mode: 0644]
drivers/regulator/qcom_spmi-regulator.c
drivers/regulator/s2mpa01.c
drivers/regulator/s2mps11.c
drivers/regulator/s5m8767.c
drivers/regulator/tps65217-regulator.c
drivers/regulator/uniphier-regulator.c [new file with mode: 0644]
drivers/soc/qcom/Kconfig
drivers/soc/qcom/Makefile
drivers/soc/qcom/llcc-sdm845.c [new file with mode: 0644]
drivers/soc/qcom/llcc-slice.c [new file with mode: 0644]
drivers/soc/qcom/rmtfs_mem.c
drivers/soc/qcom/rpmh-internal.h [new file with mode: 0644]
drivers/soc/qcom/rpmh-rsc.c [new file with mode: 0644]
drivers/soc/qcom/rpmh.c [new file with mode: 0644]
drivers/soc/qcom/smem.c
drivers/soc/qcom/trace-rpmh.h [new file with mode: 0644]
include/dt-bindings/regulator/maxim,max77802.h
include/dt-bindings/regulator/qcom,rpmh-regulator.h [new file with mode: 0644]
include/dt-bindings/soc/qcom,rpmh-rsc.h [new file with mode: 0644]
include/linux/device.h
include/linux/qcom_scm.h
include/linux/regulator/driver.h
include/linux/regulator/pfuze100.h
include/linux/soc/qcom/llcc-qcom.h [new file with mode: 0644]
include/soc/qcom/rpmh.h [new file with mode: 0644]
include/soc/qcom/tcs.h [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator b/Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator
new file mode 100644 (file)
index 0000000..4d63a79
--- /dev/null
@@ -0,0 +1,27 @@
+What:          /sys/bus/i2c/devices/.../bd9571mwv-regulator.*.auto/backup_mode
+Date:          Jul 2018
+KernelVersion: 4.19
+Contact:       Geert Uytterhoeven <geert+renesas@glider.be>
+Description:   Read/write the current state of DDR Backup Mode, which controls
+               if DDR power rails will be kept powered during system suspend.
+               ("on"/"1" = enabled, "off"/"0" = disabled).
+               Two types of power switches (or control signals) can be used:
+                 A. With a momentary power switch (or pulse signal), DDR
+                    Backup Mode is enabled by default when available, as the
+                    PMIC will be configured only during system suspend.
+                 B. With a toggle power switch (or level signal), the
+                    following steps must be followed exactly:
+                      1. Configure PMIC for backup mode, to change the role of
+                         the accessory power switch from a power switch to a
+                         wake-up switch,
+                      2. Switch accessory power switch off, to prepare for
+                         system suspend, which is a manual step not controlled
+                         by software,
+                      3. Suspend system,
+                      4. Switch accessory power switch on, to resume the
+                         system.
+                    DDR Backup Mode must be explicitly enabled by the user,
+                    to invoke step 1.
+               See also Documentation/devicetree/bindings/mfd/bd9571mwv.txt.
+Users:         User space applications for embedded boards equipped with a
+               BD9571MWV PMIC.
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
new file mode 100644 (file)
index 0000000..5e85749
--- /dev/null
@@ -0,0 +1,26 @@
+== Introduction==
+
+LLCC (Last Level Cache Controller) provides last level of cache memory in SOC,
+that can be shared by multiple clients. Clients here are different cores in the
+SOC, the idea is to minimize the local caches at the clients and migrate to
+common pool of memory. Cache memory is divided into partitions called slices
+which are assigned to clients. Clients can query the slice details, activate
+and deactivate them.
+
+Properties:
+- compatible:
+       Usage: required
+       Value type: <string>
+       Definition: must be "qcom,sdm845-llcc"
+
+- reg:
+       Usage: required
+       Value Type: <prop-encoded-array>
+       Definition: Start address and the the size of the register region.
+
+Example:
+
+       cache-controller@1100000 {
+               compatible = "qcom,sdm845-llcc";
+               reg = <0x1100000 0x250000>;
+       };
index 675f4437ce92a7e3d78a90d58fbfe80cbdb6e8b1..36f5e2f5cc0fb4f0866feb9ed499d20ff1a1de31 100644 (file)
@@ -5,6 +5,7 @@ Requires node properties:
 - "compatible" value one of:
     "motorola,cpcap-regulator"
     "motorola,mapphone-cpcap-regulator"
+    "motorola,xoom-cpcap-regulator"
 
 Required regulator properties:
 - "regulator-name"
index f0ada3b14d70d05e22f6da2be3e3b297c439412a..c7610718adfff69fd8e98068721534c4cdf7f7c7 100644 (file)
@@ -1,9 +1,18 @@
 PFUZE100 family of regulators
 
 Required properties:
-- compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000"
+- compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000", "fsl,pfuze3001"
 - reg: I2C slave address
 
+Optional properties:
+- fsl,pfuze-support-disable-sw: Boolean, if present disable all unused switch
+  regulators to save power consumption. Attention, ensure that all important
+  regulators (e.g. DDR ref, DDR supply) has set the "regulator-always-on"
+  property. If not present, the switched regualtors are always on and can't be
+  disabled. This binding is a workaround to keep backward compatibility with
+  old dtb's which rely on the fact that the switched regulators are always on
+  and don't mark them explicit as "regulator-always-on".
+
 Required child node:
 - regulators: This is the list of child nodes that specify the regulator
   initialization data for defined regulators. Please refer to below doc
@@ -16,6 +25,8 @@ Required child node:
   sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6,coin
   --PFUZE3000
   sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4
+  --PFUZE3001
+  sw1,sw2,sw3,vsnvs,vldo1,vldo2,vccsd,v33,vldo3,vldo4
 
 Each regulator is defined using the standard binding for regulators.
 
@@ -303,3 +314,76 @@ Example 3: PFUZE3000
                        };
                };
        };
+
+Example 4: PFUZE 3001
+
+       pfuze3001: pmic@8 {
+               compatible = "fsl,pfuze3001";
+               reg = <0x08>;
+
+               regulators {
+                       sw1_reg: sw1 {
+                               regulator-min-microvolt = <700000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
+
+                       sw2_reg: sw2 {
+                               regulator-min-microvolt = <1500000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
+
+                       sw3_reg: sw3 {
+                               regulator-min-microvolt = <900000>;
+                               regulator-max-microvolt = <1650000>;
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
+
+                       snvs_reg: vsnvs {
+                               regulator-min-microvolt = <1000000>;
+                               regulator-max-microvolt = <3000000>;
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
+
+                       vgen1_reg: vldo1 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+
+                       vgen2_reg: vldo2 {
+                               regulator-min-microvolt = <800000>;
+                               regulator-max-microvolt = <1550000>;
+                               regulator-always-on;
+                       };
+
+                       vgen3_reg: vccsd {
+                               regulator-min-microvolt = <2850000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+
+                       vgen4_reg: v33 {
+                               regulator-min-microvolt = <2850000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+
+                       vgen5_reg: vldo3 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+
+                       vgen6_reg: vldo4 {
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+               };
+       };
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
new file mode 100644 (file)
index 0000000..7ef2dbe
--- /dev/null
@@ -0,0 +1,160 @@
+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 three
+parameters for a given regulator: enable state, output voltage, and operating
+mode.  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
+       Usage:      optional (PM8998 and PM8005 only)
+       Value type: <phandle>
+       Definition: phandle of the parent supply regulator of one or more of the
+                   regulators for this PMIC.
+
+- 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
+- vin-lvs-1-2-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
+
+===============================
+Second Level Nodes - Regulators
+===============================
+
+- 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.  regulator-initial-mode and regulator-allowed-modes may be
+specified for VRM regulators using mode values from
+include/dt-bindings/regulator/qcom,rpmh-regulator.h.  regulator-allow-bypass
+may be specified for BOB type regulators managed via VRM.
+regulator-allow-set-load may be specified for LDO type regulators managed via
+VRM.
+
+========
+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>;
+               };
+
+               pm8998_s5: smps5 {
+                       regulator-min-microvolt = <1904000>;
+                       regulator-max-microvolt = <2040000>;
+               };
+
+               ldo7 {
+                       regulator-min-microvolt = <1800000>;
+                       regulator-max-microvolt = <1800000>;
+                       regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
+                       regulator-allowed-modes =
+                               <RPMH_REGULATOR_MODE_LPM
+                                RPMH_REGULATOR_MODE_HPM>;
+                       regulator-allow-set-load;
+               };
+
+               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>;
+                       regulator-allowed-modes =
+                               <RPMH_REGULATOR_MODE_AUTO
+                                RPMH_REGULATOR_MODE_HPM>;
+                       regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>;
+               };
+       };
+};
index 4edf3137d9f731c35fb47639f1fe8357560d907a..76ead07072b130a54c84a80e88c1f965c7cc6de0 100644 (file)
@@ -1,13 +1,5 @@
 ROHM BD71837 Power Management Integrated Circuit (PMIC) regulator bindings
 
-BD71837MWV is a programmable Power Management
-IC (PMIC) for powering single-core, dual-core, and
-quad-core SoC’s such as NXP-i.MX 8M. It is optimized
-for low BOM cost and compact solution footprint. It
-integrates 8 Buck regulators and 7 LDO’s to provide all
-the power rails required by the SoC and the commonly
-used peripherals.
-
 Required properties:
  - regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7"
 
diff --git a/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt b/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt
new file mode 100644 (file)
index 0000000..c9919f4
--- /dev/null
@@ -0,0 +1,57 @@
+Socionext UniPhier Regulator Controller
+
+This describes the devicetree bindings for regulator controller implemented
+on Socionext UniPhier SoCs.
+
+USB3 Controller
+---------------
+
+This regulator controls VBUS and belongs to USB3 glue layer. Before using
+the regulator, it is necessary to control the clocks and resets to enable
+this layer. These clocks and resets should be described in each property.
+
+Required properties:
+- compatible: Should be
+    "socionext,uniphier-pro4-usb3-regulator" - for Pro4 SoC
+    "socionext,uniphier-pxs2-usb3-regulator" - for PXs2 SoC
+    "socionext,uniphier-ld20-usb3-regulator" - for LD20 SoC
+    "socionext,uniphier-pxs3-usb3-regulator" - for PXs3 SoC
+- reg: Specifies offset and length of the register set for the device.
+- clocks: A list of phandles to the clock gate for USB3 glue layer.
+       According to the clock-names, appropriate clocks are required.
+- clock-names: Should contain
+    "gio", "link" - for Pro4 SoC
+    "link"        - for others
+- resets: A list of phandles to the reset control for USB3 glue layer.
+       According to the reset-names, appropriate resets are required.
+- reset-names: Should contain
+    "gio", "link" - for Pro4 SoC
+    "link"        - for others
+
+See Documentation/devicetree/bindings/regulator/regulator.txt
+for more details about the regulator properties.
+
+Example:
+
+       usb-glue@65b00000 {
+               compatible = "socionext,uniphier-ld20-dwc3-glue",
+                            "simple-mfd";
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges = <0 0x65b00000 0x400>;
+
+               usb_vbus0: regulators@100 {
+                       compatible = "socionext,uniphier-ld20-usb3-regulator";
+                       reg = <0x100 0x10>;
+                       clock-names = "link";
+                       clocks = <&sys_clk 14>;
+                       reset-names = "link";
+                       resets = <&sys_rst 14>;
+               };
+
+               phy {
+                       ...
+                       phy-supply = <&usb_vbus0>;
+               };
+               ...
+       };
diff --git a/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt b/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
new file mode 100644 (file)
index 0000000..9b86d1e
--- /dev/null
@@ -0,0 +1,137 @@
+RPMH RSC:
+------------
+
+Resource Power Manager Hardened (RPMH) is the mechanism for communicating with
+the hardened resource accelerators on Qualcomm SoCs. Requests to the resources
+can be written to the Trigger Command Set (TCS)  registers and using a (addr,
+val) pair and triggered. Messages in the TCS are then sent in sequence over an
+internal bus.
+
+The hardware block (Direct Resource Voter or DRV) is a part of the h/w entity
+(Resource State Coordinator a.k.a RSC) that can handle multiple sleep and
+active/wake resource requests. Multiple such DRVs can exist in a SoC and can
+be written to from Linux. The structure of each DRV follows the same template
+with a few variations that are captured by the properties here.
+
+A TCS may be triggered from Linux or triggered by the F/W after all the CPUs
+have powered off to facilitate idle power saving. TCS could be classified as -
+
+       ACTIVE  /* Triggered by Linux */
+       SLEEP   /* Triggered by F/W */
+       WAKE    /* Triggered by F/W */
+       CONTROL /* Triggered by F/W */
+
+The order in which they are described in the DT, should match the hardware
+configuration.
+
+Requests can be made for the state of a resource, when the subsystem is active
+or idle. When all subsystems like Modem, GPU, CPU are idle, the resource state
+will be an aggregate of the sleep votes from each of those subsystems. Clients
+may request a sleep value for their shared resources in addition to the active
+mode requests.
+
+Properties:
+
+- compatible:
+       Usage: required
+       Value type: <string>
+       Definition: Should be "qcom,rpmh-rsc".
+
+- reg:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: The first register specifies the base address of the
+                   DRV(s). The number of DRVs in the dependent on the RSC.
+                   The tcs-offset specifies the start address of the
+                   TCS in the DRVs.
+
+- reg-names:
+       Usage: required
+       Value type: <string>
+       Definition: Maps the register specified in the reg property. Must be
+                   "drv-0", "drv-1", "drv-2" etc and "tcs-offset". The
+
+- interrupts:
+       Usage: required
+       Value type: <prop-encoded-interrupt>
+       Definition: The interrupt that trips when a message complete/response
+                  is received for this DRV from the accelerators.
+
+- qcom,drv-id:
+       Usage: required
+       Value type: <u32>
+       Definition: The id of the DRV in the RSC block that will be used by
+                   this controller.
+
+- qcom,tcs-config:
+       Usage: required
+       Value type: <prop-encoded-array>
+       Definition: The tuple defining the configuration of TCS.
+                   Must have 2 cells which describe each TCS type.
+                   <type number_of_tcs>.
+                   The order of the TCS must match the hardware
+                   configuration.
+       - Cell #1 (TCS Type): TCS types to be specified -
+                   ACTIVE_TCS
+                   SLEEP_TCS
+                   WAKE_TCS
+                   CONTROL_TCS
+       - Cell #2 (Number of TCS): <u32>
+
+- label:
+       Usage: optional
+       Value type: <string>
+       Definition: Name for the RSC. The name would be used in trace logs.
+
+Drivers that want to use the RSC to communicate with RPMH must specify their
+bindings as child nodes of the RSC controllers they wish to communicate with.
+
+Example 1:
+
+For a TCS whose RSC base address is is 0x179C0000 and is at a DRV id of 2, the
+register offsets for DRV2 start at 0D00, the register calculations are like
+this -
+DRV0: 0x179C0000
+DRV2: 0x179C0000 + 0x10000 = 0x179D0000
+DRV2: 0x179C0000 + 0x10000 * 2 = 0x179E0000
+TCS-OFFSET: 0xD00
+
+       apps_rsc: rsc@179c0000 {
+               label = "apps_rsc";
+               compatible = "qcom,rpmh-rsc";
+               reg = <0x179c0000 0x10000>,
+                     <0x179d0000 0x10000>,
+                     <0x179e0000 0x10000>;
+               reg-names = "drv-0", "drv-1", "drv-2";
+               interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
+               qcom,tcs-offset = <0xd00>;
+               qcom,drv-id = <2>;
+               qcom,tcs-config = <ACTIVE_TCS  2>,
+                                 <SLEEP_TCS   3>,
+                                 <WAKE_TCS    3>,
+                                 <CONTROL_TCS 1>;
+       };
+
+Example 2:
+
+For a TCS whose RSC base address is 0xAF20000 and is at DRV id of 0, the
+register offsets for DRV0 start at 01C00, the register calculations are like
+this -
+DRV0: 0xAF20000
+TCS-OFFSET: 0x1C00
+
+       disp_rsc: rsc@af20000 {
+               label = "disp_rsc";
+               compatible = "qcom,rpmh-rsc";
+               reg = <0xaf20000 0x10000>;
+               reg-names = "drv-0";
+               interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+               qcom,tcs-offset = <0x1c00>;
+               qcom,drv-id = <0>;
+               qcom,tcs-config = <ACTIVE_TCS  0>,
+                                 <SLEEP_TCS   1>,
+                                 <WAKE_TCS    1>,
+                                 <CONTROL_TCS 0>;
+       };
index df3e1a44707acc74010cf5ce6fab815c4f744896..2ab316d856510958b58501203bb7fc61c2f49a46 100644 (file)
@@ -372,6 +372,36 @@ void device_link_del(struct device_link *link)
 }
 EXPORT_SYMBOL_GPL(device_link_del);
 
+/**
+ * device_link_remove - remove a link between two devices.
+ * @consumer: Consumer end of the link.
+ * @supplier: Supplier end of the link.
+ *
+ * The caller must ensure proper synchronization of this function with runtime
+ * PM.
+ */
+void device_link_remove(void *consumer, struct device *supplier)
+{
+       struct device_link *link;
+
+       if (WARN_ON(consumer == supplier))
+               return;
+
+       device_links_write_lock();
+       device_pm_lock();
+
+       list_for_each_entry(link, &supplier->links.consumers, s_node) {
+               if (link->consumer == consumer) {
+                       kref_put(&link->kref, __device_link_del);
+                       break;
+               }
+       }
+
+       device_pm_unlock();
+       device_links_write_unlock();
+}
+EXPORT_SYMBOL_GPL(device_link_remove);
+
 static void device_links_missing_supplier(struct device *dev)
 {
        struct device_link *link;
index 5dbccf5f3037a84b2aab52d1305e7612a33db0a4..329cdd33ed624b8dc06ad1a72dee73009e630095 100644 (file)
@@ -180,9 +180,9 @@ config REGULATOR_BCM590XX
          BCM590xx PMUs. This will enable support for the software
          controllable LDO/Switching regulators.
 
-config REGULATOR_BD71837
+config REGULATOR_BD718XX
        tristate "ROHM BD71837 Power Regulator"
-       depends on MFD_BD71837
+       depends on MFD_ROHM_BD718XX
        help
          This driver supports voltage regulators on ROHM BD71837 PMIC.
          This will enable support for the software controllable buck
@@ -633,12 +633,12 @@ config REGULATOR_PCF50633
         on PCF50633
 
 config REGULATOR_PFUZE100
-       tristate "Freescale PFUZE100/200/3000 regulator driver"
+       tristate "Freescale PFUZE100/200/3000/3001 regulator driver"
        depends on I2C
        select REGMAP_I2C
        help
          Say y here to support the regulators found on the Freescale
-         PFUZE100/200/3000 PMIC.
+         PFUZE100/200/3000/3001 PMIC.
 
 config REGULATOR_PV88060
        tristate "Powerventure Semiconductor PV88060 regulator"
@@ -682,6 +682,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 || 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
@@ -950,6 +959,14 @@ config REGULATOR_TWL4030
          This driver supports the voltage regulators provided by
          this family of companion chips.
 
+config REGULATOR_UNIPHIER
+       tristate "UniPhier regulator driver"
+       depends on ARCH_UNIPHIER || COMPILE_TEST
+       depends on OF && MFD_SYSCON
+       default ARCH_UNIPHIER
+       help
+         Support for regulators implemented on Socionext UniPhier SoCs.
+
 config REGULATOR_VCTRL
        tristate "Voltage controlled regulators"
        depends on OF
index bd818ceb7c72ed6ae38f64e27b3a45fd0ca6566c..801d9a34a2037c5b7c7bb8e8c93a62ad1a9aebf1 100644 (file)
@@ -27,7 +27,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
 obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
 obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
 obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
-obj-$(CONFIG_REGULATOR_BD71837) += bd71837-regulator.o
+obj-$(CONFIG_REGULATOR_BD718XX) += bd71837-regulator.o
 obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
 obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
@@ -78,6 +78,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
@@ -118,6 +119,7 @@ obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
 obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
+obj-$(CONFIG_REGULATOR_UNIPHIER) += uniphier-regulator.o
 obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
 obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
index f6d6a4ad9e8a28c8561b2ca807ccf2b11ed9aad3..e976d073f28d6ffa1562d0a7e39f6b979b926a4c 100644 (file)
@@ -36,6 +36,8 @@ struct arizona_ldo1 {
 
        struct regulator_consumer_supply supply;
        struct regulator_init_data init_data;
+
+       struct gpio_desc *ena_gpiod;
 };
 
 static int arizona_ldo1_hc_list_voltage(struct regulator_dev *rdev,
@@ -253,12 +255,17 @@ static int arizona_ldo1_common_init(struct platform_device *pdev,
                }
        }
 
-       /* We assume that high output = regulator off */
-       config.ena_gpiod = devm_gpiod_get_optional(&pdev->dev, "wlf,ldoena",
-                                                  GPIOD_OUT_HIGH);
+       /* We assume that high output = regulator off
+        * Don't use devm, since we need to get against the parent device
+        * so clean up would happen at the wrong time
+        */
+       config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena",
+                                             GPIOD_OUT_LOW);
        if (IS_ERR(config.ena_gpiod))
                return PTR_ERR(config.ena_gpiod);
 
+       ldo1->ena_gpiod = config.ena_gpiod;
+
        if (pdata->init_data)
                config.init_data = pdata->init_data;
        else
@@ -276,6 +283,9 @@ static int arizona_ldo1_common_init(struct platform_device *pdev,
        of_node_put(config.of_node);
 
        if (IS_ERR(ldo1->regulator)) {
+               if (config.ena_gpiod)
+                       gpiod_put(config.ena_gpiod);
+
                ret = PTR_ERR(ldo1->regulator);
                dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
                        ret);
@@ -334,8 +344,19 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
        return ret;
 }
 
+static int arizona_ldo1_remove(struct platform_device *pdev)
+{
+       struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);
+
+       if (ldo1->ena_gpiod)
+               gpiod_put(ldo1->ena_gpiod);
+
+       return 0;
+}
+
 static struct platform_driver arizona_ldo1_driver = {
        .probe = arizona_ldo1_probe,
+       .remove = arizona_ldo1_remove,
        .driver         = {
                .name   = "arizona-ldo1",
        },
index 6eae4d0432a23fb41f11649f1aea2867c6d34f46..0f8ac8dec3e16a21e066e9f76318d14ec6c9d78f 100644 (file)
@@ -2,19 +2,18 @@
 // Copyright (C) 2018 ROHM Semiconductors
 // bd71837-regulator.c ROHM BD71837MWV regulator driver
 
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
+#include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/gpio.h>
 #include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/rohm-bd718x7.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/mfd/bd71837.h>
 #include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
 
 struct bd71837_pmic {
        struct regulator_desc descs[BD71837_REGULATOR_CNT];
@@ -39,7 +38,7 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev,
        int id = rdev->desc->id;
        unsigned int ramp_value = BUCK_RAMPRATE_10P00MV;
 
-       dev_dbg(&(pmic->pdev->dev), "Buck[%d] Set Ramp = %d\n", id + 1,
+       dev_dbg(&pmic->pdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1,
                ramp_delay);
        switch (ramp_delay) {
        case 1 ... 1250:
@@ -73,14 +72,10 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev,
 static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev,
                                                    unsigned int sel)
 {
-       int ret;
-
-       ret = regulator_is_enabled_regmap(rdev);
-       if (!ret)
-               ret = regulator_set_voltage_sel_regmap(rdev, sel);
-       else if (ret == 1)
-               ret = -EBUSY;
-       return ret;
+       if (regulator_is_enabled_regmap(rdev))
+               return -EBUSY;
+
+       return regulator_set_voltage_sel_regmap(rdev, sel);
 }
 
 static struct regulator_ops bd71837_ldo_regulator_ops = {
@@ -195,7 +190,7 @@ static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = {
  * LDO2
  * 0.8 or 0.9V
  */
-const unsigned int ldo_2_volts[] = {
+static const unsigned int ldo_2_volts[] = {
        900000, 800000
 };
 
@@ -495,7 +490,6 @@ struct reg_init {
 static int bd71837_probe(struct platform_device *pdev)
 {
        struct bd71837_pmic *pmic;
-       struct bd71837_board *pdata;
        struct regulator_config config = { 0 };
        struct reg_init pmic_regulator_inits[] = {
                {
@@ -548,8 +542,7 @@ static int bd71837_probe(struct platform_device *pdev)
 
        int i, err;
 
-       pmic = devm_kzalloc(&pdev->dev, sizeof(struct bd71837_pmic),
-                           GFP_KERNEL);
+       pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
        if (!pmic)
                return -ENOMEM;
 
@@ -564,7 +557,6 @@ static int bd71837_probe(struct platform_device *pdev)
                goto err;
        }
        platform_set_drvdata(pdev, pmic);
-       pdata = dev_get_platdata(pmic->mfd->dev);
 
        /* Register LOCK release */
        err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK,
@@ -573,8 +565,8 @@ static int bd71837_probe(struct platform_device *pdev)
                dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err);
                goto err;
        } else {
-               dev_dbg(&pmic->pdev->dev, "%s: Unlocked lock register 0x%x\n",
-                       __func__, BD71837_REG_REGLOCK);
+               dev_dbg(&pmic->pdev->dev, "Unlocked lock register 0x%x\n",
+                       BD71837_REG_REGLOCK);
        }
 
        for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) {
@@ -584,9 +576,6 @@ static int bd71837_probe(struct platform_device *pdev)
 
                desc = &pmic->descs[i];
 
-               if (pdata)
-                       config.init_data = pdata->init_data[i];
-
                config.dev = pdev->dev.parent;
                config.driver_data = pmic;
                config.regmap = pmic->mfd->regmap;
@@ -619,8 +608,6 @@ static int bd71837_probe(struct platform_device *pdev)
                pmic->rdev[i] = rdev;
        }
 
-       return 0;
-
 err:
        return err;
 }
@@ -628,7 +615,6 @@ err:
 static struct platform_driver bd71837_regulator = {
        .driver = {
                .name = "bd71837-pmic",
-               .owner = THIS_MODULE,
        },
        .probe = bd71837_probe,
 };
index be574eb444ebda97be0be91db5b1c465cee424aa..274c5ed7cd737ecd3e4eae78458b1879af44747a 100644 (file)
@@ -30,6 +30,7 @@ struct bd9571mwv_reg {
        /* DDR Backup Power */
        u8 bkup_mode_cnt_keepon;        /* from "rohm,ddr-backup-power" */
        u8 bkup_mode_cnt_saved;
+       bool bkup_mode_enabled;
 
        /* Power switch type */
        bool rstbmode_level;
@@ -171,13 +172,60 @@ static int bd9571mwv_bkup_mode_write(struct bd9571mwv *bd, unsigned int mode)
        return 0;
 }
 
+static ssize_t backup_mode_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", bdreg->bkup_mode_enabled ? "on" : "off");
+}
+
+static ssize_t backup_mode_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
+       unsigned int mode;
+       int ret;
+
+       if (!count)
+               return 0;
+
+       ret = kstrtobool(buf, &bdreg->bkup_mode_enabled);
+       if (ret)
+               return ret;
+
+       if (!bdreg->rstbmode_level)
+               return count;
+
+       /*
+        * Configure DDR Backup Mode, to change the role of the accessory power
+        * switch from a power switch to a wake-up switch, or vice versa
+        */
+       ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode);
+       if (ret)
+               return ret;
+
+       mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK;
+       if (bdreg->bkup_mode_enabled)
+               mode |= bdreg->bkup_mode_cnt_keepon;
+
+       ret = bd9571mwv_bkup_mode_write(bdreg->bd, mode);
+       if (ret)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR_RW(backup_mode);
+
 static int bd9571mwv_suspend(struct device *dev)
 {
        struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
        unsigned int mode;
        int ret;
 
-       if (!device_may_wakeup(dev))
+       if (!bdreg->bkup_mode_enabled)
                return 0;
 
        /* Save DDR Backup Mode */
@@ -204,7 +252,7 @@ static int bd9571mwv_resume(struct device *dev)
 {
        struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
 
-       if (!device_may_wakeup(dev))
+       if (!bdreg->bkup_mode_enabled)
                return 0;
 
        /* Restore DDR Backup Mode */
@@ -215,9 +263,15 @@ static const struct dev_pm_ops bd9571mwv_pm  = {
        SET_SYSTEM_SLEEP_PM_OPS(bd9571mwv_suspend, bd9571mwv_resume)
 };
 
+static int bd9571mwv_regulator_remove(struct platform_device *pdev)
+{
+       device_remove_file(&pdev->dev, &dev_attr_backup_mode);
+       return 0;
+}
 #define DEV_PM_OPS     &bd9571mwv_pm
 #else
 #define DEV_PM_OPS     NULL
+#define bd9571mwv_regulator_remove     NULL
 #endif /* CONFIG_PM_SLEEP */
 
 static int bd9571mwv_regulator_probe(struct platform_device *pdev)
@@ -270,14 +324,21 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+#ifdef CONFIG_PM_SLEEP
        if (bdreg->bkup_mode_cnt_keepon) {
-               device_set_wakeup_capable(&pdev->dev, true);
+               int ret;
+
                /*
-                * Wakeup is enabled by default in pulse mode, but needs
+                * Backup mode is enabled by default in pulse mode, but needs
                 * explicit user setup in level mode.
                 */
-               device_set_wakeup_enable(&pdev->dev, bdreg->rstbmode_pulse);
+               bdreg->bkup_mode_enabled = bdreg->rstbmode_pulse;
+
+               ret = device_create_file(&pdev->dev, &dev_attr_backup_mode);
+               if (ret)
+                       return ret;
        }
+#endif /* CONFIG_PM_SLEEP */
 
        return 0;
 }
@@ -294,6 +355,7 @@ static struct platform_driver bd9571mwv_regulator_driver = {
                .pm = DEV_PM_OPS,
        },
        .probe = bd9571mwv_regulator_probe,
+       .remove = bd9571mwv_regulator_remove,
        .id_table = bd9571mwv_regulator_id_table,
 };
 module_platform_driver(bd9571mwv_regulator_driver);
index 6ed568b96c0ec1edf77cc2b4059c36d51b58cb93..bb1324f93143f66e609fea2602329230f70ed4ce 100644 (file)
@@ -1740,6 +1740,8 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
                        rdev->use_count = 0;
        }
 
+       device_link_add(dev, &rdev->dev, DL_FLAG_STATELESS);
+
        return regulator;
 }
 
@@ -1829,9 +1831,21 @@ static void _regulator_put(struct regulator *regulator)
 
        debugfs_remove_recursive(regulator->debugfs);
 
-       /* remove any sysfs entries */
-       if (regulator->dev)
+       if (regulator->dev) {
+               int count = 0;
+               struct regulator *r;
+
+               list_for_each_entry(r, &rdev->consumer_list, list)
+                       if (r->dev == regulator->dev)
+                               count++;
+
+               if (count == 1)
+                       device_link_remove(regulator->dev, &rdev->dev);
+
+               /* remove any sysfs entries */
                sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
+       }
+
        regulator_lock(rdev);
        list_del(&regulator->list);
 
@@ -4441,7 +4455,7 @@ void regulator_unregister(struct regulator_dev *rdev)
 EXPORT_SYMBOL_GPL(regulator_unregister);
 
 #ifdef CONFIG_SUSPEND
-static int _regulator_suspend_late(struct device *dev, void *data)
+static int _regulator_suspend(struct device *dev, void *data)
 {
        struct regulator_dev *rdev = dev_to_rdev(dev);
        suspend_state_t *state = data;
@@ -4455,20 +4469,20 @@ static int _regulator_suspend_late(struct device *dev, void *data)
 }
 
 /**
- * regulator_suspend_late - prepare regulators for system wide suspend
+ * regulator_suspend - prepare regulators for system wide suspend
  * @state: system suspend state
  *
  * Configure each regulator with it's suspend operating parameters for state.
  */
-static int regulator_suspend_late(struct device *dev)
+static int regulator_suspend(struct device *dev)
 {
        suspend_state_t state = pm_suspend_target_state;
 
        return class_for_each_device(&regulator_class, NULL, &state,
-                                    _regulator_suspend_late);
+                                    _regulator_suspend);
 }
 
-static int _regulator_resume_early(struct device *dev, void *data)
+static int _regulator_resume(struct device *dev, void *data)
 {
        int ret = 0;
        struct regulator_dev *rdev = dev_to_rdev(dev);
@@ -4481,35 +4495,35 @@ static int _regulator_resume_early(struct device *dev, void *data)
 
        regulator_lock(rdev);
 
-       if (rdev->desc->ops->resume_early &&
+       if (rdev->desc->ops->resume &&
            (rstate->enabled == ENABLE_IN_SUSPEND ||
             rstate->enabled == DISABLE_IN_SUSPEND))
-               ret = rdev->desc->ops->resume_early(rdev);
+               ret = rdev->desc->ops->resume(rdev);
 
        regulator_unlock(rdev);
 
        return ret;
 }
 
-static int regulator_resume_early(struct device *dev)
+static int regulator_resume(struct device *dev)
 {
        suspend_state_t state = pm_suspend_target_state;
 
        return class_for_each_device(&regulator_class, NULL, &state,
-                                    _regulator_resume_early);
+                                    _regulator_resume);
 }
 
 #else /* !CONFIG_SUSPEND */
 
-#define regulator_suspend_late NULL
-#define regulator_resume_early NULL
+#define regulator_suspend      NULL
+#define regulator_resume       NULL
 
 #endif /* !CONFIG_SUSPEND */
 
 #ifdef CONFIG_PM
 static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
-       .suspend_late   = regulator_suspend_late,
-       .resume_early   = regulator_resume_early,
+       .suspend        = regulator_suspend,
+       .resume         = regulator_resume,
 };
 #endif
 
index bd910fe123d98e65794990a14d7c26986369b6cb..2131457937b7553221a6e41d7e82da2ddb6b33e1 100644 (file)
@@ -271,6 +271,29 @@ static struct regulator_ops cpcap_regulator_ops = {
 };
 
 static const unsigned int unknown_val_tbl[] = { 0, };
+static const unsigned int sw2_sw4_val_tbl[] = { 612500, 625000, 637500,
+                                               650000, 662500, 675000,
+                                               687500, 700000, 712500,
+                                               725000, 737500, 750000,
+                                               762500, 775000, 787500,
+                                               800000, 812500, 825000,
+                                               837500, 850000, 862500,
+                                               875000, 887500, 900000,
+                                               912500, 925000, 937500,
+                                               950000, 962500, 975000,
+                                               987500, 1000000, 1012500,
+                                               1025000, 1037500, 1050000,
+                                               1062500, 1075000, 1087500,
+                                               1100000, 1112500, 1125000,
+                                               1137500, 1150000, 1162500,
+                                               1175000, 1187500, 1200000,
+                                               1212500, 1225000, 1237500,
+                                               1250000, 1262500, 1275000,
+                                               1287500, 1300000, 1312500,
+                                               1325000, 1337500, 1350000,
+                                               1362500, 1375000, 1387500,
+                                               1400000, 1412500, 1425000,
+                                               1437500, 1450000, 1462500, };
 static const unsigned int sw5_val_tbl[] = { 0, 5050000, };
 static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000,
                                             2900000, };
@@ -389,6 +412,82 @@ static struct cpcap_regulator omap4_regulators[] = {
        { /* sentinel */ },
 };
 
+static struct cpcap_regulator xoom_regulators[] = {
+       CPCAP_REG(SW1, CPCAP_REG_S1C1, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_SW1_SEL, unknown_val_tbl,
+                 0, 0, 0, 0, 0, 0),
+       CPCAP_REG(SW2, CPCAP_REG_S2C1, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_SW2_SEL, sw2_sw4_val_tbl,
+                 0xf00, 0x7f, 0, 0x800, 0, 120),
+       CPCAP_REG(SW3, CPCAP_REG_S3C, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_SW3_SEL, unknown_val_tbl,
+                 0, 0, 0, 0, 0, 0),
+       CPCAP_REG(SW4, CPCAP_REG_S4C1, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_SW4_SEL, sw2_sw4_val_tbl,
+                 0xf00, 0x7f, 0, 0x900, 0, 100),
+       CPCAP_REG(SW5, CPCAP_REG_S5C, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_SW5_SEL, sw5_val_tbl,
+                 0x2a, 0, 0, 0x22, 0, 0),
+       CPCAP_REG(SW6, CPCAP_REG_S6C, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_SW6_SEL, unknown_val_tbl,
+                 0, 0, 0, 0, 0, 0),
+       CPCAP_REG(VCAM, CPCAP_REG_VCAMC, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_VCAM_SEL, vcam_val_tbl,
+                 0x87, 0x30, 4, 0x7, 0, 420),
+       CPCAP_REG(VCSI, CPCAP_REG_VCSIC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VCSI_SEL, vcsi_val_tbl,
+                 0x47, 0x10, 4, 0x7, 0, 350),
+       CPCAP_REG(VDAC, CPCAP_REG_VDACC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VDAC_SEL, vdac_val_tbl,
+                 0x87, 0x30, 4, 0x3, 0, 420),
+       CPCAP_REG(VDIG, CPCAP_REG_VDIGC, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_VDIG_SEL, vdig_val_tbl,
+                 0x87, 0x30, 4, 0x5, 0, 420),
+       CPCAP_REG(VFUSE, CPCAP_REG_VFUSEC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VFUSE_SEL, vfuse_val_tbl,
+                 0x80, 0xf, 0, 0x80, 0, 420),
+       CPCAP_REG(VHVIO, CPCAP_REG_VHVIOC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VHVIO_SEL, vhvio_val_tbl,
+                 0x17, 0, 0, 0x2, 0, 0),
+       CPCAP_REG(VSDIO, CPCAP_REG_VSDIOC, CPCAP_REG_ASSIGN2,
+                 CPCAP_BIT_VSDIO_SEL, vsdio_val_tbl,
+                 0x87, 0x38, 3, 0x2, 0, 420),
+       CPCAP_REG(VPLL, CPCAP_REG_VPLLC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VPLL_SEL, vpll_val_tbl,
+                 0x43, 0x18, 3, 0x1, 0, 420),
+       CPCAP_REG(VRF1, CPCAP_REG_VRF1C, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VRF1_SEL, vrf1_val_tbl,
+                 0xac, 0x2, 1, 0xc, 0, 10),
+       CPCAP_REG(VRF2, CPCAP_REG_VRF2C, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VRF2_SEL, vrf2_val_tbl,
+                 0x23, 0x8, 3, 0x3, 0, 10),
+       CPCAP_REG(VRFREF, CPCAP_REG_VRFREFC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VRFREF_SEL, vrfref_val_tbl,
+                 0x23, 0x8, 3, 0x3, 0, 420),
+       CPCAP_REG(VWLAN1, CPCAP_REG_VWLAN1C, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VWLAN1_SEL, vwlan1_val_tbl,
+                 0x47, 0x10, 4, 0x5, 0, 420),
+       CPCAP_REG(VWLAN2, CPCAP_REG_VWLAN2C, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VWLAN2_SEL, vwlan2_val_tbl,
+                 0x20c, 0xc0, 6, 0x8, 0, 420),
+       CPCAP_REG(VSIM, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+                 0xffff, vsim_val_tbl,
+                 0x23, 0x8, 3, 0x3, 0, 420),
+       CPCAP_REG(VSIMCARD, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
+                 0xffff, vsimcard_val_tbl,
+                 0x1e80, 0x8, 3, 0x1e00, 0, 420),
+       CPCAP_REG(VVIB, CPCAP_REG_VVIBC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VVIB_SEL, vvib_val_tbl,
+                 0x1, 0xc, 2, 0, 0x1, 500),
+       CPCAP_REG(VUSB, CPCAP_REG_VUSBC, CPCAP_REG_ASSIGN3,
+                 CPCAP_BIT_VUSB_SEL, vusb_val_tbl,
+                 0x11c, 0x40, 6, 0xc, 0, 0),
+       CPCAP_REG(VAUDIO, CPCAP_REG_VAUDIOC, CPCAP_REG_ASSIGN4,
+                 CPCAP_BIT_VAUDIO_SEL, vaudio_val_tbl,
+                 0x16, 0x1, 0, 0x4, 0, 0),
+       { /* sentinel */ },
+};
+
 static const struct of_device_id cpcap_regulator_id_table[] = {
        {
                .compatible = "motorola,cpcap-regulator",
@@ -397,6 +496,10 @@ static const struct of_device_id cpcap_regulator_id_table[] = {
                .compatible = "motorola,mapphone-cpcap-regulator",
                .data = omap4_regulators,
        },
+       {
+               .compatible = "motorola,xoom-cpcap-regulator",
+               .data = xoom_regulators,
+       },
        {},
 };
 MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table);
index 0db288ce319ce4a27562fd5e8afe5cb113c3799a..bc7f4751bf9ca0a5eca167029187f2fdb2280c7e 100644 (file)
@@ -1,19 +1,9 @@
-/*
- * max14577.c - Regulator driver for the Maxim 14577/77836
- *
- * Copyright (C) 2013,2014 Samsung Electronics
- * Krzysztof Kozlowski <krzk@kernel.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max14577.c - Regulator driver for the Maxim 14577/77836
+//
+// Copyright (C) 2013,2014 Samsung Electronics
+// Krzysztof Kozlowski <krzk@kernel.org>
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
index c301f37334758599fc98ba615a9159a62c9ba708..bee060937f56080709e235bb4ebe5c1b494c681d 100644 (file)
@@ -1,26 +1,12 @@
-/*
- * max77686.c - Regulator driver for the Maxim 77686
- *
- * Copyright (C) 2012 Samsung Electronics
- * Chiwoong Byun <woong.byun@samsung.com>
- * Jonghwa Lee <jonghwa3.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * This driver is based on max8997.c
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max77686.c - Regulator driver for the Maxim 77686
+//
+// Copyright (C) 2012 Samsung Electronics
+// Chiwoong Byun <woong.byun@samsung.com>
+// Jonghwa Lee <jonghwa3.lee@samsung.com>
+//
+// This driver is based on max8997.c
 
 #include <linux/kernel.h>
 #include <linux/bug.h>
index e7000e777292f957f2e6a49bbcec72fbfa5a42c4..077ecbbfdf762904fe2663ee1bf01926eec3d011 100644 (file)
@@ -1,26 +1,12 @@
-/*
- * max77693.c - Regulator driver for the Maxim 77693 and 77843
- *
- * Copyright (C) 2013-2015 Samsung Electronics
- * Jonghwa Lee <jonghwa3.lee@samsung.com>
- * Krzysztof Kozlowski <krzk@kernel.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * This driver is based on max77686.c
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max77693.c - Regulator driver for the Maxim 77693 and 77843
+//
+// Copyright (C) 2013-2015 Samsung Electronics
+// Jonghwa Lee <jonghwa3.lee@samsung.com>
+// Krzysztof Kozlowski <krzk@kernel.org>
+//
+// This driver is based on max77686.c
 
 #include <linux/err.h>
 #include <linux/slab.h>
index b6261903818c6266a431f3cb2bb8d83b7a5296f1..c30cf5c9f2de33470273784e8491ed155c196b63 100644 (file)
@@ -1,25 +1,15 @@
-/*
- * max77802.c - Regulator driver for the Maxim 77802
- *
- * Copyright (C) 2013-2014 Google, Inc
- * Simon Glass <sjg@chromium.org>
- *
- * Copyright (C) 2012 Samsung Electronics
- * Chiwoong Byun <woong.byun@samsung.com>
- * Jonghwa Lee <jonghwa3.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * This driver is based on max8997.c
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max77802.c - Regulator driver for the Maxim 77802
+//
+// Copyright (C) 2013-2014 Google, Inc
+// Simon Glass <sjg@chromium.org>
+//
+// Copyright (C) 2012 Samsung Electronics
+// Chiwoong Byun <woong.byun@samsung.com>
+// Jonghwa Lee <jonghwa3.lee@samsung.com>
+//
+// This driver is based on max8997.c
 
 #include <linux/kernel.h>
 #include <linux/bug.h>
index a8ea30ee18a6be50722c51a0b045fd93c5b2e2be..ad0c806b07376e55307664542a899d329eb78df9 100644 (file)
@@ -1,25 +1,11 @@
-/*
- * max8997.c - Regulator driver for the Maxim 8997/8966
- *
- * Copyright (C) 2011 Samsung Electronics
- * MyungJoo Ham <myungjoo.ham@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * This driver is based on max8998.c
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max8997.c - Regulator driver for the Maxim 8997/8966
+//
+// Copyright (C) 2011 Samsung Electronics
+// MyungJoo Ham <myungjoo.ham@samsung.com>
+//
+// This driver is based on max8998.c
 
 #include <linux/bug.h>
 #include <linux/err.h>
@@ -165,8 +151,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev,
        int rid = rdev_get_id(rdev);
        int val;
 
-       if (rid >= ARRAY_SIZE(reg_voltage_map) ||
-                       rid < 0)
+       if (rid < 0 || rid >= ARRAY_SIZE(reg_voltage_map))
                return -EINVAL;
 
        desc = reg_voltage_map[rid];
index 6b9f262ebbb09034d8822da5bfed683dbff09e2e..271bb736f3f55782c50bb3d2b6a73d0ddfe958b8 100644 (file)
@@ -1,24 +1,10 @@
-/*
- * max8998.c - Voltage regulator driver for the Maxim 8998
- *
- *  Copyright (C) 2009-2010 Samsung Electronics
- *  Kyungmin Park <kyungmin.park@samsung.com>
- *  Marek Szyprowski <m.szyprowski@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// max8998.c - Voltage regulator driver for the Maxim 8998
+//
+//  Copyright (C) 2009-2010 Samsung Electronics
+//  Kyungmin Park <kyungmin.park@samsung.com>
+//  Marek Szyprowski <m.szyprowski@samsung.com>
 
 #include <linux/module.h>
 #include <linux/init.h>
index 8d9dbcc775eaa77dce16612d451b9a790dacc45b..31c3a236120a80909ede26f29caa5288011d8cbf 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/slab.h>
 #include <linux/regmap.h>
 
+#define PFUZE_FLAG_DISABLE_SW  BIT(1)
+
 #define PFUZE_NUMREGS          128
 #define PFUZE100_VOL_OFFSET    0
 #define PFUZE100_STANDBY_OFFSET        1
 #define PFUZE100_VGEN5VOL      0x70
 #define PFUZE100_VGEN6VOL      0x71
 
-enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3 };
+enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, };
 
 struct pfuze_regulator {
        struct regulator_desc desc;
        unsigned char stby_reg;
        unsigned char stby_mask;
+       bool sw_reg;
 };
 
 struct pfuze_chip {
        int     chip_id;
+       int     flags;
        struct regmap *regmap;
        struct device *dev;
        struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR];
@@ -92,6 +96,7 @@ static const struct i2c_device_id pfuze_device_id[] = {
        {.name = "pfuze100", .driver_data = PFUZE100},
        {.name = "pfuze200", .driver_data = PFUZE200},
        {.name = "pfuze3000", .driver_data = PFUZE3000},
+       {.name = "pfuze3001", .driver_data = PFUZE3001},
        { }
 };
 MODULE_DEVICE_TABLE(i2c, pfuze_device_id);
@@ -100,6 +105,7 @@ static const struct of_device_id pfuze_dt_ids[] = {
        { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100},
        { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
        { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000},
+       { .compatible = "fsl,pfuze3001", .data = (void *)PFUZE3001},
        { }
 };
 MODULE_DEVICE_TABLE(of, pfuze_dt_ids);
@@ -108,10 +114,28 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
 {
        struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev);
        int id = rdev_get_id(rdev);
+       bool reg_has_ramp_delay;
        unsigned int ramp_bits;
        int ret;
 
-       if (id < PFUZE100_SWBST) {
+       switch (pfuze100->chip_id) {
+       case PFUZE3001:
+               /* no dynamic voltage scaling for PF3001 */
+               reg_has_ramp_delay = false;
+               break;
+       case PFUZE3000:
+               reg_has_ramp_delay = (id < PFUZE3000_SWBST);
+               break;
+       case PFUZE200:
+               reg_has_ramp_delay = (id < PFUZE200_SWBST);
+               break;
+       case PFUZE100:
+       default:
+               reg_has_ramp_delay = (id < PFUZE100_SWBST);
+               break;
+       }
+
+       if (reg_has_ramp_delay) {
                ramp_delay = 12500 / ramp_delay;
                ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3);
                ret = regmap_update_bits(pfuze100->regmap,
@@ -119,8 +143,9 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
                                         0xc0, ramp_bits << 6);
                if (ret < 0)
                        dev_err(pfuze100->dev, "ramp failed, err %d\n", ret);
-       } else
+       } else {
                ret = -EACCES;
+       }
 
        return ret;
 }
@@ -142,6 +167,14 @@ static const struct regulator_ops pfuze100_fixed_regulator_ops = {
 };
 
 static const struct regulator_ops pfuze100_sw_regulator_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .set_voltage_time_sel = regulator_set_voltage_time_sel,
+       .set_ramp_delay = pfuze100_set_ramp_delay,
+};
+
+static const struct regulator_ops pfuze100_sw_disable_regulator_ops = {
        .enable = regulator_enable_regmap,
        .disable = regulator_disable_regmap,
        .is_enabled = regulator_is_enabled_regmap,
@@ -192,13 +225,11 @@ static const struct regulator_ops pfuze100_swb_regulator_ops = {
                        .vsel_reg = (base) + PFUZE100_VOL_OFFSET,       \
                        .vsel_mask = 0x3f,      \
                        .enable_reg = (base) + PFUZE100_MODE_OFFSET,    \
-                       .enable_val = 0xc,      \
-                       .disable_val = 0x0,     \
                        .enable_mask = 0xf,     \
-                       .enable_time = 500,     \
                },      \
                .stby_reg = (base) + PFUZE100_STANDBY_OFFSET,   \
                .stby_mask = 0x3f,      \
+               .sw_reg = true,         \
        }
 
 #define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages)   \
@@ -361,6 +392,19 @@ static struct pfuze_regulator pfuze3000_regulators[] = {
        PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
 };
 
+static struct pfuze_regulator pfuze3001_regulators[] = {
+       PFUZE100_SWB_REG(PFUZE3001, SW1, PFUZE100_SW1ABVOL, 0x1f, pfuze3000_sw1a),
+       PFUZE100_SWB_REG(PFUZE3001, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo),
+       PFUZE3000_SW3_REG(PFUZE3001, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000),
+       PFUZE100_SWB_REG(PFUZE3001, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
+       PFUZE100_VGEN_REG(PFUZE3001, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000),
+       PFUZE100_VGEN_REG(PFUZE3001, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000),
+       PFUZE3000_VCC_REG(PFUZE3001, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000),
+       PFUZE3000_VCC_REG(PFUZE3001, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000),
+       PFUZE100_VGEN_REG(PFUZE3001, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
+       PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+};
+
 #ifdef CONFIG_OF
 /* PFUZE100 */
 static struct of_regulator_match pfuze100_matches[] = {
@@ -418,6 +462,21 @@ static struct of_regulator_match pfuze3000_matches[] = {
        { .name = "vldo4",      },
 };
 
+/* PFUZE3001 */
+static struct of_regulator_match pfuze3001_matches[] = {
+
+       { .name = "sw1",        },
+       { .name = "sw2",        },
+       { .name = "sw3",        },
+       { .name = "vsnvs",      },
+       { .name = "vldo1",      },
+       { .name = "vldo2",      },
+       { .name = "vccsd",      },
+       { .name = "v33",        },
+       { .name = "vldo3",      },
+       { .name = "vldo4",      },
+};
+
 static struct of_regulator_match *pfuze_matches;
 
 static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
@@ -430,6 +489,9 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
        if (!np)
                return -EINVAL;
 
+       if (of_property_read_bool(np, "fsl,pfuze-support-disable-sw"))
+               chip->flags |= PFUZE_FLAG_DISABLE_SW;
+
        parent = of_get_child_by_name(np, "regulators");
        if (!parent) {
                dev_err(dev, "regulators node not found\n");
@@ -437,6 +499,11 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
        }
 
        switch (chip->chip_id) {
+       case PFUZE3001:
+               pfuze_matches = pfuze3001_matches;
+               ret = of_regulator_match(dev, parent, pfuze3001_matches,
+                                        ARRAY_SIZE(pfuze3001_matches));
+               break;
        case PFUZE3000:
                pfuze_matches = pfuze3000_matches;
                ret = of_regulator_match(dev, parent, pfuze3000_matches,
@@ -508,7 +575,8 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip)
                 */
                dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8");
        } else if ((value & 0x0f) != pfuze_chip->chip_id &&
-                  (value & 0xf0) >> 4 != pfuze_chip->chip_id) {
+                  (value & 0xf0) >> 4 != pfuze_chip->chip_id &&
+                  (value != pfuze_chip->chip_id)) {
                /* device id NOT match with your setting */
                dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
                return -ENODEV;
@@ -588,6 +656,13 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
 
        /* use the right regulators after identify the right device */
        switch (pfuze_chip->chip_id) {
+       case PFUZE3001:
+               pfuze_chip->pfuze_regulators = pfuze3001_regulators;
+               regulator_num = ARRAY_SIZE(pfuze3001_regulators);
+               sw_check_start = PFUZE3001_SW2;
+               sw_check_end = PFUZE3001_SW2;
+               sw_hi = 1 << 3;
+               break;
        case PFUZE3000:
                pfuze_chip->pfuze_regulators = pfuze3000_regulators;
                regulator_num = ARRAY_SIZE(pfuze3000_regulators);
@@ -611,7 +686,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
        }
        dev_info(&client->dev, "pfuze%s found.\n",
                (pfuze_chip->chip_id == PFUZE100) ? "100" :
-               ((pfuze_chip->chip_id == PFUZE200) ? "200" : "3000"));
+               (((pfuze_chip->chip_id == PFUZE200) ? "200" :
+               ((pfuze_chip->chip_id == PFUZE3000) ? "3000" : "3001"))));
 
        memcpy(pfuze_chip->regulator_descs, pfuze_chip->pfuze_regulators,
                sizeof(pfuze_chip->regulator_descs));
@@ -636,7 +712,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
                if (i >= sw_check_start && i <= sw_check_end) {
                        regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val);
                        if (val & sw_hi) {
-                               if (pfuze_chip->chip_id == PFUZE3000) {
+                               if (pfuze_chip->chip_id == PFUZE3000 ||
+                                       pfuze_chip->chip_id == PFUZE3001) {
                                        desc->volt_table = pfuze3000_sw2hi;
                                        desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi);
                                } else {
@@ -647,6 +724,21 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
                        }
                }
 
+               /*
+                * Allow SW regulators to turn off. Checking it trough a flag is
+                * a workaround to keep the backward compatibility with existing
+                * old dtb's which may relay on the fact that we didn't disable
+                * the switched regulator till yet.
+                */
+               if (pfuze_chip->flags & PFUZE_FLAG_DISABLE_SW) {
+                       if (pfuze_chip->regulator_descs[i].sw_reg) {
+                               desc->ops = &pfuze100_sw_disable_regulator_ops;
+                               desc->enable_val = 0x8;
+                               desc->disable_val = 0x0;
+                               desc->enable_time = 500;
+                       }
+               }
+
                config.dev = &client->dev;
                config.init_data = init_data;
                config.driver_data = pfuze_chip;
@@ -675,5 +767,5 @@ static struct i2c_driver pfuze_driver = {
 module_i2c_driver(pfuze_driver);
 
 MODULE_AUTHOR("Robin Gong <b38343@freescale.com>");
-MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000 PMIC");
+MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000/3001 PMIC");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
new file mode 100644 (file)
index 0000000..9f27dae
--- /dev/null
@@ -0,0 +1,769 @@
+// 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,
+ *             and mode 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_REGULATOR_REG_VRM_VOLTAGE         0x0
+#define RPMH_REGULATOR_REG_ENABLE              0x4
+#define RPMH_REGULATOR_REG_VRM_MODE            0x8
+
+#define PMIC4_LDO_MODE_RETENTION               4
+#define PMIC4_LDO_MODE_LPM                     5
+#define PMIC4_LDO_MODE_HPM                     7
+
+#define PMIC4_SMPS_MODE_RETENTION              4
+#define PMIC4_SMPS_MODE_PFM                    5
+#define PMIC4_SMPS_MODE_AUTO                   6
+#define PMIC4_SMPS_MODE_PWM                    7
+
+#define PMIC4_BOB_MODE_PASS                    0
+#define PMIC4_BOB_MODE_PFM                     1
+#define PMIC4_BOB_MODE_AUTO                    2
+#define PMIC4_BOB_MODE_PWM                     3
+
+/**
+ * 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
+ * @hpm_min_load_uA:           Minimum load current in microamps that requires
+ *                             high power mode (HPM) operation.  This is used
+ *                             for LDO hardware type regulators only.
+ * @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
+ */
+struct rpmh_vreg_hw_data {
+       enum rpmh_regulator_type                regulator_type;
+       const struct regulator_ops              *ops;
+       const struct regulator_linear_range     voltage_range;
+       int                                     n_voltages;
+       int                                     hpm_min_load_uA;
+       const int                               *pmic_mode_map;
+       unsigned int                          (*of_map_mode)(unsigned int mode);
+};
+
+/**
+ * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a
+ *             single regulator device
+ * @dev:                       Device pointer for the top-level PMIC RPMh
+ *                             regulator parent device.  This is used as a
+ *                             handle in RPMh write requests.
+ * @addr:                      Base address of the regulator resource within
+ *                             an RPMh accelerator
+ * @rdesc:                     Regulator descriptor
+ * @hw_data:                   PMIC regulator configuration data for this RPMh
+ *                             regulator
+ * @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).
+ * @enabled:                   Flag indicating if the regulator is enabled or
+ *                             not
+ * @bypassed:                  Boolean indicating if the regulator is in
+ *                             bypass (pass-through) mode or not.  This is
+ *                             only used by BOB rpmh-regulator resources.
+ * @voltage_selector:          Selector used for get_voltage_sel() and
+ *                             set_voltage_sel() callbacks
+ * @mode:                      RPMh VRM regulator current framework mode
+ */
+struct rpmh_vreg {
+       struct device                   *dev;
+       u32                             addr;
+       struct regulator_desc           rdesc;
+       const struct rpmh_vreg_hw_data  *hw_data;
+       bool                            always_wait_for_ack;
+
+       int                             enabled;
+       bool                            bypassed;
+       int                             voltage_selector;
+       unsigned int                    mode;
+};
+
+/**
+ * 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:               Pointer to the RPMh command to send
+ * @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, bool wait_for_ack)
+{
+       int ret;
+
+       if (wait_for_ack || vreg->always_wait_for_ack)
+               ret = rpmh_write(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, 1);
+       else
+               ret = rpmh_write_async(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd,
+                                       1);
+
+       return ret;
+}
+
+static int _rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+                               unsigned int selector, bool wait_for_ack)
+{
+       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, wait_for_ack);
+       if (!ret)
+               vreg->voltage_selector = selector;
+
+       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);
+
+       if (vreg->enabled == -EINVAL) {
+               /*
+                * Cache the voltage and send it later when the regulator is
+                * enabled or disabled.
+                */
+               vreg->voltage_selector = selector;
+               return 0;
+       }
+
+       return _rpmh_regulator_vrm_set_voltage_sel(rdev, selector,
+                                       selector > vreg->voltage_selector);
+}
+
+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_is_enabled(struct regulator_dev *rdev)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+       return vreg->enabled;
+}
+
+static int rpmh_regulator_set_enable_state(struct regulator_dev *rdev,
+                                       bool enable)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       struct tcs_cmd cmd = {
+               .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+               .data = enable,
+       };
+       int ret;
+
+       if (vreg->enabled == -EINVAL &&
+           vreg->voltage_selector != -ENOTRECOVERABLE) {
+               ret = _rpmh_regulator_vrm_set_voltage_sel(rdev,
+                                               vreg->voltage_selector, true);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = rpmh_regulator_send_request(vreg, &cmd, enable);
+       if (!ret)
+               vreg->enabled = enable;
+
+       return ret;
+}
+
+static int rpmh_regulator_enable(struct regulator_dev *rdev)
+{
+       return rpmh_regulator_set_enable_state(rdev, true);
+}
+
+static int rpmh_regulator_disable(struct regulator_dev *rdev)
+{
+       return rpmh_regulator_set_enable_state(rdev, false);
+}
+
+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,
+       };
+       int pmic_mode;
+
+       if (mode > REGULATOR_MODE_STANDBY)
+               return -EINVAL;
+
+       pmic_mode = vreg->hw_data->pmic_mode_map[mode];
+       if (pmic_mode < 0)
+               return pmic_mode;
+
+       if (bypassed)
+               cmd.data = PMIC4_BOB_MODE_PASS;
+       else
+               cmd.data = pmic_mode;
+
+       return rpmh_regulator_send_request(vreg, &cmd, 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;
+
+       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);
+
+       return vreg->mode;
+}
+
+/**
+ * rpmh_regulator_vrm_set_load() - set the regulator mode based upon the load
+ *             current requested
+ * @rdev:              Regulator device pointer for the rpmh-regulator
+ * @load_uA:           Aggregated load current in microamps
+ *
+ * This function is used in the regulator_ops for VRM type RPMh regulator
+ * devices.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+       struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+       unsigned int mode;
+
+       if (load_uA >= vreg->hw_data->hpm_min_load_uA)
+               mode = REGULATOR_MODE_NORMAL;
+       else
+               mode = REGULATOR_MODE_IDLE;
+
+       return rpmh_regulator_vrm_set_mode(rdev, mode);
+}
+
+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,
+};
+
+static const struct regulator_ops rpmh_regulator_vrm_drms_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_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_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
+ * pmic_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 *pmic_rpmh_data)
+{
+       struct regulator_config reg_config = {};
+       char rpmh_resource_name[20] = "";
+       const struct rpmh_vreg_init_data *rpmh_data;
+       struct regulator_init_data *init_data;
+       struct regulator_dev *rdev;
+       int ret;
+
+       vreg->dev = dev;
+
+       for (rpmh_data = pmic_rpmh_data; 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->hw_data = rpmh_data->hw_data;
+
+       vreg->enabled = -EINVAL;
+       vreg->voltage_selector = -ENOTRECOVERABLE;
+       vreg->mode = REGULATOR_MODE_INVALID;
+
+       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;
+       }
+
+       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 (rpmh_data->hw_data->regulator_type == XOB &&
+           init_data->constraints.min_uV &&
+           init_data->constraints.min_uV == init_data->constraints.max_uV) {
+               vreg->rdesc.fixed_uV = init_data->constraints.min_uV;
+               vreg->rdesc.n_voltages = 1;
+       }
+
+       reg_config.dev          = dev;
+       reg_config.init_data    = init_data;
+       reg_config.of_node      = node;
+       reg_config.driver_data  = vreg;
+
+       rdev = devm_regulator_register(dev, &vreg->rdesc, &reg_config);
+       if (IS_ERR(rdev)) {
+               ret = PTR_ERR(rdev);
+               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);
+
+       return 0;
+}
+
+static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
+       [REGULATOR_MODE_INVALID] = -EINVAL,
+       [REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION,
+       [REGULATOR_MODE_IDLE]    = PMIC4_LDO_MODE_LPM,
+       [REGULATOR_MODE_NORMAL]  = PMIC4_LDO_MODE_HPM,
+       [REGULATOR_MODE_FAST]    = -EINVAL,
+};
+
+static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode)
+{
+       unsigned int mode;
+
+       switch (rpmh_mode) {
+       case RPMH_REGULATOR_MODE_HPM:
+               mode = REGULATOR_MODE_NORMAL;
+               break;
+       case RPMH_REGULATOR_MODE_LPM:
+               mode = REGULATOR_MODE_IDLE;
+               break;
+       case RPMH_REGULATOR_MODE_RET:
+               mode = REGULATOR_MODE_STANDBY;
+               break;
+       default:
+               mode = REGULATOR_MODE_INVALID;
+       }
+
+       return mode;
+}
+
+static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = {
+       [REGULATOR_MODE_INVALID] = -EINVAL,
+       [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,
+};
+
+static unsigned int
+rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode)
+{
+       unsigned int mode;
+
+       switch (rpmh_mode) {
+       case RPMH_REGULATOR_MODE_HPM:
+               mode = REGULATOR_MODE_FAST;
+               break;
+       case RPMH_REGULATOR_MODE_AUTO:
+               mode = REGULATOR_MODE_NORMAL;
+               break;
+       case RPMH_REGULATOR_MODE_LPM:
+               mode = REGULATOR_MODE_IDLE;
+               break;
+       case RPMH_REGULATOR_MODE_RET:
+               mode = REGULATOR_MODE_STANDBY;
+               break;
+       default:
+               mode = REGULATOR_MODE_INVALID;
+       }
+
+       return mode;
+}
+
+static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = {
+       [REGULATOR_MODE_INVALID] = -EINVAL,
+       [REGULATOR_MODE_STANDBY] = -EINVAL,
+       [REGULATOR_MODE_IDLE]    = PMIC4_BOB_MODE_PFM,
+       [REGULATOR_MODE_NORMAL]  = PMIC4_BOB_MODE_AUTO,
+       [REGULATOR_MODE_FAST]    = PMIC4_BOB_MODE_PWM,
+};
+
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode)
+{
+       unsigned int mode;
+
+       switch (rpmh_mode) {
+       case RPMH_REGULATOR_MODE_HPM:
+               mode = REGULATOR_MODE_FAST;
+               break;
+       case RPMH_REGULATOR_MODE_AUTO:
+               mode = REGULATOR_MODE_NORMAL;
+               break;
+       case RPMH_REGULATOR_MODE_LPM:
+               mode = REGULATOR_MODE_IDLE;
+               break;
+       default:
+               mode = REGULATOR_MODE_INVALID;
+       }
+
+       return mode;
+}
+
+static const struct rpmh_vreg_hw_data pmic4_pldo = {
+       .regulator_type = VRM,
+       .ops = &rpmh_regulator_vrm_drms_ops,
+       .voltage_range = REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
+       .n_voltages = 256,
+       .hpm_min_load_uA = 10000,
+       .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_drms_ops,
+       .voltage_range = REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
+       .n_voltages = 128,
+       .hpm_min_load_uA = 10000,
+       .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_drms_ops,
+       .voltage_range = REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
+       .n_voltages = 128,
+       .hpm_min_load_uA = 30000,
+       .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,
+};
+
+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,       "vin-lvs-1-2"),
+       RPMH_VREG("lvs2",   "vs%s2",   &pmic4_lvs,       "vin-lvs-1-2"),
+       {},
+};
+
+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 device_node *node;
+       struct rpmh_vreg *vreg;
+       const char *pmic_id;
+       int 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;
+       }
+
+       for_each_available_child_of_node(dev->of_node, node) {
+               vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+               if (!vreg) {
+                       of_node_put(node);
+                       return -ENOMEM;
+               }
+
+               ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id,
+                                               vreg_data);
+               if (ret < 0) {
+                       of_node_put(node);
+                       return ret;
+               }
+       }
+
+       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,
+};
+module_platform_driver(rpmh_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm RPMh regulator driver");
+MODULE_LICENSE("GPL v2");
index 9817f1a753425e2199b0f3fda82ac15907ffc63a..53a61fb6564289a6585f4ee042c50562b526905f 100644 (file)
@@ -1060,7 +1060,7 @@ static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
 #define SAW3_AVS_CTL_TGGL_MASK 0x8000000
 #define SAW3_AVS_CTL_CLEAR_MASK        0x7efc00
 
-static struct regmap *saw_regmap = NULL;
+static struct regmap *saw_regmap;
 
 static void spmi_saw_set_vdd(void *data)
 {
@@ -1728,7 +1728,7 @@ static const struct spmi_regulator_data pmi8994_regulators[] = {
        { "s2", 0x1700, "vdd_s2", },
        { "s3", 0x1a00, "vdd_s3", },
        { "l1", 0x4000, "vdd_l1", },
-        { }
+       { }
 };
 
 static const struct of_device_id qcom_spmi_regulator_match[] = {
@@ -1752,7 +1752,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
        const char *name;
        struct device *dev = &pdev->dev;
        struct device_node *node = pdev->dev.of_node;
-       struct device_node *syscon;
+       struct device_node *syscon, *reg_node;
+       struct property *reg_prop;
        int ret, lenp;
        struct list_head *vreg_list;
 
@@ -1774,16 +1775,19 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
                syscon = of_parse_phandle(node, "qcom,saw-reg", 0);
                saw_regmap = syscon_node_to_regmap(syscon);
                of_node_put(syscon);
-               if (IS_ERR(regmap))
+               if (IS_ERR(saw_regmap))
                        dev_err(dev, "ERROR reading SAW regmap\n");
        }
 
        for (reg = match->data; reg->name; reg++) {
 
-               if (saw_regmap && \
-                   of_find_property(of_find_node_by_name(node, reg->name), \
-                                    "qcom,saw-slave", &lenp)) {
-                       continue;
+               if (saw_regmap) {
+                       reg_node = of_get_child_by_name(node, reg->name);
+                       reg_prop = of_find_property(reg_node, "qcom,saw-slave",
+                                                   &lenp);
+                       of_node_put(reg_node);
+                       if (reg_prop)
+                               continue;
                }
 
                vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
@@ -1816,13 +1820,17 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
                if (ret)
                        continue;
 
-               if (saw_regmap && \
-                   of_find_property(of_find_node_by_name(node, reg->name), \
-                                    "qcom,saw-leader", &lenp)) {
-                       spmi_saw_ops = *(vreg->desc.ops);
-                       spmi_saw_ops.set_voltage_sel = \
-                               spmi_regulator_saw_set_voltage;
-                       vreg->desc.ops = &spmi_saw_ops;
+               if (saw_regmap) {
+                       reg_node = of_get_child_by_name(node, reg->name);
+                       reg_prop = of_find_property(reg_node, "qcom,saw-leader",
+                                                   &lenp);
+                       of_node_put(reg_node);
+                       if (reg_prop) {
+                               spmi_saw_ops = *(vreg->desc.ops);
+                               spmi_saw_ops.set_voltage_sel =
+                                       spmi_regulator_saw_set_voltage;
+                               vreg->desc.ops = &spmi_saw_ops;
+                       }
                }
 
                config.dev = dev;
index 48f0ca90743cca5cec830e6b2ce74fa9f7e8d0a8..095d25f3d2eae1fbc3220e2628f678c4e4498294 100644 (file)
@@ -1,13 +1,7 @@
-/*
- * Copyright (c) 2013 Samsung Electronics Co., Ltd
- *             http://www.samsung.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2013 Samsung Electronics Co., Ltd
+//             http://www.samsung.com
 
 #include <linux/bug.h>
 #include <linux/err.h>
index d1207ec683db22e72c4811a04f2d9529b7a65906..5bb6f4ca48db8e0abb49b7af9b419829031b41f9 100644 (file)
@@ -1,20 +1,7 @@
-/*
- * s2mps11.c
- *
- * Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
- *              http://www.samsung.com
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
+//              http://www.samsung.com
 
 #include <linux/bug.h>
 #include <linux/err.h>
index 0cbc980753c2f0fcb6151a67d60457e2daeadb93..667d16dc83ce1506737ddc6c10fa072d31d558d9 100644 (file)
@@ -1,15 +1,7 @@
-/*
- * s5m8767.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd
- *              http://www.samsung.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Copyright (c) 2011 Samsung Electronics Co., Ltd
+//              http://www.samsung.com
 
 #include <linux/err.h>
 #include <linux/of_gpio.h>
index fc12badf38059f96b4bea38a6ffaed8bd9a71dbc..d84fab616abf45cde4e04887500ed57aa4fc5ea9 100644 (file)
@@ -232,6 +232,8 @@ static int tps65217_regulator_probe(struct platform_device *pdev)
        tps->strobes = devm_kcalloc(&pdev->dev,
                                    TPS65217_NUM_REGULATOR, sizeof(u8),
                                    GFP_KERNEL);
+       if (!tps->strobes)
+               return -ENOMEM;
 
        platform_set_drvdata(pdev, tps);
 
diff --git a/drivers/regulator/uniphier-regulator.c b/drivers/regulator/uniphier-regulator.c
new file mode 100644 (file)
index 0000000..abf22ac
--- /dev/null
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Regulator controller driver for UniPhier SoC
+// Copyright 2018 Socionext Inc.
+// Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/reset.h>
+
+#define MAX_CLKS       2
+#define MAX_RSTS       2
+
+struct uniphier_regulator_soc_data {
+       int nclks;
+       const char * const *clock_names;
+       int nrsts;
+       const char * const *reset_names;
+       const struct regulator_desc *desc;
+       const struct regmap_config *regconf;
+};
+
+struct uniphier_regulator_priv {
+       struct clk_bulk_data clk[MAX_CLKS];
+       struct reset_control *rst[MAX_RSTS];
+       const struct uniphier_regulator_soc_data *data;
+};
+
+static struct regulator_ops uniphier_regulator_ops = {
+       .enable     = regulator_enable_regmap,
+       .disable    = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+};
+
+static int uniphier_regulator_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct uniphier_regulator_priv *priv;
+       struct regulator_config config = { };
+       struct regulator_dev *rdev;
+       struct regmap *regmap;
+       struct resource *res;
+       void __iomem *base;
+       const char *name;
+       int i, ret, nr;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->data = of_device_get_match_data(dev);
+       if (WARN_ON(!priv->data))
+               return -EINVAL;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       for (i = 0; i < priv->data->nclks; i++)
+               priv->clk[i].id = priv->data->clock_names[i];
+       ret = devm_clk_bulk_get(dev, priv->data->nclks, priv->clk);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < priv->data->nrsts; i++) {
+               name = priv->data->reset_names[i];
+               priv->rst[i] = devm_reset_control_get_shared(dev, name);
+               if (IS_ERR(priv->rst[i]))
+                       return PTR_ERR(priv->rst[i]);
+       }
+
+       ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk);
+       if (ret)
+               return ret;
+
+       for (nr = 0; nr < priv->data->nrsts; nr++) {
+               ret = reset_control_deassert(priv->rst[nr]);
+               if (ret)
+                       goto out_rst_assert;
+       }
+
+       regmap = devm_regmap_init_mmio(dev, base, priv->data->regconf);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       config.dev = dev;
+       config.driver_data = priv;
+       config.of_node = dev->of_node;
+       config.regmap = regmap;
+       config.init_data = of_get_regulator_init_data(dev, dev->of_node,
+                                                     priv->data->desc);
+       rdev = devm_regulator_register(dev, priv->data->desc, &config);
+       if (IS_ERR(rdev)) {
+               ret = PTR_ERR(rdev);
+               goto out_rst_assert;
+       }
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+
+out_rst_assert:
+       while (nr--)
+               reset_control_assert(priv->rst[nr]);
+
+       clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
+
+       return ret;
+}
+
+static int uniphier_regulator_remove(struct platform_device *pdev)
+{
+       struct uniphier_regulator_priv *priv = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < priv->data->nrsts; i++)
+               reset_control_assert(priv->rst[i]);
+
+       clk_bulk_disable_unprepare(priv->data->nclks, priv->clk);
+
+       return 0;
+}
+
+/* USB3 controller data */
+#define USB3VBUS_OFFSET                0x0
+#define USB3VBUS_REG           BIT(4)
+#define USB3VBUS_REG_EN                BIT(3)
+static const struct regulator_desc uniphier_usb3_regulator_desc = {
+       .name = "vbus",
+       .of_match = of_match_ptr("vbus"),
+       .ops = &uniphier_regulator_ops,
+       .type = REGULATOR_VOLTAGE,
+       .owner = THIS_MODULE,
+       .enable_reg  = USB3VBUS_OFFSET,
+       .enable_mask = USB3VBUS_REG_EN | USB3VBUS_REG,
+       .enable_val  = USB3VBUS_REG_EN | USB3VBUS_REG,
+       .disable_val = USB3VBUS_REG_EN,
+};
+
+static const struct regmap_config uniphier_usb3_regulator_regconf = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = 1,
+};
+
+static const char * const uniphier_pro4_clock_reset_names[] = {
+       "gio", "link",
+};
+
+static const struct uniphier_regulator_soc_data uniphier_pro4_usb3_data = {
+       .nclks = ARRAY_SIZE(uniphier_pro4_clock_reset_names),
+       .clock_names = uniphier_pro4_clock_reset_names,
+       .nrsts = ARRAY_SIZE(uniphier_pro4_clock_reset_names),
+       .reset_names = uniphier_pro4_clock_reset_names,
+       .desc = &uniphier_usb3_regulator_desc,
+       .regconf = &uniphier_usb3_regulator_regconf,
+};
+
+static const char * const uniphier_pxs2_clock_reset_names[] = {
+       "link",
+};
+
+static const struct uniphier_regulator_soc_data uniphier_pxs2_usb3_data = {
+       .nclks = ARRAY_SIZE(uniphier_pxs2_clock_reset_names),
+       .clock_names = uniphier_pxs2_clock_reset_names,
+       .nrsts = ARRAY_SIZE(uniphier_pxs2_clock_reset_names),
+       .reset_names = uniphier_pxs2_clock_reset_names,
+       .desc = &uniphier_usb3_regulator_desc,
+       .regconf = &uniphier_usb3_regulator_regconf,
+};
+
+static const struct of_device_id uniphier_regulator_match[] = {
+       /* USB VBUS */
+       {
+               .compatible = "socionext,uniphier-pro4-usb3-regulator",
+               .data = &uniphier_pro4_usb3_data,
+       },
+       {
+               .compatible = "socionext,uniphier-pxs2-usb3-regulator",
+               .data = &uniphier_pxs2_usb3_data,
+       },
+       {
+               .compatible = "socionext,uniphier-ld20-usb3-regulator",
+               .data = &uniphier_pxs2_usb3_data,
+       },
+       {
+               .compatible = "socionext,uniphier-pxs3-usb3-regulator",
+               .data = &uniphier_pxs2_usb3_data,
+       },
+       { /* Sentinel */ },
+};
+
+static struct platform_driver uniphier_regulator_driver = {
+       .probe = uniphier_regulator_probe,
+       .remove = uniphier_regulator_remove,
+       .driver = {
+               .name  = "uniphier-regulator",
+               .of_match_table = uniphier_regulator_match,
+       },
+};
+module_platform_driver(uniphier_regulator_driver);
+
+MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
+MODULE_DESCRIPTION("UniPhier Regulator Controller Driver");
+MODULE_LICENSE("GPL");
index 5856e792d09c8d317b01627c2d03a97eeaebff37..ba79b609aca21068fc73b7b6f552137dfebdce87 100644 (file)
@@ -40,6 +40,23 @@ config QCOM_GSBI
           functions for connecting the underlying serial UART, SPI, and I2C
           devices to the output pins.
 
+config QCOM_LLCC
+       tristate "Qualcomm Technologies, Inc. LLCC driver"
+       depends on ARCH_QCOM
+       help
+         Qualcomm Technologies, Inc. platform specific
+         Last Level Cache Controller(LLCC) driver. This provides interfaces
+         to clients that use the LLCC. Say yes here to enable LLCC slice
+         driver.
+
+config QCOM_SDM845_LLCC
+       tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver"
+       depends on QCOM_LLCC
+       help
+         Say yes here to enable the LLCC driver for SDM845. This provides
+         data required to configure LLCC so that clients can start using the
+         LLCC slices.
+
 config QCOM_MDT_LOADER
        tristate
        select QCOM_SCM
@@ -75,6 +92,16 @@ config QCOM_RMTFS_MEM
 
          Say y here if you intend to boot the modem remoteproc.
 
+config QCOM_RPMH
+       bool "Qualcomm RPM-Hardened (RPMH) Communication"
+       depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST
+       help
+         Support for communication with the hardened-RPM blocks in
+         Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
+         internal bus to transmit state requests for shared resources. A set
+         of hardware components aggregate requests for these resources and
+         help apply the aggregated state on the resource.
+
 config QCOM_SMEM
        tristate "Qualcomm Shared Memory Manager (SMEM)"
        depends on ARCH_QCOM
index 19dcf957cb3a052bb339d0559223adc45b05c43d..f25b54cd6cf83d5771680b6c7db94ca12cd47722 100644 (file)
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
+CFLAGS_rpmh-rsc.o := -I$(src)
 obj-$(CONFIG_QCOM_GENI_SE) +=  qcom-geni-se.o
 obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
 obj-$(CONFIG_QCOM_GLINK_SSR) +=        glink_ssr.o
@@ -8,6 +9,9 @@ obj-$(CONFIG_QCOM_PM)   +=      spm.o
 obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
 qmi_helpers-y  += qmi_encdec.o qmi_interface.o
 obj-$(CONFIG_QCOM_RMTFS_MEM)   += rmtfs_mem.o
+obj-$(CONFIG_QCOM_RPMH)                += qcom_rpmh.o
+qcom_rpmh-y                    += rpmh-rsc.o
+qcom_rpmh-y                    += rpmh.o
 obj-$(CONFIG_QCOM_SMD_RPM)     += smd-rpm.o
 obj-$(CONFIG_QCOM_SMEM) +=     smem.o
 obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
@@ -15,3 +19,5 @@ obj-$(CONFIG_QCOM_SMP2P)      += smp2p.o
 obj-$(CONFIG_QCOM_SMSM)        += smsm.o
 obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
 obj-$(CONFIG_QCOM_APR) += apr.o
+obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
+obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c
new file mode 100644 (file)
index 0000000..2e1e4f0
--- /dev/null
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+
+/*
+ * SCT(System Cache Table) entry contains of the following members:
+ * usecase_id: Unique id for the client's use case
+ * slice_id: llcc slice id for each client
+ * max_cap: The maximum capacity of the cache slice provided in KB
+ * priority: Priority of the client used to select victim line for replacement
+ * fixed_size: Boolean indicating if the slice has a fixed capacity
+ * bonus_ways: Bonus ways are additional ways to be used for any slice,
+ *             if client ends up using more than reserved cache ways. Bonus
+ *             ways are allocated only if they are not reserved for some
+ *             other client.
+ * res_ways: Reserved ways for the cache slice, the reserved ways cannot
+ *             be used by any other client than the one its assigned to.
+ * cache_mode: Each slice operates as a cache, this controls the mode of the
+ *             slice: normal or TCM(Tightly Coupled Memory)
+ * probe_target_ways: Determines what ways to probe for access hit. When
+ *                    configured to 1 only bonus and reserved ways are probed.
+ *                    When configured to 0 all ways in llcc are probed.
+ * dis_cap_alloc: Disable capacity based allocation for a client
+ * retain_on_pc: If this bit is set and client has maintained active vote
+ *               then the ways assigned to this client are not flushed on power
+ *               collapse.
+ * activate_on_init: Activate the slice immediately after the SCT is programmed
+ */
+#define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \
+       {                                       \
+               .usecase_id = uid,              \
+               .slice_id = sid,                \
+               .max_cap = mc,                  \
+               .priority = p,                  \
+               .fixed_size = fs,               \
+               .bonus_ways = bway,             \
+               .res_ways = rway,               \
+               .cache_mode = cmod,             \
+               .probe_target_ways = ptw,       \
+               .dis_cap_alloc = dca,           \
+               .retain_on_pc = rp,             \
+               .activate_on_init = a,          \
+       }
+
+static struct llcc_slice_config sdm845_data[] =  {
+       SCT_ENTRY(LLCC_CPUSS,    1,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 1),
+       SCT_ENTRY(LLCC_VIDSC0,   2,  512,  2, 1, 0x0,   0x0f0, 0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_VIDSC1,   3,  512,  2, 1, 0x0,   0x0f0, 0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_ROTATOR,  4,  563,  2, 1, 0x0,   0x00e, 2, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_VOICE,    5,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_AUDIO,    6,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_MDMHPGRW, 7,  1024, 2, 0, 0xfc,  0xf00, 0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_MDM,      8,  2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_CMPT,     10, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_GPUHTW,   11, 512,  1, 1, 0xc,   0x0,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_GPU,      12, 2304, 1, 0, 0xff0, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_MMUHWT,   13, 256,  2, 0, 0x0,   0x1,   0, 0, 1, 0, 1),
+       SCT_ENTRY(LLCC_CMPTDMA,  15, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_DISP,     16, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_VIDFW,    17, 2816, 1, 0, 0xffc, 0x2,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_MDMHPFX,  20, 1024, 2, 1, 0x0,   0xf00, 0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_MDMPNG,   21, 1024, 0, 1, 0x1e,  0x0,   0, 0, 1, 1, 0),
+       SCT_ENTRY(LLCC_AUDHW,    22, 1024, 1, 1, 0xffc, 0x2,   0, 0, 1, 1, 0),
+};
+
+static int sdm845_qcom_llcc_probe(struct platform_device *pdev)
+{
+       return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data));
+}
+
+static const struct of_device_id sdm845_qcom_llcc_of_match[] = {
+       { .compatible = "qcom,sdm845-llcc", },
+       { }
+};
+
+static struct platform_driver sdm845_qcom_llcc_driver = {
+       .driver = {
+               .name = "sdm845-llcc",
+               .of_match_table = sdm845_qcom_llcc_of_match,
+       },
+       .probe = sdm845_qcom_llcc_probe,
+};
+module_platform_driver(sdm845_qcom_llcc_driver);
+
+MODULE_DESCRIPTION("QCOM sdm845 LLCC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c
new file mode 100644 (file)
index 0000000..54063a3
--- /dev/null
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+
+#define ACTIVATE                      BIT(0)
+#define DEACTIVATE                    BIT(1)
+#define ACT_CTRL_OPCODE_ACTIVATE      BIT(0)
+#define ACT_CTRL_OPCODE_DEACTIVATE    BIT(1)
+#define ACT_CTRL_ACT_TRIG             BIT(0)
+#define ACT_CTRL_OPCODE_SHIFT         0x01
+#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x02
+#define ATTR1_FIXED_SIZE_SHIFT        0x03
+#define ATTR1_PRIORITY_SHIFT          0x04
+#define ATTR1_MAX_CAP_SHIFT           0x10
+#define ATTR0_RES_WAYS_MASK           GENMASK(11, 0)
+#define ATTR0_BONUS_WAYS_MASK         GENMASK(27, 16)
+#define ATTR0_BONUS_WAYS_SHIFT        0x10
+#define LLCC_STATUS_READ_DELAY        100
+
+#define CACHE_LINE_SIZE_SHIFT         6
+
+#define LLCC_COMMON_STATUS0           0x0003000c
+#define LLCC_LB_CNT_MASK              GENMASK(31, 28)
+#define LLCC_LB_CNT_SHIFT             28
+
+#define MAX_CAP_TO_BYTES(n)           (n * SZ_1K)
+#define LLCC_TRP_ACT_CTRLn(n)         (n * SZ_4K)
+#define LLCC_TRP_STATUSn(n)           (4 + n * SZ_4K)
+#define LLCC_TRP_ATTR0_CFGn(n)        (0x21000 + SZ_8 * n)
+#define LLCC_TRP_ATTR1_CFGn(n)        (0x21004 + SZ_8 * n)
+
+#define BANK_OFFSET_STRIDE           0x80000
+
+static struct llcc_drv_data *drv_data;
+
+static const struct regmap_config llcc_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .fast_io = true,
+};
+
+/**
+ * llcc_slice_getd - get llcc slice descriptor
+ * @uid: usecase_id for the client
+ *
+ * A pointer to llcc slice descriptor will be returned on success and
+ * and error pointer is returned on failure
+ */
+struct llcc_slice_desc *llcc_slice_getd(u32 uid)
+{
+       const struct llcc_slice_config *cfg;
+       struct llcc_slice_desc *desc;
+       u32 sz, count;
+
+       cfg = drv_data->cfg;
+       sz = drv_data->cfg_size;
+
+       for (count = 0; cfg && count < sz; count++, cfg++)
+               if (cfg->usecase_id == uid)
+                       break;
+
+       if (count == sz || !cfg)
+               return ERR_PTR(-ENODEV);
+
+       desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+       if (!desc)
+               return ERR_PTR(-ENOMEM);
+
+       desc->slice_id = cfg->slice_id;
+       desc->slice_size = cfg->max_cap;
+
+       return desc;
+}
+EXPORT_SYMBOL_GPL(llcc_slice_getd);
+
+/**
+ * llcc_slice_putd - llcc slice descritpor
+ * @desc: Pointer to llcc slice descriptor
+ */
+void llcc_slice_putd(struct llcc_slice_desc *desc)
+{
+       kfree(desc);
+}
+EXPORT_SYMBOL_GPL(llcc_slice_putd);
+
+static int llcc_update_act_ctrl(u32 sid,
+                               u32 act_ctrl_reg_val, u32 status)
+{
+       u32 act_ctrl_reg;
+       u32 status_reg;
+       u32 slice_status;
+       int ret;
+
+       act_ctrl_reg = drv_data->bcast_off + LLCC_TRP_ACT_CTRLn(sid);
+       status_reg = drv_data->bcast_off + LLCC_TRP_STATUSn(sid);
+
+       /* Set the ACTIVE trigger */
+       act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
+       ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val);
+       if (ret)
+               return ret;
+
+       /* Clear the ACTIVE trigger */
+       act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
+       ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val);
+       if (ret)
+               return ret;
+
+       ret = regmap_read_poll_timeout(drv_data->regmap, status_reg,
+                                     slice_status, !(slice_status & status),
+                                     0, LLCC_STATUS_READ_DELAY);
+       return ret;
+}
+
+/**
+ * llcc_slice_activate - Activate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ *
+ * A value of zero will be returned on success and a negative errno will
+ * be returned in error cases
+ */
+int llcc_slice_activate(struct llcc_slice_desc *desc)
+{
+       int ret;
+       u32 act_ctrl_val;
+
+       mutex_lock(&drv_data->lock);
+       if (test_bit(desc->slice_id, drv_data->bitmap)) {
+               mutex_unlock(&drv_data->lock);
+               return 0;
+       }
+
+       act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
+
+       ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
+                                 DEACTIVATE);
+       if (ret) {
+               mutex_unlock(&drv_data->lock);
+               return ret;
+       }
+
+       __set_bit(desc->slice_id, drv_data->bitmap);
+       mutex_unlock(&drv_data->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(llcc_slice_activate);
+
+/**
+ * llcc_slice_deactivate - Deactivate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ *
+ * A value of zero will be returned on success and a negative errno will
+ * be returned in error cases
+ */
+int llcc_slice_deactivate(struct llcc_slice_desc *desc)
+{
+       u32 act_ctrl_val;
+       int ret;
+
+       mutex_lock(&drv_data->lock);
+       if (!test_bit(desc->slice_id, drv_data->bitmap)) {
+               mutex_unlock(&drv_data->lock);
+               return 0;
+       }
+       act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
+
+       ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
+                                 ACTIVATE);
+       if (ret) {
+               mutex_unlock(&drv_data->lock);
+               return ret;
+       }
+
+       __clear_bit(desc->slice_id, drv_data->bitmap);
+       mutex_unlock(&drv_data->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(llcc_slice_deactivate);
+
+/**
+ * llcc_get_slice_id - return the slice id
+ * @desc: Pointer to llcc slice descriptor
+ */
+int llcc_get_slice_id(struct llcc_slice_desc *desc)
+{
+       return desc->slice_id;
+}
+EXPORT_SYMBOL_GPL(llcc_get_slice_id);
+
+/**
+ * llcc_get_slice_size - return the slice id
+ * @desc: Pointer to llcc slice descriptor
+ */
+size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
+{
+       return desc->slice_size;
+}
+EXPORT_SYMBOL_GPL(llcc_get_slice_size);
+
+static int qcom_llcc_cfg_program(struct platform_device *pdev)
+{
+       int i;
+       u32 attr1_cfg;
+       u32 attr0_cfg;
+       u32 attr1_val;
+       u32 attr0_val;
+       u32 max_cap_cacheline;
+       u32 sz;
+       int ret;
+       const struct llcc_slice_config *llcc_table;
+       struct llcc_slice_desc desc;
+       u32 bcast_off = drv_data->bcast_off;
+
+       sz = drv_data->cfg_size;
+       llcc_table = drv_data->cfg;
+
+       for (i = 0; i < sz; i++) {
+               attr1_cfg = bcast_off +
+                               LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
+               attr0_cfg = bcast_off +
+                               LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
+
+               attr1_val = llcc_table[i].cache_mode;
+               attr1_val |= llcc_table[i].probe_target_ways <<
+                               ATTR1_PROBE_TARGET_WAYS_SHIFT;
+               attr1_val |= llcc_table[i].fixed_size <<
+                               ATTR1_FIXED_SIZE_SHIFT;
+               attr1_val |= llcc_table[i].priority <<
+                               ATTR1_PRIORITY_SHIFT;
+
+               max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap);
+
+               /* LLCC instances can vary for each target.
+                * The SW writes to broadcast register which gets propagated
+                * to each llcc instace (llcc0,.. llccN).
+                * Since the size of the memory is divided equally amongst the
+                * llcc instances, we need to configure the max cap accordingly.
+                */
+               max_cap_cacheline = max_cap_cacheline / drv_data->num_banks;
+               max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
+               attr1_val |= max_cap_cacheline << ATTR1_MAX_CAP_SHIFT;
+
+               attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
+               attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT;
+
+               ret = regmap_write(drv_data->regmap, attr1_cfg, attr1_val);
+               if (ret)
+                       return ret;
+               ret = regmap_write(drv_data->regmap, attr0_cfg, attr0_val);
+               if (ret)
+                       return ret;
+               if (llcc_table[i].activate_on_init) {
+                       desc.slice_id = llcc_table[i].slice_id;
+                       ret = llcc_slice_activate(&desc);
+               }
+       }
+       return ret;
+}
+
+int qcom_llcc_probe(struct platform_device *pdev,
+                     const struct llcc_slice_config *llcc_cfg, u32 sz)
+{
+       u32 num_banks;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       void __iomem *base;
+       int ret, i;
+
+       drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       drv_data->regmap = devm_regmap_init_mmio(dev, base,
+                                       &llcc_regmap_config);
+       if (IS_ERR(drv_data->regmap))
+               return PTR_ERR(drv_data->regmap);
+
+       ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
+                                               &num_banks);
+       if (ret)
+               return ret;
+
+       num_banks &= LLCC_LB_CNT_MASK;
+       num_banks >>= LLCC_LB_CNT_SHIFT;
+       drv_data->num_banks = num_banks;
+
+       for (i = 0; i < sz; i++)
+               if (llcc_cfg[i].slice_id > drv_data->max_slices)
+                       drv_data->max_slices = llcc_cfg[i].slice_id;
+
+       drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32),
+                                                       GFP_KERNEL);
+       if (!drv_data->offsets)
+               return -ENOMEM;
+
+       for (i = 0; i < num_banks; i++)
+               drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
+
+       drv_data->bcast_off = num_banks * BANK_OFFSET_STRIDE;
+
+       drv_data->bitmap = devm_kcalloc(dev,
+       BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
+                                               GFP_KERNEL);
+       if (!drv_data->bitmap)
+               return -ENOMEM;
+
+       drv_data->cfg = llcc_cfg;
+       drv_data->cfg_size = sz;
+       mutex_init(&drv_data->lock);
+       platform_set_drvdata(pdev, drv_data);
+
+       return qcom_llcc_cfg_program(pdev);
+}
+EXPORT_SYMBOL_GPL(qcom_llcc_probe);
+
+MODULE_LICENSE("GPL v2");
index c8999e38b005b5d85616e6a4073347fa57c0659a..8a3678c2e83cf609daddfdcd7c50e9de0a671ad2 100644 (file)
@@ -184,6 +184,7 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
        device_initialize(&rmtfs_mem->dev);
        rmtfs_mem->dev.parent = &pdev->dev;
        rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
+       rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
 
        rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr,
                                        rmtfs_mem->size, MEMREMAP_WC);
@@ -206,8 +207,6 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
                goto put_device;
        }
 
-       rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
-
        ret = of_property_read_u32(node, "qcom,vmid", &vmid);
        if (ret < 0 && ret != -EINVAL) {
                dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h
new file mode 100644 (file)
index 0000000..a7bbbb6
--- /dev/null
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+
+#ifndef __RPM_INTERNAL_H__
+#define __RPM_INTERNAL_H__
+
+#include <linux/bitmap.h>
+#include <soc/qcom/tcs.h>
+
+#define TCS_TYPE_NR                    4
+#define MAX_CMDS_PER_TCS               16
+#define MAX_TCS_PER_TYPE               3
+#define MAX_TCS_NR                     (MAX_TCS_PER_TYPE * TCS_TYPE_NR)
+#define MAX_TCS_SLOTS                  (MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE)
+
+struct rsc_drv;
+
+/**
+ * struct tcs_group: group of Trigger Command Sets (TCS) to send state requests
+ * to the controller
+ *
+ * @drv:       the controller
+ * @type:      type of the TCS in this group - active, sleep, wake
+ * @mask:      mask of the TCSes relative to all the TCSes in the RSC
+ * @offset:    start of the TCS group relative to the TCSes in the RSC
+ * @num_tcs:   number of TCSes in this type
+ * @ncpt:      number of commands in each TCS
+ * @lock:      lock for synchronizing this TCS writes
+ * @req:       requests that are sent from the TCS
+ * @cmd_cache: flattened cache of cmds in sleep/wake TCS
+ * @slots:     indicates which of @cmd_addr are occupied
+ */
+struct tcs_group {
+       struct rsc_drv *drv;
+       int type;
+       u32 mask;
+       u32 offset;
+       int num_tcs;
+       int ncpt;
+       spinlock_t lock;
+       const struct tcs_request *req[MAX_TCS_PER_TYPE];
+       u32 *cmd_cache;
+       DECLARE_BITMAP(slots, MAX_TCS_SLOTS);
+};
+
+/**
+ * struct rpmh_request: the message to be sent to rpmh-rsc
+ *
+ * @msg: the request
+ * @cmd: the payload that will be part of the @msg
+ * @completion: triggered when request is done
+ * @dev: the device making the request
+ * @err: err return from the controller
+ * @needs_free: check to free dynamically allocated request object
+ */
+struct rpmh_request {
+       struct tcs_request msg;
+       struct tcs_cmd cmd[MAX_RPMH_PAYLOAD];
+       struct completion *completion;
+       const struct device *dev;
+       int err;
+       bool needs_free;
+};
+
+/**
+ * struct rpmh_ctrlr: our representation of the controller
+ *
+ * @cache: the list of cached requests
+ * @cache_lock: synchronize access to the cache data
+ * @dirty: was the cache updated since flush
+ * @batch_cache: Cache sleep and wake requests sent as batch
+ */
+struct rpmh_ctrlr {
+       struct list_head cache;
+       spinlock_t cache_lock;
+       bool dirty;
+       struct list_head batch_cache;
+};
+
+/**
+ * struct rsc_drv: the Direct Resource Voter (DRV) of the
+ * Resource State Coordinator controller (RSC)
+ *
+ * @name:       controller identifier
+ * @tcs_base:   start address of the TCS registers in this controller
+ * @id:         instance id in the controller (Direct Resource Voter)
+ * @num_tcs:    number of TCSes in this DRV
+ * @tcs:        TCS groups
+ * @tcs_in_use: s/w state of the TCS
+ * @lock:       synchronize state of the controller
+ * @client:     handle to the DRV's client.
+ */
+struct rsc_drv {
+       const char *name;
+       void __iomem *tcs_base;
+       int id;
+       int num_tcs;
+       struct tcs_group tcs[TCS_TYPE_NR];
+       DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR);
+       spinlock_t lock;
+       struct rpmh_ctrlr client;
+};
+
+int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg);
+int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv,
+                            const struct tcs_request *msg);
+int rpmh_rsc_invalidate(struct rsc_drv *drv);
+
+void rpmh_tx_done(const struct tcs_request *msg, int r);
+
+#endif /* __RPM_INTERNAL_H__ */
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
new file mode 100644 (file)
index 0000000..ee75da6
--- /dev/null
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/tcs.h>
+#include <dt-bindings/soc/qcom,rpmh-rsc.h>
+
+#include "rpmh-internal.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace-rpmh.h"
+
+#define RSC_DRV_TCS_OFFSET             672
+#define RSC_DRV_CMD_OFFSET             20
+
+/* DRV Configuration Information Register */
+#define DRV_PRNT_CHLD_CONFIG           0x0C
+#define DRV_NUM_TCS_MASK               0x3F
+#define DRV_NUM_TCS_SHIFT              6
+#define DRV_NCPT_MASK                  0x1F
+#define DRV_NCPT_SHIFT                 27
+
+/* Register offsets */
+#define RSC_DRV_IRQ_ENABLE             0x00
+#define RSC_DRV_IRQ_STATUS             0x04
+#define RSC_DRV_IRQ_CLEAR              0x08
+#define RSC_DRV_CMD_WAIT_FOR_CMPL      0x10
+#define RSC_DRV_CONTROL                        0x14
+#define RSC_DRV_STATUS                 0x18
+#define RSC_DRV_CMD_ENABLE             0x1C
+#define RSC_DRV_CMD_MSGID              0x30
+#define RSC_DRV_CMD_ADDR               0x34
+#define RSC_DRV_CMD_DATA               0x38
+#define RSC_DRV_CMD_STATUS             0x3C
+#define RSC_DRV_CMD_RESP_DATA          0x40
+
+#define TCS_AMC_MODE_ENABLE            BIT(16)
+#define TCS_AMC_MODE_TRIGGER           BIT(24)
+
+/* TCS CMD register bit mask */
+#define CMD_MSGID_LEN                  8
+#define CMD_MSGID_RESP_REQ             BIT(8)
+#define CMD_MSGID_WRITE                        BIT(16)
+#define CMD_STATUS_ISSUED              BIT(8)
+#define CMD_STATUS_COMPL               BIT(16)
+
+static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id)
+{
+       return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id +
+                            RSC_DRV_CMD_OFFSET * cmd_id);
+}
+
+static void write_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id,
+                         u32 data)
+{
+       writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id +
+                      RSC_DRV_CMD_OFFSET * cmd_id);
+}
+
+static void write_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, u32 data)
+{
+       writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id);
+}
+
+static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id,
+                              u32 data)
+{
+       writel(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id);
+       for (;;) {
+               if (data == readl(drv->tcs_base + reg +
+                                 RSC_DRV_TCS_OFFSET * tcs_id))
+                       break;
+               udelay(1);
+       }
+}
+
+static bool tcs_is_free(struct rsc_drv *drv, int tcs_id)
+{
+       return !test_bit(tcs_id, drv->tcs_in_use) &&
+              read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id, 0);
+}
+
+static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type)
+{
+       return &drv->tcs[type];
+}
+
+static int tcs_invalidate(struct rsc_drv *drv, int type)
+{
+       int m;
+       struct tcs_group *tcs;
+
+       tcs = get_tcs_of_type(drv, type);
+
+       spin_lock(&tcs->lock);
+       if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) {
+               spin_unlock(&tcs->lock);
+               return 0;
+       }
+
+       for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) {
+               if (!tcs_is_free(drv, m)) {
+                       spin_unlock(&tcs->lock);
+                       return -EAGAIN;
+               }
+               write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0);
+       }
+       bitmap_zero(tcs->slots, MAX_TCS_SLOTS);
+       spin_unlock(&tcs->lock);
+
+       return 0;
+}
+
+/**
+ * rpmh_rsc_invalidate - Invalidate sleep and wake TCSes
+ *
+ * @drv: the RSC controller
+ */
+int rpmh_rsc_invalidate(struct rsc_drv *drv)
+{
+       int ret;
+
+       ret = tcs_invalidate(drv, SLEEP_TCS);
+       if (!ret)
+               ret = tcs_invalidate(drv, WAKE_TCS);
+
+       return ret;
+}
+
+static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
+                                        const struct tcs_request *msg)
+{
+       int type, ret;
+       struct tcs_group *tcs;
+
+       switch (msg->state) {
+       case RPMH_ACTIVE_ONLY_STATE:
+               type = ACTIVE_TCS;
+               break;
+       case RPMH_WAKE_ONLY_STATE:
+               type = WAKE_TCS;
+               break;
+       case RPMH_SLEEP_STATE:
+               type = SLEEP_TCS;
+               break;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
+
+       /*
+        * If we are making an active request on a RSC that does not have a
+        * dedicated TCS for active state use, then re-purpose a wake TCS to
+        * send active votes.
+        * NOTE: The driver must be aware that this RSC does not have a
+        * dedicated AMC, and therefore would invalidate the sleep and wake
+        * TCSes before making an active state request.
+        */
+       tcs = get_tcs_of_type(drv, type);
+       if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) {
+               tcs = get_tcs_of_type(drv, WAKE_TCS);
+               if (tcs->num_tcs) {
+                       ret = rpmh_rsc_invalidate(drv);
+                       if (ret)
+                               return ERR_PTR(ret);
+               }
+       }
+
+       return tcs;
+}
+
+static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv,
+                                                 int tcs_id)
+{
+       struct tcs_group *tcs;
+       int i;
+
+       for (i = 0; i < TCS_TYPE_NR; i++) {
+               tcs = &drv->tcs[i];
+               if (tcs->mask & BIT(tcs_id))
+                       return tcs->req[tcs_id - tcs->offset];
+       }
+
+       return NULL;
+}
+
+/**
+ * tcs_tx_done: TX Done interrupt handler
+ */
+static irqreturn_t tcs_tx_done(int irq, void *p)
+{
+       struct rsc_drv *drv = p;
+       int i, j, err = 0;
+       unsigned long irq_status;
+       const struct tcs_request *req;
+       struct tcs_cmd *cmd;
+
+       irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0, 0);
+
+       for_each_set_bit(i, &irq_status, BITS_PER_LONG) {
+               req = get_req_from_tcs(drv, i);
+               if (!req) {
+                       WARN_ON(1);
+                       goto skip;
+               }
+
+               err = 0;
+               for (j = 0; j < req->num_cmds; j++) {
+                       u32 sts;
+
+                       cmd = &req->cmds[j];
+                       sts = read_tcs_reg(drv, RSC_DRV_CMD_STATUS, i, j);
+                       if (!(sts & CMD_STATUS_ISSUED) ||
+                          ((req->wait_for_compl || cmd->wait) &&
+                          !(sts & CMD_STATUS_COMPL))) {
+                               pr_err("Incomplete request: %s: addr=%#x data=%#x",
+                                      drv->name, cmd->addr, cmd->data);
+                               err = -EIO;
+                       }
+               }
+
+               trace_rpmh_tx_done(drv, i, req, err);
+skip:
+               /* Reclaim the TCS */
+               write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0);
+               write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i));
+               spin_lock(&drv->lock);
+               clear_bit(i, drv->tcs_in_use);
+               spin_unlock(&drv->lock);
+               if (req)
+                       rpmh_tx_done(req, err);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id,
+                              const struct tcs_request *msg)
+{
+       u32 msgid, cmd_msgid;
+       u32 cmd_enable = 0;
+       u32 cmd_complete;
+       struct tcs_cmd *cmd;
+       int i, j;
+
+       cmd_msgid = CMD_MSGID_LEN;
+       cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0;
+       cmd_msgid |= CMD_MSGID_WRITE;
+
+       cmd_complete = read_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0);
+
+       for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) {
+               cmd = &msg->cmds[i];
+               cmd_enable |= BIT(j);
+               cmd_complete |= cmd->wait << j;
+               msgid = cmd_msgid;
+               msgid |= cmd->wait ? CMD_MSGID_RESP_REQ : 0;
+
+               write_tcs_cmd(drv, RSC_DRV_CMD_MSGID, tcs_id, j, msgid);
+               write_tcs_cmd(drv, RSC_DRV_CMD_ADDR, tcs_id, j, cmd->addr);
+               write_tcs_cmd(drv, RSC_DRV_CMD_DATA, tcs_id, j, cmd->data);
+               trace_rpmh_send_msg(drv, tcs_id, j, msgid, cmd);
+       }
+
+       write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, cmd_complete);
+       cmd_enable |= read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0);
+       write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, cmd_enable);
+}
+
+static void __tcs_trigger(struct rsc_drv *drv, int tcs_id)
+{
+       u32 enable;
+
+       /*
+        * HW req: Clear the DRV_CONTROL and enable TCS again
+        * While clearing ensure that the AMC mode trigger is cleared
+        * and then the mode enable is cleared.
+        */
+       enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id, 0);
+       enable &= ~TCS_AMC_MODE_TRIGGER;
+       write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable);
+       enable &= ~TCS_AMC_MODE_ENABLE;
+       write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable);
+
+       /* Enable the AMC mode on the TCS and then trigger the TCS */
+       enable = TCS_AMC_MODE_ENABLE;
+       write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable);
+       enable |= TCS_AMC_MODE_TRIGGER;
+       write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable);
+}
+
+static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs,
+                                 const struct tcs_request *msg)
+{
+       unsigned long curr_enabled;
+       u32 addr;
+       int i, j, k;
+       int tcs_id = tcs->offset;
+
+       for (i = 0; i < tcs->num_tcs; i++, tcs_id++) {
+               if (tcs_is_free(drv, tcs_id))
+                       continue;
+
+               curr_enabled = read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0);
+
+               for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) {
+                       addr = read_tcs_reg(drv, RSC_DRV_CMD_ADDR, tcs_id, j);
+                       for (k = 0; k < msg->num_cmds; k++) {
+                               if (addr == msg->cmds[k].addr)
+                                       return -EBUSY;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int find_free_tcs(struct tcs_group *tcs)
+{
+       int i;
+
+       for (i = 0; i < tcs->num_tcs; i++) {
+               if (tcs_is_free(tcs->drv, tcs->offset + i))
+                       return tcs->offset + i;
+       }
+
+       return -EBUSY;
+}
+
+static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+       struct tcs_group *tcs;
+       int tcs_id;
+       unsigned long flags;
+       int ret;
+
+       tcs = get_tcs_for_msg(drv, msg);
+       if (IS_ERR(tcs))
+               return PTR_ERR(tcs);
+
+       spin_lock_irqsave(&tcs->lock, flags);
+       spin_lock(&drv->lock);
+       /*
+        * The h/w does not like if we send a request to the same address,
+        * when one is already in-flight or being processed.
+        */
+       ret = check_for_req_inflight(drv, tcs, msg);
+       if (ret) {
+               spin_unlock(&drv->lock);
+               goto done_write;
+       }
+
+       tcs_id = find_free_tcs(tcs);
+       if (tcs_id < 0) {
+               ret = tcs_id;
+               spin_unlock(&drv->lock);
+               goto done_write;
+       }
+
+       tcs->req[tcs_id - tcs->offset] = msg;
+       set_bit(tcs_id, drv->tcs_in_use);
+       spin_unlock(&drv->lock);
+
+       __tcs_buffer_write(drv, tcs_id, 0, msg);
+       __tcs_trigger(drv, tcs_id);
+
+done_write:
+       spin_unlock_irqrestore(&tcs->lock, flags);
+       return ret;
+}
+
+/**
+ * rpmh_rsc_send_data: Validate the incoming message and write to the
+ * appropriate TCS block.
+ *
+ * @drv: the controller
+ * @msg: the data to be sent
+ *
+ * Return: 0 on success, -EINVAL on error.
+ * Note: This call blocks until a valid data is written to the TCS.
+ */
+int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+       int ret;
+
+       if (!msg || !msg->cmds || !msg->num_cmds ||
+           msg->num_cmds > MAX_RPMH_PAYLOAD) {
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       do {
+               ret = tcs_write(drv, msg);
+               if (ret == -EBUSY) {
+                       pr_info_ratelimited("TCS Busy, retrying RPMH message send: addr=%#x\n",
+                                           msg->cmds[0].addr);
+                       udelay(10);
+               }
+       } while (ret == -EBUSY);
+
+       return ret;
+}
+
+static int find_match(const struct tcs_group *tcs, const struct tcs_cmd *cmd,
+                     int len)
+{
+       int i, j;
+
+       /* Check for already cached commands */
+       for_each_set_bit(i, tcs->slots, MAX_TCS_SLOTS) {
+               if (tcs->cmd_cache[i] != cmd[0].addr)
+                       continue;
+               if (i + len >= tcs->num_tcs * tcs->ncpt)
+                       goto seq_err;
+               for (j = 0; j < len; j++) {
+                       if (tcs->cmd_cache[i + j] != cmd[j].addr)
+                               goto seq_err;
+               }
+               return i;
+       }
+
+       return -ENODATA;
+
+seq_err:
+       WARN(1, "Message does not match previous sequence.\n");
+       return -EINVAL;
+}
+
+static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg,
+                     int *tcs_id, int *cmd_id)
+{
+       int slot, offset;
+       int i = 0;
+
+       /* Find if we already have the msg in our TCS */
+       slot = find_match(tcs, msg->cmds, msg->num_cmds);
+       if (slot >= 0)
+               goto copy_data;
+
+       /* Do over, until we can fit the full payload in a TCS */
+       do {
+               slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS,
+                                                 i, msg->num_cmds, 0);
+               if (slot == tcs->num_tcs * tcs->ncpt)
+                       return -ENOMEM;
+               i += tcs->ncpt;
+       } while (slot + msg->num_cmds - 1 >= i);
+
+copy_data:
+       bitmap_set(tcs->slots, slot, msg->num_cmds);
+       /* Copy the addresses of the resources over to the slots */
+       for (i = 0; i < msg->num_cmds; i++)
+               tcs->cmd_cache[slot + i] = msg->cmds[i].addr;
+
+       offset = slot / tcs->ncpt;
+       *tcs_id = offset + tcs->offset;
+       *cmd_id = slot % tcs->ncpt;
+
+       return 0;
+}
+
+static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+       struct tcs_group *tcs;
+       int tcs_id = 0, cmd_id = 0;
+       unsigned long flags;
+       int ret;
+
+       tcs = get_tcs_for_msg(drv, msg);
+       if (IS_ERR(tcs))
+               return PTR_ERR(tcs);
+
+       spin_lock_irqsave(&tcs->lock, flags);
+       /* find the TCS id and the command in the TCS to write to */
+       ret = find_slots(tcs, msg, &tcs_id, &cmd_id);
+       if (!ret)
+               __tcs_buffer_write(drv, tcs_id, cmd_id, msg);
+       spin_unlock_irqrestore(&tcs->lock, flags);
+
+       return ret;
+}
+
+/**
+ * rpmh_rsc_write_ctrl_data: Write request to the controller
+ *
+ * @drv: the controller
+ * @msg: the data to be written to the controller
+ *
+ * There is no response returned for writing the request to the controller.
+ */
+int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg)
+{
+       if (!msg || !msg->cmds || !msg->num_cmds ||
+           msg->num_cmds > MAX_RPMH_PAYLOAD) {
+               pr_err("Payload error\n");
+               return -EINVAL;
+       }
+
+       /* Data sent to this API will not be sent immediately */
+       if (msg->state == RPMH_ACTIVE_ONLY_STATE)
+               return -EINVAL;
+
+       return tcs_ctrl_write(drv, msg);
+}
+
+static int rpmh_probe_tcs_config(struct platform_device *pdev,
+                                struct rsc_drv *drv)
+{
+       struct tcs_type_config {
+               u32 type;
+               u32 n;
+       } tcs_cfg[TCS_TYPE_NR] = { { 0 } };
+       struct device_node *dn = pdev->dev.of_node;
+       u32 config, max_tcs, ncpt, offset;
+       int i, ret, n, st = 0;
+       struct tcs_group *tcs;
+       struct resource *res;
+       void __iomem *base;
+       char drv_id[10] = {0};
+
+       snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id);
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       ret = of_property_read_u32(dn, "qcom,tcs-offset", &offset);
+       if (ret)
+               return ret;
+       drv->tcs_base = base + offset;
+
+       config = readl_relaxed(base + DRV_PRNT_CHLD_CONFIG);
+
+       max_tcs = config;
+       max_tcs &= DRV_NUM_TCS_MASK << (DRV_NUM_TCS_SHIFT * drv->id);
+       max_tcs = max_tcs >> (DRV_NUM_TCS_SHIFT * drv->id);
+
+       ncpt = config & (DRV_NCPT_MASK << DRV_NCPT_SHIFT);
+       ncpt = ncpt >> DRV_NCPT_SHIFT;
+
+       n = of_property_count_u32_elems(dn, "qcom,tcs-config");
+       if (n != 2 * TCS_TYPE_NR)
+               return -EINVAL;
+
+       for (i = 0; i < TCS_TYPE_NR; i++) {
+               ret = of_property_read_u32_index(dn, "qcom,tcs-config",
+                                                i * 2, &tcs_cfg[i].type);
+               if (ret)
+                       return ret;
+               if (tcs_cfg[i].type >= TCS_TYPE_NR)
+                       return -EINVAL;
+
+               ret = of_property_read_u32_index(dn, "qcom,tcs-config",
+                                                i * 2 + 1, &tcs_cfg[i].n);
+               if (ret)
+                       return ret;
+               if (tcs_cfg[i].n > MAX_TCS_PER_TYPE)
+                       return -EINVAL;
+       }
+
+       for (i = 0; i < TCS_TYPE_NR; i++) {
+               tcs = &drv->tcs[tcs_cfg[i].type];
+               if (tcs->drv)
+                       return -EINVAL;
+               tcs->drv = drv;
+               tcs->type = tcs_cfg[i].type;
+               tcs->num_tcs = tcs_cfg[i].n;
+               tcs->ncpt = ncpt;
+               spin_lock_init(&tcs->lock);
+
+               if (!tcs->num_tcs || tcs->type == CONTROL_TCS)
+                       continue;
+
+               if (st + tcs->num_tcs > max_tcs ||
+                   st + tcs->num_tcs >= BITS_PER_BYTE * sizeof(tcs->mask))
+                       return -EINVAL;
+
+               tcs->mask = ((1 << tcs->num_tcs) - 1) << st;
+               tcs->offset = st;
+               st += tcs->num_tcs;
+
+               /*
+                * Allocate memory to cache sleep and wake requests to
+                * avoid reading TCS register memory.
+                */
+               if (tcs->type == ACTIVE_TCS)
+                       continue;
+
+               tcs->cmd_cache = devm_kcalloc(&pdev->dev,
+                                             tcs->num_tcs * ncpt, sizeof(u32),
+                                             GFP_KERNEL);
+               if (!tcs->cmd_cache)
+                       return -ENOMEM;
+       }
+
+       drv->num_tcs = st;
+
+       return 0;
+}
+
+static int rpmh_rsc_probe(struct platform_device *pdev)
+{
+       struct device_node *dn = pdev->dev.of_node;
+       struct rsc_drv *drv;
+       int ret, irq;
+
+       /*
+        * Even though RPMh doesn't directly use cmd-db, all of its children
+        * do. To avoid adding this check to our children we'll do it now.
+        */
+       ret = cmd_db_ready();
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "Command DB not available (%d)\n",
+                                                                       ret);
+               return ret;
+       }
+
+       drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+       if (!drv)
+               return -ENOMEM;
+
+       ret = of_property_read_u32(dn, "qcom,drv-id", &drv->id);
+       if (ret)
+               return ret;
+
+       drv->name = of_get_property(dn, "label", NULL);
+       if (!drv->name)
+               drv->name = dev_name(&pdev->dev);
+
+       ret = rpmh_probe_tcs_config(pdev, drv);
+       if (ret)
+               return ret;
+
+       spin_lock_init(&drv->lock);
+       bitmap_zero(drv->tcs_in_use, MAX_TCS_NR);
+
+       irq = platform_get_irq(pdev, drv->id);
+       if (irq < 0)
+               return irq;
+
+       ret = devm_request_irq(&pdev->dev, irq, tcs_tx_done,
+                              IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND,
+                              drv->name, drv);
+       if (ret)
+               return ret;
+
+       /* Enable the active TCS to send requests immediately */
+       write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, drv->tcs[ACTIVE_TCS].mask);
+
+       spin_lock_init(&drv->client.cache_lock);
+       INIT_LIST_HEAD(&drv->client.cache);
+       INIT_LIST_HEAD(&drv->client.batch_cache);
+
+       dev_set_drvdata(&pdev->dev, drv);
+
+       return devm_of_platform_populate(&pdev->dev);
+}
+
+static const struct of_device_id rpmh_drv_match[] = {
+       { .compatible = "qcom,rpmh-rsc", },
+       { }
+};
+
+static struct platform_driver rpmh_driver = {
+       .probe = rpmh_rsc_probe,
+       .driver = {
+                 .name = "rpmh",
+                 .of_match_table = rpmh_drv_match,
+       },
+};
+
+static int __init rpmh_driver_init(void)
+{
+       return platform_driver_register(&rpmh_driver);
+}
+arch_initcall(rpmh_driver_init);
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
new file mode 100644 (file)
index 0000000..c7beb68
--- /dev/null
@@ -0,0 +1,513 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <soc/qcom/rpmh.h>
+
+#include "rpmh-internal.h"
+
+#define RPMH_TIMEOUT_MS                        msecs_to_jiffies(10000)
+
+#define DEFINE_RPMH_MSG_ONSTACK(dev, s, q, name)       \
+       struct rpmh_request name = {                    \
+               .msg = {                                \
+                       .state = s,                     \
+                       .cmds = name.cmd,               \
+                       .num_cmds = 0,                  \
+                       .wait_for_compl = true,         \
+               },                                      \
+               .cmd = { { 0 } },                       \
+               .completion = q,                        \
+               .dev = dev,                             \
+               .needs_free = false,                            \
+       }
+
+#define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client)
+
+/**
+ * struct cache_req: the request object for caching
+ *
+ * @addr: the address of the resource
+ * @sleep_val: the sleep vote
+ * @wake_val: the wake vote
+ * @list: linked list obj
+ */
+struct cache_req {
+       u32 addr;
+       u32 sleep_val;
+       u32 wake_val;
+       struct list_head list;
+};
+
+/**
+ * struct batch_cache_req - An entry in our batch catch
+ *
+ * @list: linked list obj
+ * @count: number of messages
+ * @rpm_msgs: the messages
+ */
+
+struct batch_cache_req {
+       struct list_head list;
+       int count;
+       struct rpmh_request rpm_msgs[];
+};
+
+static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev)
+{
+       struct rsc_drv *drv = dev_get_drvdata(dev->parent);
+
+       return &drv->client;
+}
+
+void rpmh_tx_done(const struct tcs_request *msg, int r)
+{
+       struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request,
+                                                   msg);
+       struct completion *compl = rpm_msg->completion;
+
+       rpm_msg->err = r;
+
+       if (r)
+               dev_err(rpm_msg->dev, "RPMH TX fail in msg addr=%#x, err=%d\n",
+                       rpm_msg->msg.cmds[0].addr, r);
+
+       if (!compl)
+               goto exit;
+
+       /* Signal the blocking thread we are done */
+       complete(compl);
+
+exit:
+       if (rpm_msg->needs_free)
+               kfree(rpm_msg);
+}
+
+static struct cache_req *__find_req(struct rpmh_ctrlr *ctrlr, u32 addr)
+{
+       struct cache_req *p, *req = NULL;
+
+       list_for_each_entry(p, &ctrlr->cache, list) {
+               if (p->addr == addr) {
+                       req = p;
+                       break;
+               }
+       }
+
+       return req;
+}
+
+static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr,
+                                          enum rpmh_state state,
+                                          struct tcs_cmd *cmd)
+{
+       struct cache_req *req;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctrlr->cache_lock, flags);
+       req = __find_req(ctrlr, cmd->addr);
+       if (req)
+               goto existing;
+
+       req = kzalloc(sizeof(*req), GFP_ATOMIC);
+       if (!req) {
+               req = ERR_PTR(-ENOMEM);
+               goto unlock;
+       }
+
+       req->addr = cmd->addr;
+       req->sleep_val = req->wake_val = UINT_MAX;
+       INIT_LIST_HEAD(&req->list);
+       list_add_tail(&req->list, &ctrlr->cache);
+
+existing:
+       switch (state) {
+       case RPMH_ACTIVE_ONLY_STATE:
+               if (req->sleep_val != UINT_MAX)
+                       req->wake_val = cmd->data;
+               break;
+       case RPMH_WAKE_ONLY_STATE:
+               req->wake_val = cmd->data;
+               break;
+       case RPMH_SLEEP_STATE:
+               req->sleep_val = cmd->data;
+               break;
+       default:
+               break;
+       }
+
+       ctrlr->dirty = true;
+unlock:
+       spin_unlock_irqrestore(&ctrlr->cache_lock, flags);
+
+       return req;
+}
+
+/**
+ * __rpmh_write: Cache and send the RPMH request
+ *
+ * @dev: The device making the request
+ * @state: Active/Sleep request type
+ * @rpm_msg: The data that needs to be sent (cmds).
+ *
+ * Cache the RPMH request and send if the state is ACTIVE_ONLY.
+ * SLEEP/WAKE_ONLY requests are not sent to the controller at
+ * this time. Use rpmh_flush() to send them to the controller.
+ */
+static int __rpmh_write(const struct device *dev, enum rpmh_state state,
+                       struct rpmh_request *rpm_msg)
+{
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+       int ret = -EINVAL;
+       struct cache_req *req;
+       int i;
+
+       rpm_msg->msg.state = state;
+
+       /* Cache the request in our store and link the payload */
+       for (i = 0; i < rpm_msg->msg.num_cmds; i++) {
+               req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]);
+               if (IS_ERR(req))
+                       return PTR_ERR(req);
+       }
+
+       rpm_msg->msg.state = state;
+
+       if (state == RPMH_ACTIVE_ONLY_STATE) {
+               WARN_ON(irqs_disabled());
+               ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg);
+       } else {
+               ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr),
+                               &rpm_msg->msg);
+               /* Clean up our call by spoofing tx_done */
+               rpmh_tx_done(&rpm_msg->msg, ret);
+       }
+
+       return ret;
+}
+
+static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state,
+               const struct tcs_cmd *cmd, u32 n)
+{
+       if (!cmd || !n || n > MAX_RPMH_PAYLOAD)
+               return -EINVAL;
+
+       memcpy(req->cmd, cmd, n * sizeof(*cmd));
+
+       req->msg.state = state;
+       req->msg.cmds = req->cmd;
+       req->msg.num_cmds = n;
+
+       return 0;
+}
+
+/**
+ * rpmh_write_async: Write a set of RPMH commands
+ *
+ * @dev: The device making the request
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The number of elements in payload
+ *
+ * Write a set of RPMH commands, the order of commands is maintained
+ * and will be sent as a single shot.
+ */
+int rpmh_write_async(const struct device *dev, enum rpmh_state state,
+                    const struct tcs_cmd *cmd, u32 n)
+{
+       struct rpmh_request *rpm_msg;
+       int ret;
+
+       rpm_msg = kzalloc(sizeof(*rpm_msg), GFP_ATOMIC);
+       if (!rpm_msg)
+               return -ENOMEM;
+       rpm_msg->needs_free = true;
+
+       ret = __fill_rpmh_msg(rpm_msg, state, cmd, n);
+       if (ret) {
+               kfree(rpm_msg);
+               return ret;
+       }
+
+       return __rpmh_write(dev, state, rpm_msg);
+}
+EXPORT_SYMBOL(rpmh_write_async);
+
+/**
+ * rpmh_write: Write a set of RPMH commands and block until response
+ *
+ * @rc: The RPMH handle got from rpmh_get_client
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The number of elements in @cmd
+ *
+ * May sleep. Do not call from atomic contexts.
+ */
+int rpmh_write(const struct device *dev, enum rpmh_state state,
+              const struct tcs_cmd *cmd, u32 n)
+{
+       DECLARE_COMPLETION_ONSTACK(compl);
+       DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg);
+       int ret;
+
+       if (!cmd || !n || n > MAX_RPMH_PAYLOAD)
+               return -EINVAL;
+
+       memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd));
+       rpm_msg.msg.num_cmds = n;
+
+       ret = __rpmh_write(dev, state, &rpm_msg);
+       if (ret)
+               return ret;
+
+       ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);
+       WARN_ON(!ret);
+       return (ret > 0) ? 0 : -ETIMEDOUT;
+}
+EXPORT_SYMBOL(rpmh_write);
+
+static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctrlr->cache_lock, flags);
+       list_add_tail(&req->list, &ctrlr->batch_cache);
+       spin_unlock_irqrestore(&ctrlr->cache_lock, flags);
+}
+
+static int flush_batch(struct rpmh_ctrlr *ctrlr)
+{
+       struct batch_cache_req *req;
+       const struct rpmh_request *rpm_msg;
+       unsigned long flags;
+       int ret = 0;
+       int i;
+
+       /* Send Sleep/Wake requests to the controller, expect no response */
+       spin_lock_irqsave(&ctrlr->cache_lock, flags);
+       list_for_each_entry(req, &ctrlr->batch_cache, list) {
+               for (i = 0; i < req->count; i++) {
+                       rpm_msg = req->rpm_msgs + i;
+                       ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr),
+                                                      &rpm_msg->msg);
+                       if (ret)
+                               break;
+               }
+       }
+       spin_unlock_irqrestore(&ctrlr->cache_lock, flags);
+
+       return ret;
+}
+
+static void invalidate_batch(struct rpmh_ctrlr *ctrlr)
+{
+       struct batch_cache_req *req, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctrlr->cache_lock, flags);
+       list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list)
+               kfree(req);
+       INIT_LIST_HEAD(&ctrlr->batch_cache);
+       spin_unlock_irqrestore(&ctrlr->cache_lock, flags);
+}
+
+/**
+ * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the
+ * batch to finish.
+ *
+ * @dev: the device making the request
+ * @state: Active/sleep set
+ * @cmd: The payload data
+ * @n: The array of count of elements in each batch, 0 terminated.
+ *
+ * Write a request to the RSC controller without caching. If the request
+ * state is ACTIVE, then the requests are treated as completion request
+ * and sent to the controller immediately. The function waits until all the
+ * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the
+ * request is sent as fire-n-forget and no ack is expected.
+ *
+ * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests.
+ */
+int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
+                    const struct tcs_cmd *cmd, u32 *n)
+{
+       struct batch_cache_req *req;
+       struct rpmh_request *rpm_msgs;
+       DECLARE_COMPLETION_ONSTACK(compl);
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+       unsigned long time_left;
+       int count = 0;
+       int ret, i, j;
+
+       if (!cmd || !n)
+               return -EINVAL;
+
+       while (n[count] > 0)
+               count++;
+       if (!count)
+               return -EINVAL;
+
+       req = kzalloc(sizeof(*req) + count * sizeof(req->rpm_msgs[0]),
+                     GFP_ATOMIC);
+       if (!req)
+               return -ENOMEM;
+       req->count = count;
+       rpm_msgs = req->rpm_msgs;
+
+       for (i = 0; i < count; i++) {
+               __fill_rpmh_msg(rpm_msgs + i, state, cmd, n[i]);
+               cmd += n[i];
+       }
+
+       if (state != RPMH_ACTIVE_ONLY_STATE) {
+               cache_batch(ctrlr, req);
+               return 0;
+       }
+
+       for (i = 0; i < count; i++) {
+               rpm_msgs[i].completion = &compl;
+               ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msgs[i].msg);
+               if (ret) {
+                       pr_err("Error(%d) sending RPMH message addr=%#x\n",
+                              ret, rpm_msgs[i].msg.cmds[0].addr);
+                       for (j = i; j < count; j++)
+                               rpmh_tx_done(&rpm_msgs[j].msg, ret);
+                       break;
+               }
+       }
+
+       time_left = RPMH_TIMEOUT_MS;
+       for (i = 0; i < count; i++) {
+               time_left = wait_for_completion_timeout(&compl, time_left);
+               if (!time_left) {
+                       /*
+                        * Better hope they never finish because they'll signal
+                        * the completion on our stack and that's bad once
+                        * we've returned from the function.
+                        */
+                       WARN_ON(1);
+                       ret = -ETIMEDOUT;
+                       goto exit;
+               }
+       }
+
+exit:
+       kfree(req);
+
+       return ret;
+}
+EXPORT_SYMBOL(rpmh_write_batch);
+
+static int is_req_valid(struct cache_req *req)
+{
+       return (req->sleep_val != UINT_MAX &&
+               req->wake_val != UINT_MAX &&
+               req->sleep_val != req->wake_val);
+}
+
+static int send_single(const struct device *dev, enum rpmh_state state,
+                      u32 addr, u32 data)
+{
+       DEFINE_RPMH_MSG_ONSTACK(dev, state, NULL, rpm_msg);
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+
+       /* Wake sets are always complete and sleep sets are not */
+       rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE);
+       rpm_msg.cmd[0].addr = addr;
+       rpm_msg.cmd[0].data = data;
+       rpm_msg.msg.num_cmds = 1;
+
+       return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg);
+}
+
+/**
+ * rpmh_flush: Flushes the buffered active and sleep sets to TCS
+ *
+ * @dev: The device making the request
+ *
+ * Return: -EBUSY if the controller is busy, probably waiting on a response
+ * to a RPMH request sent earlier.
+ *
+ * This function is always called from the sleep code from the last CPU
+ * that is powering down the entire system. Since no other RPMH API would be
+ * executing at this time, it is safe to run lockless.
+ */
+int rpmh_flush(const struct device *dev)
+{
+       struct cache_req *p;
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+       int ret;
+
+       if (!ctrlr->dirty) {
+               pr_debug("Skipping flush, TCS has latest data.\n");
+               return 0;
+       }
+
+       /* First flush the cached batch requests */
+       ret = flush_batch(ctrlr);
+       if (ret)
+               return ret;
+
+       /*
+        * Nobody else should be calling this function other than system PM,
+        * hence we can run without locks.
+        */
+       list_for_each_entry(p, &ctrlr->cache, list) {
+               if (!is_req_valid(p)) {
+                       pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x",
+                                __func__, p->addr, p->sleep_val, p->wake_val);
+                       continue;
+               }
+               ret = send_single(dev, RPMH_SLEEP_STATE, p->addr, p->sleep_val);
+               if (ret)
+                       return ret;
+               ret = send_single(dev, RPMH_WAKE_ONLY_STATE,
+                                 p->addr, p->wake_val);
+               if (ret)
+                       return ret;
+       }
+
+       ctrlr->dirty = false;
+
+       return 0;
+}
+EXPORT_SYMBOL(rpmh_flush);
+
+/**
+ * rpmh_invalidate: Invalidate all sleep and active sets
+ * sets.
+ *
+ * @dev: The device making the request
+ *
+ * Invalidate the sleep and active values in the TCS blocks.
+ */
+int rpmh_invalidate(const struct device *dev)
+{
+       struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
+       int ret;
+
+       invalidate_batch(ctrlr);
+       ctrlr->dirty = true;
+
+       do {
+               ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr));
+       } while (ret == -EAGAIN);
+
+       return ret;
+}
+EXPORT_SYMBOL(rpmh_invalidate);
index 70b2ee80d6bdfab8f0cb960143b83d127eee3ba2..bf4bd71ab53fd9a36460b7d1a1f52793ea998506 100644 (file)
@@ -364,11 +364,6 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
        end = phdr_to_last_uncached_entry(phdr);
        cached = phdr_to_last_cached_entry(phdr);
 
-       if (smem->global_partition) {
-               dev_err(smem->dev, "Already found the global partition\n");
-               return -EINVAL;
-       }
-
        while (hdr < end) {
                if (hdr->canary != SMEM_PRIVATE_CANARY)
                        goto bad_canary;
@@ -736,6 +731,11 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
        bool found = false;
        int i;
 
+       if (smem->global_partition) {
+               dev_err(smem->dev, "Already found the global partition\n");
+               return -EINVAL;
+       }
+
        ptable = qcom_smem_get_ptable(smem);
        if (IS_ERR(ptable))
                return PTR_ERR(ptable);
diff --git a/drivers/soc/qcom/trace-rpmh.h b/drivers/soc/qcom/trace-rpmh.h
new file mode 100644 (file)
index 0000000..feb0cb4
--- /dev/null
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#if !defined(_TRACE_RPMH_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RPMH_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM rpmh
+
+#include <linux/tracepoint.h>
+#include "rpmh-internal.h"
+
+TRACE_EVENT(rpmh_tx_done,
+
+       TP_PROTO(struct rsc_drv *d, int m, const struct tcs_request *r, int e),
+
+       TP_ARGS(d, m, r, e),
+
+       TP_STRUCT__entry(
+                        __string(name, d->name)
+                        __field(int, m)
+                        __field(u32, addr)
+                        __field(u32, data)
+                        __field(int, err)
+       ),
+
+       TP_fast_assign(
+                      __assign_str(name, d->name);
+                      __entry->m = m;
+                      __entry->addr = r->cmds[0].addr;
+                      __entry->data = r->cmds[0].data;
+                      __entry->err = e;
+       ),
+
+       TP_printk("%s: ack: tcs-m: %d addr: %#x data: %#x errno: %d",
+                 __get_str(name), __entry->m, __entry->addr, __entry->data,
+                 __entry->err)
+);
+
+TRACE_EVENT(rpmh_send_msg,
+
+       TP_PROTO(struct rsc_drv *d, int m, int n, u32 h,
+                const struct tcs_cmd *c),
+
+       TP_ARGS(d, m, n, h, c),
+
+       TP_STRUCT__entry(
+                        __string(name, d->name)
+                        __field(int, m)
+                        __field(int, n)
+                        __field(u32, hdr)
+                        __field(u32, addr)
+                        __field(u32, data)
+                        __field(bool, wait)
+       ),
+
+       TP_fast_assign(
+                      __assign_str(name, d->name);
+                      __entry->m = m;
+                      __entry->n = n;
+                      __entry->hdr = h;
+                      __entry->addr = c->addr;
+                      __entry->data = c->data;
+                      __entry->wait = c->wait;
+       ),
+
+       TP_printk("%s: send-msg: tcs(m): %d cmd(n): %d msgid: %#x addr: %#x data: %#x complete: %d",
+                 __get_str(name), __entry->m, __entry->n, __entry->hdr,
+                 __entry->addr, __entry->data, __entry->wait)
+);
+
+#endif /* _TRACE_RPMH_H */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace-rpmh
+
+#include <trace/define_trace.h>
index cf28631d71091417a032bc0308c2e93c19bf2097..d0baba1973d4c4d751b02af74a704482d61c3b7b 100644 (file)
@@ -1,10 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (C) 2014 Google, Inc
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
  * Device Tree binding constants for the Maxim 77802 PMIC regulators
  */
 
diff --git a/include/dt-bindings/regulator/qcom,rpmh-regulator.h b/include/dt-bindings/regulator/qcom,rpmh-regulator.h
new file mode 100644 (file)
index 0000000..86713dc
--- /dev/null
@@ -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 to specify modes for various RPMh regulator
+ * device tree properties (e.g. regulator-initial-mode).  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
diff --git a/include/dt-bindings/soc/qcom,rpmh-rsc.h b/include/dt-bindings/soc/qcom,rpmh-rsc.h
new file mode 100644 (file)
index 0000000..868f998
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DT_QCOM_RPMH_RSC_H__
+#define __DT_QCOM_RPMH_RSC_H__
+
+#define SLEEP_TCS      0
+#define WAKE_TCS       1
+#define ACTIVE_TCS     2
+#define CONTROL_TCS    3
+
+#endif /* __DT_QCOM_RPMH_RSC_H__ */
index 6d3b000be57e08d2fc83180096d4808eb2e01ae0..b42c301c0f909fc26f7de50d3cfe0c45105ce3d8 100644 (file)
@@ -1316,6 +1316,7 @@ extern const char *dev_driver_string(const struct device *dev);
 struct device_link *device_link_add(struct device *consumer,
                                    struct device *supplier, u32 flags);
 void device_link_del(struct device_link *link);
+void device_link_remove(void *consumer, struct device *supplier);
 
 #ifdef CONFIG_PRINTK
 
index b401b962afffd23574a7d1c7e25f1bb8514af186..5d65521260b3dd278505487da1b06b6679427f7b 100644 (file)
@@ -87,6 +87,10 @@ static inline int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr,
 static inline int
 qcom_scm_pas_auth_and_reset(u32 peripheral) { return -ENODEV; }
 static inline int qcom_scm_pas_shutdown(u32 peripheral) { return -ENODEV; }
+static inline int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
+                                     unsigned int *src,
+                                     struct qcom_scm_vmperm *newvm,
+                                     int dest_cnt) { return -ENODEV; }
 static inline void qcom_scm_cpu_power_down(u32 flags) {}
 static inline u32 qcom_scm_get_version(void) { return 0; }
 static inline u32
index fc2dc8df476fe30d1bfefb0394510af91b49efde..0fd8fbb74763e4be6cb80e9137921f9bb5339f37 100644 (file)
@@ -46,7 +46,7 @@ enum regulator_status {
 /**
  * struct regulator_linear_range - specify linear voltage ranges
  *
- * Specify a range of voltages for regulator_map_linar_range() and
+ * Specify a range of voltages for regulator_map_linear_range() and
  * regulator_list_linear_range().
  *
  * @min_uV:  Lowest voltage in range
@@ -220,7 +220,7 @@ struct regulator_ops {
        /* set regulator suspend operating mode (defined in consumer.h) */
        int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
 
-       int (*resume_early)(struct regulator_dev *rdev);
+       int (*resume)(struct regulator_dev *rdev);
 
        int (*set_pull_down) (struct regulator_dev *);
 };
index e0ccf46f66cf34ae44296de9410fa107270aff4e..cb5aecd40f07d95478141af180d071e43f53af6e 100644 (file)
 #define PFUZE3000_VLDO3                11
 #define PFUZE3000_VLDO4                12
 
+#define PFUZE3001_SW1          0
+#define PFUZE3001_SW2          1
+#define PFUZE3001_SW3          2
+#define PFUZE3001_VSNVS                3
+#define PFUZE3001_VLDO1                4
+#define PFUZE3001_VLDO2                5
+#define PFUZE3001_VCCSD                6
+#define PFUZE3001_V33          7
+#define PFUZE3001_VLDO3                8
+#define PFUZE3001_VLDO4                9
+
 struct regulator_init_data;
 
 struct pfuze_regulator_platform_data {
diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h
new file mode 100644 (file)
index 0000000..7e3b9c6
--- /dev/null
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/platform_device.h>
+#ifndef __LLCC_QCOM__
+#define __LLCC_QCOM__
+
+#define LLCC_CPUSS       1
+#define LLCC_VIDSC0      2
+#define LLCC_VIDSC1      3
+#define LLCC_ROTATOR     4
+#define LLCC_VOICE       5
+#define LLCC_AUDIO       6
+#define LLCC_MDMHPGRW    7
+#define LLCC_MDM         8
+#define LLCC_CMPT        10
+#define LLCC_GPUHTW      11
+#define LLCC_GPU         12
+#define LLCC_MMUHWT      13
+#define LLCC_CMPTDMA     15
+#define LLCC_DISP        16
+#define LLCC_VIDFW       17
+#define LLCC_MDMHPFX     20
+#define LLCC_MDMPNG      21
+#define LLCC_AUDHW       22
+
+/**
+ * llcc_slice_desc - Cache slice descriptor
+ * @slice_id: llcc slice id
+ * @slice_size: Size allocated for the llcc slice
+ */
+struct llcc_slice_desc {
+       u32 slice_id;
+       size_t slice_size;
+};
+
+/**
+ * llcc_slice_config - Data associated with the llcc slice
+ * @usecase_id: usecase id for which the llcc slice is used
+ * @slice_id: llcc slice id assigned to each slice
+ * @max_cap: maximum capacity of the llcc slice
+ * @priority: priority of the llcc slice
+ * @fixed_size: whether the llcc slice can grow beyond its size
+ * @bonus_ways: bonus ways associated with llcc slice
+ * @res_ways: reserved ways associated with llcc slice
+ * @cache_mode: mode of the llcc slice
+ * @probe_target_ways: Probe only reserved and bonus ways on a cache miss
+ * @dis_cap_alloc: Disable capacity based allocation
+ * @retain_on_pc: Retain through power collapse
+ * @activate_on_init: activate the slice on init
+ */
+struct llcc_slice_config {
+       u32 usecase_id;
+       u32 slice_id;
+       u32 max_cap;
+       u32 priority;
+       bool fixed_size;
+       u32 bonus_ways;
+       u32 res_ways;
+       u32 cache_mode;
+       u32 probe_target_ways;
+       bool dis_cap_alloc;
+       bool retain_on_pc;
+       bool activate_on_init;
+};
+
+/**
+ * llcc_drv_data - Data associated with the llcc driver
+ * @regmap: regmap associated with the llcc device
+ * @cfg: pointer to the data structure for slice configuration
+ * @lock: mutex associated with each slice
+ * @cfg_size: size of the config data table
+ * @max_slices: max slices as read from device tree
+ * @bcast_off: Offset of the broadcast bank
+ * @num_banks: Number of llcc banks
+ * @bitmap: Bit map to track the active slice ids
+ * @offsets: Pointer to the bank offsets array
+ */
+struct llcc_drv_data {
+       struct regmap *regmap;
+       const struct llcc_slice_config *cfg;
+       struct mutex lock;
+       u32 cfg_size;
+       u32 max_slices;
+       u32 bcast_off;
+       u32 num_banks;
+       unsigned long *bitmap;
+       u32 *offsets;
+};
+
+#if IS_ENABLED(CONFIG_QCOM_LLCC)
+/**
+ * llcc_slice_getd - get llcc slice descriptor
+ * @uid: usecase_id of the client
+ */
+struct llcc_slice_desc *llcc_slice_getd(u32 uid);
+
+/**
+ * llcc_slice_putd - llcc slice descritpor
+ * @desc: Pointer to llcc slice descriptor
+ */
+void llcc_slice_putd(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_get_slice_id - get slice id
+ * @desc: Pointer to llcc slice descriptor
+ */
+int llcc_get_slice_id(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_get_slice_size - llcc slice size
+ * @desc: Pointer to llcc slice descriptor
+ */
+size_t llcc_get_slice_size(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_slice_activate - Activate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ */
+int llcc_slice_activate(struct llcc_slice_desc *desc);
+
+/**
+ * llcc_slice_deactivate - Deactivate the llcc slice
+ * @desc: Pointer to llcc slice descriptor
+ */
+int llcc_slice_deactivate(struct llcc_slice_desc *desc);
+
+/**
+ * qcom_llcc_probe - program the sct table
+ * @pdev: platform device pointer
+ * @table: soc sct table
+ * @sz: Size of the config table
+ */
+int qcom_llcc_probe(struct platform_device *pdev,
+                     const struct llcc_slice_config *table, u32 sz);
+#else
+static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid)
+{
+       return NULL;
+}
+
+static inline void llcc_slice_putd(struct llcc_slice_desc *desc)
+{
+
+};
+
+static inline int llcc_get_slice_id(struct llcc_slice_desc *desc)
+{
+       return -EINVAL;
+}
+
+static inline size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
+{
+       return 0;
+}
+static inline int llcc_slice_activate(struct llcc_slice_desc *desc)
+{
+       return -EINVAL;
+}
+
+static inline int llcc_slice_deactivate(struct llcc_slice_desc *desc)
+{
+       return -EINVAL;
+}
+static inline int qcom_llcc_probe(struct platform_device *pdev,
+                     const struct llcc_slice_config *table, u32 sz)
+{
+       return -ENODEV;
+}
+
+static inline int qcom_llcc_remove(struct platform_device *pdev)
+{
+       return -ENODEV;
+}
+#endif
+
+#endif
diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h
new file mode 100644 (file)
index 0000000..619e07c
--- /dev/null
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __SOC_QCOM_RPMH_H__
+#define __SOC_QCOM_RPMH_H__
+
+#include <soc/qcom/tcs.h>
+#include <linux/platform_device.h>
+
+
+#if IS_ENABLED(CONFIG_QCOM_RPMH)
+int rpmh_write(const struct device *dev, enum rpmh_state state,
+              const struct tcs_cmd *cmd, u32 n);
+
+int rpmh_write_async(const struct device *dev, enum rpmh_state state,
+                    const struct tcs_cmd *cmd, u32 n);
+
+int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
+                    const struct tcs_cmd *cmd, u32 *n);
+
+int rpmh_flush(const struct device *dev);
+
+int rpmh_invalidate(const struct device *dev);
+
+#else
+
+static inline int rpmh_write(const struct device *dev, enum rpmh_state state,
+                            const struct tcs_cmd *cmd, u32 n)
+{ return -ENODEV; }
+
+static inline int rpmh_write_async(const struct device *dev,
+                                  enum rpmh_state state,
+                                  const struct tcs_cmd *cmd, u32 n)
+{ return -ENODEV; }
+
+static inline int rpmh_write_batch(const struct device *dev,
+                                  enum rpmh_state state,
+                                  const struct tcs_cmd *cmd, u32 *n)
+{ return -ENODEV; }
+
+static inline int rpmh_flush(const struct device *dev)
+{ return -ENODEV; }
+
+static inline int rpmh_invalidate(const struct device *dev)
+{ return -ENODEV; }
+
+#endif /* CONFIG_QCOM_RPMH */
+
+#endif /* __SOC_QCOM_RPMH_H__ */
diff --git a/include/soc/qcom/tcs.h b/include/soc/qcom/tcs.h
new file mode 100644 (file)
index 0000000..262876a
--- /dev/null
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __SOC_QCOM_TCS_H__
+#define __SOC_QCOM_TCS_H__
+
+#define MAX_RPMH_PAYLOAD       16
+
+/**
+ * rpmh_state: state for the request
+ *
+ * RPMH_SLEEP_STATE:       State of the resource when the processor subsystem
+ *                         is powered down. There is no client using the
+ *                         resource actively.
+ * RPMH_WAKE_ONLY_STATE:   Resume resource state to the value previously
+ *                         requested before the processor was powered down.
+ * RPMH_ACTIVE_ONLY_STATE: Active or AMC mode requests. Resource state
+ *                         is aggregated immediately.
+ */
+enum rpmh_state {
+       RPMH_SLEEP_STATE,
+       RPMH_WAKE_ONLY_STATE,
+       RPMH_ACTIVE_ONLY_STATE,
+};
+
+/**
+ * struct tcs_cmd: an individual request to RPMH.
+ *
+ * @addr: the address of the resource slv_id:18:16 | offset:0:15
+ * @data: the resource state request
+ * @wait: wait for this request to be complete before sending the next
+ */
+struct tcs_cmd {
+       u32 addr;
+       u32 data;
+       u32 wait;
+};
+
+/**
+ * struct tcs_request: A set of tcs_cmds sent together in a TCS
+ *
+ * @state:          state for the request.
+ * @wait_for_compl: wait until we get a response from the h/w accelerator
+ * @num_cmds:       the number of @cmds in this request
+ * @cmds:           an array of tcs_cmds
+ */
+struct tcs_request {
+       enum rpmh_state state;
+       u32 wait_for_compl;
+       u32 num_cmds;
+       struct tcs_cmd *cmds;
+};
+
+#endif /* __SOC_QCOM_TCS_H__ */