Merge branch 'regulator-4.19' into regulator-next
This commit is contained in:
commit
d22d59362b
27
Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator
Normal file
27
Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator
Normal file
@ -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.
|
26
Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
Normal file
26
Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
Normal file
@ -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>;
|
||||||
|
};
|
@ -5,6 +5,7 @@ Requires node properties:
|
|||||||
- "compatible" value one of:
|
- "compatible" value one of:
|
||||||
"motorola,cpcap-regulator"
|
"motorola,cpcap-regulator"
|
||||||
"motorola,mapphone-cpcap-regulator"
|
"motorola,mapphone-cpcap-regulator"
|
||||||
|
"motorola,xoom-cpcap-regulator"
|
||||||
|
|
||||||
Required regulator properties:
|
Required regulator properties:
|
||||||
- "regulator-name"
|
- "regulator-name"
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
PFUZE100 family of regulators
|
PFUZE100 family of regulators
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000"
|
- compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000", "fsl,pfuze3001"
|
||||||
- reg: I2C slave address
|
- 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:
|
Required child node:
|
||||||
- regulators: This is the list of child nodes that specify the regulator
|
- regulators: This is the list of child nodes that specify the regulator
|
||||||
initialization data for defined regulators. Please refer to below doc
|
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
|
sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6,coin
|
||||||
--PFUZE3000
|
--PFUZE3000
|
||||||
sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4
|
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.
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
@ -1,13 +1,5 @@
|
|||||||
ROHM BD71837 Power Management Integrated Circuit (PMIC) regulator bindings
|
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:
|
Required properties:
|
||||||
- regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7"
|
- regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7"
|
||||||
|
|
||||||
|
@ -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>;
|
||||||
|
};
|
||||||
|
...
|
||||||
|
};
|
137
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
Normal file
137
Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
Normal file
@ -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>;
|
||||||
|
};
|
@ -372,6 +372,36 @@ void device_link_del(struct device_link *link)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(device_link_del);
|
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)
|
static void device_links_missing_supplier(struct device *dev)
|
||||||
{
|
{
|
||||||
struct device_link *link;
|
struct device_link *link;
|
||||||
|
@ -180,9 +180,9 @@ config REGULATOR_BCM590XX
|
|||||||
BCM590xx PMUs. This will enable support for the software
|
BCM590xx PMUs. This will enable support for the software
|
||||||
controllable LDO/Switching regulators.
|
controllable LDO/Switching regulators.
|
||||||
|
|
||||||
config REGULATOR_BD71837
|
config REGULATOR_BD718XX
|
||||||
tristate "ROHM BD71837 Power Regulator"
|
tristate "ROHM BD71837 Power Regulator"
|
||||||
depends on MFD_BD71837
|
depends on MFD_ROHM_BD718XX
|
||||||
help
|
help
|
||||||
This driver supports voltage regulators on ROHM BD71837 PMIC.
|
This driver supports voltage regulators on ROHM BD71837 PMIC.
|
||||||
This will enable support for the software controllable buck
|
This will enable support for the software controllable buck
|
||||||
@ -633,12 +633,12 @@ config REGULATOR_PCF50633
|
|||||||
on PCF50633
|
on PCF50633
|
||||||
|
|
||||||
config REGULATOR_PFUZE100
|
config REGULATOR_PFUZE100
|
||||||
tristate "Freescale PFUZE100/200/3000 regulator driver"
|
tristate "Freescale PFUZE100/200/3000/3001 regulator driver"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
help
|
help
|
||||||
Say y here to support the regulators found on the Freescale
|
Say y here to support the regulators found on the Freescale
|
||||||
PFUZE100/200/3000 PMIC.
|
PFUZE100/200/3000/3001 PMIC.
|
||||||
|
|
||||||
config REGULATOR_PV88060
|
config REGULATOR_PV88060
|
||||||
tristate "Powerventure Semiconductor PV88060 regulator"
|
tristate "Powerventure Semiconductor PV88060 regulator"
|
||||||
@ -682,6 +682,15 @@ config REGULATOR_QCOM_RPM
|
|||||||
Qualcomm RPM as a module. The module will be named
|
Qualcomm RPM as a module. The module will be named
|
||||||
"qcom_rpm-regulator".
|
"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
|
config REGULATOR_QCOM_SMD_RPM
|
||||||
tristate "Qualcomm SMD based RPM regulator driver"
|
tristate "Qualcomm SMD based RPM regulator driver"
|
||||||
depends on QCOM_SMD_RPM
|
depends on QCOM_SMD_RPM
|
||||||
@ -950,6 +959,14 @@ config REGULATOR_TWL4030
|
|||||||
This driver supports the voltage regulators provided by
|
This driver supports the voltage regulators provided by
|
||||||
this family of companion chips.
|
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
|
config REGULATOR_VCTRL
|
||||||
tristate "Voltage controlled regulators"
|
tristate "Voltage controlled regulators"
|
||||||
depends on OF
|
depends on OF
|
||||||
|
@ -27,7 +27,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
|
|||||||
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
|
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
|
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-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_BD9571MWV) += bd9571mwv-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
|
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
|
||||||
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.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_MT6380) += mt6380-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
|
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-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_SMD_RPM) += qcom_smd-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
|
obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-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_TPS80031) += tps80031-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
|
obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-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_VCTRL) += vctrl-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
|
obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
|
||||||
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
|
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
|
||||||
|
@ -2,19 +2,18 @@
|
|||||||
// Copyright (C) 2018 ROHM Semiconductors
|
// Copyright (C) 2018 ROHM Semiconductors
|
||||||
// bd71837-regulator.c ROHM BD71837MWV regulator driver
|
// bd71837-regulator.c ROHM BD71837MWV regulator driver
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
#include <linux/interrupt.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/platform_device.h>
|
||||||
#include <linux/regulator/driver.h>
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/regulator/machine.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/regulator/of_regulator.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
struct bd71837_pmic {
|
struct bd71837_pmic {
|
||||||
struct regulator_desc descs[BD71837_REGULATOR_CNT];
|
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;
|
int id = rdev->desc->id;
|
||||||
unsigned int ramp_value = BUCK_RAMPRATE_10P00MV;
|
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);
|
ramp_delay);
|
||||||
switch (ramp_delay) {
|
switch (ramp_delay) {
|
||||||
case 1 ... 1250:
|
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,
|
static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev,
|
||||||
unsigned int sel)
|
unsigned int sel)
|
||||||
{
|
{
|
||||||
int ret;
|
if (regulator_is_enabled_regmap(rdev))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
ret = regulator_is_enabled_regmap(rdev);
|
return regulator_set_voltage_sel_regmap(rdev, sel);
|
||||||
if (!ret)
|
|
||||||
ret = regulator_set_voltage_sel_regmap(rdev, sel);
|
|
||||||
else if (ret == 1)
|
|
||||||
ret = -EBUSY;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct regulator_ops bd71837_ldo_regulator_ops = {
|
static struct regulator_ops bd71837_ldo_regulator_ops = {
|
||||||
@ -195,7 +190,7 @@ static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = {
|
|||||||
* LDO2
|
* LDO2
|
||||||
* 0.8 or 0.9V
|
* 0.8 or 0.9V
|
||||||
*/
|
*/
|
||||||
const unsigned int ldo_2_volts[] = {
|
static const unsigned int ldo_2_volts[] = {
|
||||||
900000, 800000
|
900000, 800000
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -495,7 +490,6 @@ struct reg_init {
|
|||||||
static int bd71837_probe(struct platform_device *pdev)
|
static int bd71837_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct bd71837_pmic *pmic;
|
struct bd71837_pmic *pmic;
|
||||||
struct bd71837_board *pdata;
|
|
||||||
struct regulator_config config = { 0 };
|
struct regulator_config config = { 0 };
|
||||||
struct reg_init pmic_regulator_inits[] = {
|
struct reg_init pmic_regulator_inits[] = {
|
||||||
{
|
{
|
||||||
@ -548,8 +542,7 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
pmic = devm_kzalloc(&pdev->dev, sizeof(struct bd71837_pmic),
|
pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
if (!pmic)
|
if (!pmic)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -564,7 +557,6 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
platform_set_drvdata(pdev, pmic);
|
platform_set_drvdata(pdev, pmic);
|
||||||
pdata = dev_get_platdata(pmic->mfd->dev);
|
|
||||||
|
|
||||||
/* Register LOCK release */
|
/* Register LOCK release */
|
||||||
err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK,
|
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);
|
dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err);
|
||||||
goto err;
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
dev_dbg(&pmic->pdev->dev, "%s: Unlocked lock register 0x%x\n",
|
dev_dbg(&pmic->pdev->dev, "Unlocked lock register 0x%x\n",
|
||||||
__func__, BD71837_REG_REGLOCK);
|
BD71837_REG_REGLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) {
|
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];
|
desc = &pmic->descs[i];
|
||||||
|
|
||||||
if (pdata)
|
|
||||||
config.init_data = pdata->init_data[i];
|
|
||||||
|
|
||||||
config.dev = pdev->dev.parent;
|
config.dev = pdev->dev.parent;
|
||||||
config.driver_data = pmic;
|
config.driver_data = pmic;
|
||||||
config.regmap = pmic->mfd->regmap;
|
config.regmap = pmic->mfd->regmap;
|
||||||
@ -619,8 +608,6 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||||||
pmic->rdev[i] = rdev;
|
pmic->rdev[i] = rdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err:
|
err:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -628,7 +615,6 @@ err:
|
|||||||
static struct platform_driver bd71837_regulator = {
|
static struct platform_driver bd71837_regulator = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "bd71837-pmic",
|
.name = "bd71837-pmic",
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
},
|
||||||
.probe = bd71837_probe,
|
.probe = bd71837_probe,
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,7 @@ struct bd9571mwv_reg {
|
|||||||
/* DDR Backup Power */
|
/* DDR Backup Power */
|
||||||
u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */
|
u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */
|
||||||
u8 bkup_mode_cnt_saved;
|
u8 bkup_mode_cnt_saved;
|
||||||
|
bool bkup_mode_enabled;
|
||||||
|
|
||||||
/* Power switch type */
|
/* Power switch type */
|
||||||
bool rstbmode_level;
|
bool rstbmode_level;
|
||||||
@ -171,13 +172,60 @@ static int bd9571mwv_bkup_mode_write(struct bd9571mwv *bd, unsigned int mode)
|
|||||||
return 0;
|
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)
|
static int bd9571mwv_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!device_may_wakeup(dev))
|
if (!bdreg->bkup_mode_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Save DDR Backup Mode */
|
/* Save DDR Backup Mode */
|
||||||
@ -204,7 +252,7 @@ static int bd9571mwv_resume(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (!device_may_wakeup(dev))
|
if (!bdreg->bkup_mode_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Restore DDR Backup Mode */
|
/* 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)
|
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
|
#define DEV_PM_OPS &bd9571mwv_pm
|
||||||
#else
|
#else
|
||||||
#define DEV_PM_OPS NULL
|
#define DEV_PM_OPS NULL
|
||||||
|
#define bd9571mwv_regulator_remove NULL
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
static int bd9571mwv_regulator_probe(struct platform_device *pdev)
|
static int bd9571mwv_regulator_probe(struct platform_device *pdev)
|
||||||
@ -270,14 +324,21 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
if (bdreg->bkup_mode_cnt_keepon) {
|
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.
|
* 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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -294,6 +355,7 @@ static struct platform_driver bd9571mwv_regulator_driver = {
|
|||||||
.pm = DEV_PM_OPS,
|
.pm = DEV_PM_OPS,
|
||||||
},
|
},
|
||||||
.probe = bd9571mwv_regulator_probe,
|
.probe = bd9571mwv_regulator_probe,
|
||||||
|
.remove = bd9571mwv_regulator_remove,
|
||||||
.id_table = bd9571mwv_regulator_id_table,
|
.id_table = bd9571mwv_regulator_id_table,
|
||||||
};
|
};
|
||||||
module_platform_driver(bd9571mwv_regulator_driver);
|
module_platform_driver(bd9571mwv_regulator_driver);
|
||||||
|
@ -1740,6 +1740,8 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
|
|||||||
rdev->use_count = 0;
|
rdev->use_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device_link_add(dev, &rdev->dev, DL_FLAG_STATELESS);
|
||||||
|
|
||||||
return regulator;
|
return regulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1829,9 +1831,21 @@ static void _regulator_put(struct regulator *regulator)
|
|||||||
|
|
||||||
debugfs_remove_recursive(regulator->debugfs);
|
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);
|
sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
|
||||||
|
}
|
||||||
|
|
||||||
regulator_lock(rdev);
|
regulator_lock(rdev);
|
||||||
list_del(®ulator->list);
|
list_del(®ulator->list);
|
||||||
|
|
||||||
@ -4441,7 +4455,7 @@ void regulator_unregister(struct regulator_dev *rdev)
|
|||||||
EXPORT_SYMBOL_GPL(regulator_unregister);
|
EXPORT_SYMBOL_GPL(regulator_unregister);
|
||||||
|
|
||||||
#ifdef CONFIG_SUSPEND
|
#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);
|
struct regulator_dev *rdev = dev_to_rdev(dev);
|
||||||
suspend_state_t *state = data;
|
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
|
* @state: system suspend state
|
||||||
*
|
*
|
||||||
* Configure each regulator with it's suspend operating parameters for 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;
|
suspend_state_t state = pm_suspend_target_state;
|
||||||
|
|
||||||
return class_for_each_device(®ulator_class, NULL, &state,
|
return class_for_each_device(®ulator_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;
|
int ret = 0;
|
||||||
struct regulator_dev *rdev = dev_to_rdev(dev);
|
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);
|
regulator_lock(rdev);
|
||||||
|
|
||||||
if (rdev->desc->ops->resume_early &&
|
if (rdev->desc->ops->resume &&
|
||||||
(rstate->enabled == ENABLE_IN_SUSPEND ||
|
(rstate->enabled == ENABLE_IN_SUSPEND ||
|
||||||
rstate->enabled == DISABLE_IN_SUSPEND))
|
rstate->enabled == DISABLE_IN_SUSPEND))
|
||||||
ret = rdev->desc->ops->resume_early(rdev);
|
ret = rdev->desc->ops->resume(rdev);
|
||||||
|
|
||||||
regulator_unlock(rdev);
|
regulator_unlock(rdev);
|
||||||
|
|
||||||
return ret;
|
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;
|
suspend_state_t state = pm_suspend_target_state;
|
||||||
|
|
||||||
return class_for_each_device(®ulator_class, NULL, &state,
|
return class_for_each_device(®ulator_class, NULL, &state,
|
||||||
_regulator_resume_early);
|
_regulator_resume);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* !CONFIG_SUSPEND */
|
#else /* !CONFIG_SUSPEND */
|
||||||
|
|
||||||
#define regulator_suspend_late NULL
|
#define regulator_suspend NULL
|
||||||
#define regulator_resume_early NULL
|
#define regulator_resume NULL
|
||||||
|
|
||||||
#endif /* !CONFIG_SUSPEND */
|
#endif /* !CONFIG_SUSPEND */
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
|
static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
|
||||||
.suspend_late = regulator_suspend_late,
|
.suspend = regulator_suspend,
|
||||||
.resume_early = regulator_resume_early,
|
.resume = regulator_resume,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -271,6 +271,29 @@ static struct regulator_ops cpcap_regulator_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned int unknown_val_tbl[] = { 0, };
|
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 sw5_val_tbl[] = { 0, 5050000, };
|
||||||
static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000,
|
static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000,
|
||||||
2900000, };
|
2900000, };
|
||||||
@ -389,6 +412,82 @@ static struct cpcap_regulator omap4_regulators[] = {
|
|||||||
{ /* sentinel */ },
|
{ /* 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[] = {
|
static const struct of_device_id cpcap_regulator_id_table[] = {
|
||||||
{
|
{
|
||||||
.compatible = "motorola,cpcap-regulator",
|
.compatible = "motorola,cpcap-regulator",
|
||||||
@ -397,6 +496,10 @@ static const struct of_device_id cpcap_regulator_id_table[] = {
|
|||||||
.compatible = "motorola,mapphone-cpcap-regulator",
|
.compatible = "motorola,mapphone-cpcap-regulator",
|
||||||
.data = omap4_regulators,
|
.data = omap4_regulators,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "motorola,xoom-cpcap-regulator",
|
||||||
|
.data = xoom_regulators,
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table);
|
MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table);
|
||||||
|
@ -1,19 +1,9 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* max14577.c - Regulator driver for the Maxim 14577/77836
|
//
|
||||||
*
|
// max14577.c - Regulator driver for the Maxim 14577/77836
|
||||||
* Copyright (C) 2013,2014 Samsung Electronics
|
//
|
||||||
* Krzysztof Kozlowski <krzk@kernel.org>
|
// 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
@ -1,26 +1,12 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* max77686.c - Regulator driver for the Maxim 77686
|
//
|
||||||
*
|
// max77686.c - Regulator driver for the Maxim 77686
|
||||||
* Copyright (C) 2012 Samsung Electronics
|
//
|
||||||
* Chiwoong Byun <woong.byun@samsung.com>
|
// Copyright (C) 2012 Samsung Electronics
|
||||||
* Jonghwa Lee <jonghwa3.lee@samsung.com>
|
// 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
|
// This driver is based on max8997.c
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
|
@ -1,26 +1,12 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* max77693.c - Regulator driver for the Maxim 77693 and 77843
|
//
|
||||||
*
|
// max77693.c - Regulator driver for the Maxim 77693 and 77843
|
||||||
* Copyright (C) 2013-2015 Samsung Electronics
|
//
|
||||||
* Jonghwa Lee <jonghwa3.lee@samsung.com>
|
// Copyright (C) 2013-2015 Samsung Electronics
|
||||||
* Krzysztof Kozlowski <krzk@kernel.org>
|
// 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
|
// This driver is based on max77686.c
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -1,25 +1,15 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* max77802.c - Regulator driver for the Maxim 77802
|
//
|
||||||
*
|
// max77802.c - Regulator driver for the Maxim 77802
|
||||||
* Copyright (C) 2013-2014 Google, Inc
|
//
|
||||||
* Simon Glass <sjg@chromium.org>
|
// Copyright (C) 2013-2014 Google, Inc
|
||||||
*
|
// Simon Glass <sjg@chromium.org>
|
||||||
* Copyright (C) 2012 Samsung Electronics
|
//
|
||||||
* Chiwoong Byun <woong.byun@samsung.com>
|
// Copyright (C) 2012 Samsung Electronics
|
||||||
* Jonghwa Lee <jonghwa3.lee@samsung.com>
|
// 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
|
// This driver is based on max8997.c
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
|
@ -1,25 +1,11 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* max8997.c - Regulator driver for the Maxim 8997/8966
|
//
|
||||||
*
|
// max8997.c - Regulator driver for the Maxim 8997/8966
|
||||||
* Copyright (C) 2011 Samsung Electronics
|
//
|
||||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
// 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
|
// This driver is based on max8998.c
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/err.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 rid = rdev_get_id(rdev);
|
||||||
int val;
|
int val;
|
||||||
|
|
||||||
if (rid >= ARRAY_SIZE(reg_voltage_map) ||
|
if (rid < 0 || rid >= ARRAY_SIZE(reg_voltage_map))
|
||||||
rid < 0)
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
desc = reg_voltage_map[rid];
|
desc = reg_voltage_map[rid];
|
||||||
|
@ -1,24 +1,10 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* max8998.c - Voltage regulator driver for the Maxim 8998
|
//
|
||||||
*
|
// max8998.c - Voltage regulator driver for the Maxim 8998
|
||||||
* Copyright (C) 2009-2010 Samsung Electronics
|
//
|
||||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
// Copyright (C) 2009-2010 Samsung Electronics
|
||||||
* Marek Szyprowski <m.szyprowski@samsung.com>
|
// 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#define PFUZE_FLAG_DISABLE_SW BIT(1)
|
||||||
|
|
||||||
#define PFUZE_NUMREGS 128
|
#define PFUZE_NUMREGS 128
|
||||||
#define PFUZE100_VOL_OFFSET 0
|
#define PFUZE100_VOL_OFFSET 0
|
||||||
#define PFUZE100_STANDBY_OFFSET 1
|
#define PFUZE100_STANDBY_OFFSET 1
|
||||||
@ -44,16 +46,18 @@
|
|||||||
#define PFUZE100_VGEN5VOL 0x70
|
#define PFUZE100_VGEN5VOL 0x70
|
||||||
#define PFUZE100_VGEN6VOL 0x71
|
#define PFUZE100_VGEN6VOL 0x71
|
||||||
|
|
||||||
enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3 };
|
enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, };
|
||||||
|
|
||||||
struct pfuze_regulator {
|
struct pfuze_regulator {
|
||||||
struct regulator_desc desc;
|
struct regulator_desc desc;
|
||||||
unsigned char stby_reg;
|
unsigned char stby_reg;
|
||||||
unsigned char stby_mask;
|
unsigned char stby_mask;
|
||||||
|
bool sw_reg;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pfuze_chip {
|
struct pfuze_chip {
|
||||||
int chip_id;
|
int chip_id;
|
||||||
|
int flags;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR];
|
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 = "pfuze100", .driver_data = PFUZE100},
|
||||||
{.name = "pfuze200", .driver_data = PFUZE200},
|
{.name = "pfuze200", .driver_data = PFUZE200},
|
||||||
{.name = "pfuze3000", .driver_data = PFUZE3000},
|
{.name = "pfuze3000", .driver_data = PFUZE3000},
|
||||||
|
{.name = "pfuze3001", .driver_data = PFUZE3001},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, pfuze_device_id);
|
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,pfuze100", .data = (void *)PFUZE100},
|
||||||
{ .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
|
{ .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
|
||||||
{ .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000},
|
{ .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000},
|
||||||
|
{ .compatible = "fsl,pfuze3001", .data = (void *)PFUZE3001},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, pfuze_dt_ids);
|
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);
|
struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev);
|
||||||
int id = rdev_get_id(rdev);
|
int id = rdev_get_id(rdev);
|
||||||
|
bool reg_has_ramp_delay;
|
||||||
unsigned int ramp_bits;
|
unsigned int ramp_bits;
|
||||||
int ret;
|
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_delay = 12500 / ramp_delay;
|
||||||
ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3);
|
ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3);
|
||||||
ret = regmap_update_bits(pfuze100->regmap,
|
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);
|
0xc0, ramp_bits << 6);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(pfuze100->dev, "ramp failed, err %d\n", ret);
|
dev_err(pfuze100->dev, "ramp failed, err %d\n", ret);
|
||||||
} else
|
} else {
|
||||||
ret = -EACCES;
|
ret = -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -142,6 +167,14 @@ static const struct regulator_ops pfuze100_fixed_regulator_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct regulator_ops pfuze100_sw_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,
|
.enable = regulator_enable_regmap,
|
||||||
.disable = regulator_disable_regmap,
|
.disable = regulator_disable_regmap,
|
||||||
.is_enabled = regulator_is_enabled_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_reg = (base) + PFUZE100_VOL_OFFSET, \
|
||||||
.vsel_mask = 0x3f, \
|
.vsel_mask = 0x3f, \
|
||||||
.enable_reg = (base) + PFUZE100_MODE_OFFSET, \
|
.enable_reg = (base) + PFUZE100_MODE_OFFSET, \
|
||||||
.enable_val = 0xc, \
|
|
||||||
.disable_val = 0x0, \
|
|
||||||
.enable_mask = 0xf, \
|
.enable_mask = 0xf, \
|
||||||
.enable_time = 500, \
|
|
||||||
}, \
|
}, \
|
||||||
.stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \
|
.stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \
|
||||||
.stby_mask = 0x3f, \
|
.stby_mask = 0x3f, \
|
||||||
|
.sw_reg = true, \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages) \
|
#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),
|
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
|
#ifdef CONFIG_OF
|
||||||
/* PFUZE100 */
|
/* PFUZE100 */
|
||||||
static struct of_regulator_match pfuze100_matches[] = {
|
static struct of_regulator_match pfuze100_matches[] = {
|
||||||
@ -418,6 +462,21 @@ static struct of_regulator_match pfuze3000_matches[] = {
|
|||||||
{ .name = "vldo4", },
|
{ .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 struct of_regulator_match *pfuze_matches;
|
||||||
|
|
||||||
static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
|
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)
|
if (!np)
|
||||||
return -EINVAL;
|
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");
|
parent = of_get_child_by_name(np, "regulators");
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
dev_err(dev, "regulators node not found\n");
|
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) {
|
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:
|
case PFUZE3000:
|
||||||
pfuze_matches = pfuze3000_matches;
|
pfuze_matches = pfuze3000_matches;
|
||||||
ret = of_regulator_match(dev, parent, 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");
|
dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8");
|
||||||
} else if ((value & 0x0f) != pfuze_chip->chip_id &&
|
} 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 */
|
/* device id NOT match with your setting */
|
||||||
dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
|
dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -588,6 +656,13 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
/* use the right regulators after identify the right device */
|
/* use the right regulators after identify the right device */
|
||||||
switch (pfuze_chip->chip_id) {
|
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:
|
case PFUZE3000:
|
||||||
pfuze_chip->pfuze_regulators = pfuze3000_regulators;
|
pfuze_chip->pfuze_regulators = pfuze3000_regulators;
|
||||||
regulator_num = ARRAY_SIZE(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",
|
dev_info(&client->dev, "pfuze%s found.\n",
|
||||||
(pfuze_chip->chip_id == PFUZE100) ? "100" :
|
(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,
|
memcpy(pfuze_chip->regulator_descs, pfuze_chip->pfuze_regulators,
|
||||||
sizeof(pfuze_chip->regulator_descs));
|
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) {
|
if (i >= sw_check_start && i <= sw_check_end) {
|
||||||
regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val);
|
regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val);
|
||||||
if (val & sw_hi) {
|
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->volt_table = pfuze3000_sw2hi;
|
||||||
desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi);
|
desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi);
|
||||||
} else {
|
} 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.dev = &client->dev;
|
||||||
config.init_data = init_data;
|
config.init_data = init_data;
|
||||||
config.driver_data = pfuze_chip;
|
config.driver_data = pfuze_chip;
|
||||||
@ -675,5 +767,5 @@ static struct i2c_driver pfuze_driver = {
|
|||||||
module_i2c_driver(pfuze_driver);
|
module_i2c_driver(pfuze_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("Robin Gong <b38343@freescale.com>");
|
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");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
769
drivers/regulator/qcom-rpmh-regulator.c
Normal file
769
drivers/regulator/qcom-rpmh-regulator.c
Normal file
@ -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, ®_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");
|
@ -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_TGGL_MASK 0x8000000
|
||||||
#define SAW3_AVS_CTL_CLEAR_MASK 0x7efc00
|
#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)
|
static void spmi_saw_set_vdd(void *data)
|
||||||
{
|
{
|
||||||
@ -1728,7 +1728,7 @@ static const struct spmi_regulator_data pmi8994_regulators[] = {
|
|||||||
{ "s2", 0x1700, "vdd_s2", },
|
{ "s2", 0x1700, "vdd_s2", },
|
||||||
{ "s3", 0x1a00, "vdd_s3", },
|
{ "s3", 0x1a00, "vdd_s3", },
|
||||||
{ "l1", 0x4000, "vdd_l1", },
|
{ "l1", 0x4000, "vdd_l1", },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id qcom_spmi_regulator_match[] = {
|
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;
|
const char *name;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *node = pdev->dev.of_node;
|
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;
|
int ret, lenp;
|
||||||
struct list_head *vreg_list;
|
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);
|
syscon = of_parse_phandle(node, "qcom,saw-reg", 0);
|
||||||
saw_regmap = syscon_node_to_regmap(syscon);
|
saw_regmap = syscon_node_to_regmap(syscon);
|
||||||
of_node_put(syscon);
|
of_node_put(syscon);
|
||||||
if (IS_ERR(regmap))
|
if (IS_ERR(saw_regmap))
|
||||||
dev_err(dev, "ERROR reading SAW regmap\n");
|
dev_err(dev, "ERROR reading SAW regmap\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (reg = match->data; reg->name; reg++) {
|
for (reg = match->data; reg->name; reg++) {
|
||||||
|
|
||||||
if (saw_regmap && \
|
if (saw_regmap) {
|
||||||
of_find_property(of_find_node_by_name(node, reg->name), \
|
reg_node = of_get_child_by_name(node, reg->name);
|
||||||
"qcom,saw-slave", &lenp)) {
|
reg_prop = of_find_property(reg_node, "qcom,saw-slave",
|
||||||
continue;
|
&lenp);
|
||||||
|
of_node_put(reg_node);
|
||||||
|
if (reg_prop)
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
|
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
|
||||||
@ -1816,13 +1820,17 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (saw_regmap && \
|
if (saw_regmap) {
|
||||||
of_find_property(of_find_node_by_name(node, reg->name), \
|
reg_node = of_get_child_by_name(node, reg->name);
|
||||||
"qcom,saw-leader", &lenp)) {
|
reg_prop = of_find_property(reg_node, "qcom,saw-leader",
|
||||||
spmi_saw_ops = *(vreg->desc.ops);
|
&lenp);
|
||||||
spmi_saw_ops.set_voltage_sel = \
|
of_node_put(reg_node);
|
||||||
spmi_regulator_saw_set_voltage;
|
if (reg_prop) {
|
||||||
vreg->desc.ops = &spmi_saw_ops;
|
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;
|
config.dev = dev;
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd
|
//
|
||||||
* http://www.samsung.com
|
// 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -1,20 +1,7 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* s2mps11.c
|
//
|
||||||
*
|
// Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
|
||||||
* Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
|
// http://www.samsung.com
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
@ -1,15 +1,7 @@
|
|||||||
/*
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
* s5m8767.c
|
//
|
||||||
*
|
// Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
// http://www.samsung.com
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
@ -232,6 +232,8 @@ static int tps65217_regulator_probe(struct platform_device *pdev)
|
|||||||
tps->strobes = devm_kcalloc(&pdev->dev,
|
tps->strobes = devm_kcalloc(&pdev->dev,
|
||||||
TPS65217_NUM_REGULATOR, sizeof(u8),
|
TPS65217_NUM_REGULATOR, sizeof(u8),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
|
if (!tps->strobes)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, tps);
|
platform_set_drvdata(pdev, tps);
|
||||||
|
|
||||||
|
213
drivers/regulator/uniphier-regulator.c
Normal file
213
drivers/regulator/uniphier-regulator.c
Normal file
@ -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");
|
@ -40,6 +40,23 @@ config QCOM_GSBI
|
|||||||
functions for connecting the underlying serial UART, SPI, and I2C
|
functions for connecting the underlying serial UART, SPI, and I2C
|
||||||
devices to the output pins.
|
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
|
config QCOM_MDT_LOADER
|
||||||
tristate
|
tristate
|
||||||
select QCOM_SCM
|
select QCOM_SCM
|
||||||
@ -75,6 +92,16 @@ config QCOM_RMTFS_MEM
|
|||||||
|
|
||||||
Say y here if you intend to boot the modem remoteproc.
|
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
|
config QCOM_SMEM
|
||||||
tristate "Qualcomm Shared Memory Manager (SMEM)"
|
tristate "Qualcomm Shared Memory Manager (SMEM)"
|
||||||
depends on ARCH_QCOM
|
depends on ARCH_QCOM
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
CFLAGS_rpmh-rsc.o := -I$(src)
|
||||||
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
|
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
|
||||||
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
|
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
|
||||||
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.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
|
obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
|
||||||
qmi_helpers-y += qmi_encdec.o qmi_interface.o
|
qmi_helpers-y += qmi_encdec.o qmi_interface.o
|
||||||
obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.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_SMD_RPM) += smd-rpm.o
|
||||||
obj-$(CONFIG_QCOM_SMEM) += smem.o
|
obj-$(CONFIG_QCOM_SMEM) += smem.o
|
||||||
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.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_SMSM) += smsm.o
|
||||||
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
|
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
|
||||||
obj-$(CONFIG_QCOM_APR) += apr.o
|
obj-$(CONFIG_QCOM_APR) += apr.o
|
||||||
|
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
|
||||||
|
obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
|
||||||
|
94
drivers/soc/qcom/llcc-sdm845.c
Normal file
94
drivers/soc/qcom/llcc-sdm845.c
Normal file
@ -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");
|
338
drivers/soc/qcom/llcc-slice.c
Normal file
338
drivers/soc/qcom/llcc-slice.c
Normal file
@ -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");
|
@ -184,6 +184,7 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
|
|||||||
device_initialize(&rmtfs_mem->dev);
|
device_initialize(&rmtfs_mem->dev);
|
||||||
rmtfs_mem->dev.parent = &pdev->dev;
|
rmtfs_mem->dev.parent = &pdev->dev;
|
||||||
rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
|
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->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr,
|
||||||
rmtfs_mem->size, MEMREMAP_WC);
|
rmtfs_mem->size, MEMREMAP_WC);
|
||||||
@ -206,8 +207,6 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
|
|||||||
goto put_device;
|
goto put_device;
|
||||||
}
|
}
|
||||||
|
|
||||||
rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
|
|
||||||
|
|
||||||
ret = of_property_read_u32(node, "qcom,vmid", &vmid);
|
ret = of_property_read_u32(node, "qcom,vmid", &vmid);
|
||||||
if (ret < 0 && ret != -EINVAL) {
|
if (ret < 0 && ret != -EINVAL) {
|
||||||
dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
|
dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
|
||||||
|
114
drivers/soc/qcom/rpmh-internal.h
Normal file
114
drivers/soc/qcom/rpmh-internal.h
Normal file
@ -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__ */
|
693
drivers/soc/qcom/rpmh-rsc.c
Normal file
693
drivers/soc/qcom/rpmh-rsc.c
Normal file
@ -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);
|
513
drivers/soc/qcom/rpmh.c
Normal file
513
drivers/soc/qcom/rpmh.c
Normal file
@ -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);
|
@ -364,11 +364,6 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem,
|
|||||||
end = phdr_to_last_uncached_entry(phdr);
|
end = phdr_to_last_uncached_entry(phdr);
|
||||||
cached = phdr_to_last_cached_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) {
|
while (hdr < end) {
|
||||||
if (hdr->canary != SMEM_PRIVATE_CANARY)
|
if (hdr->canary != SMEM_PRIVATE_CANARY)
|
||||||
goto bad_canary;
|
goto bad_canary;
|
||||||
@ -736,6 +731,11 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (smem->global_partition) {
|
||||||
|
dev_err(smem->dev, "Already found the global partition\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
ptable = qcom_smem_get_ptable(smem);
|
ptable = qcom_smem_get_ptable(smem);
|
||||||
if (IS_ERR(ptable))
|
if (IS_ERR(ptable))
|
||||||
return PTR_ERR(ptable);
|
return PTR_ERR(ptable);
|
||||||
|
82
drivers/soc/qcom/trace-rpmh.h
Normal file
82
drivers/soc/qcom/trace-rpmh.h
Normal file
@ -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>
|
@ -1,10 +1,7 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2014 Google, Inc
|
* 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
|
* Device Tree binding constants for the Maxim 77802 PMIC regulators
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
36
include/dt-bindings/regulator/qcom,rpmh-regulator.h
Normal file
36
include/dt-bindings/regulator/qcom,rpmh-regulator.h
Normal file
@ -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
|
14
include/dt-bindings/soc/qcom,rpmh-rsc.h
Normal file
14
include/dt-bindings/soc/qcom,rpmh-rsc.h
Normal file
@ -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__ */
|
@ -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_link *device_link_add(struct device *consumer,
|
||||||
struct device *supplier, u32 flags);
|
struct device *supplier, u32 flags);
|
||||||
void device_link_del(struct device_link *link);
|
void device_link_del(struct device_link *link);
|
||||||
|
void device_link_remove(void *consumer, struct device *supplier);
|
||||||
|
|
||||||
#ifdef CONFIG_PRINTK
|
#ifdef CONFIG_PRINTK
|
||||||
|
|
||||||
|
@ -87,6 +87,10 @@ static inline int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr,
|
|||||||
static inline int
|
static inline int
|
||||||
qcom_scm_pas_auth_and_reset(u32 peripheral) { return -ENODEV; }
|
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_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 void qcom_scm_cpu_power_down(u32 flags) {}
|
||||||
static inline u32 qcom_scm_get_version(void) { return 0; }
|
static inline u32 qcom_scm_get_version(void) { return 0; }
|
||||||
static inline u32
|
static inline u32
|
||||||
|
@ -46,7 +46,7 @@ enum regulator_status {
|
|||||||
/**
|
/**
|
||||||
* struct regulator_linear_range - specify linear voltage ranges
|
* 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().
|
* regulator_list_linear_range().
|
||||||
*
|
*
|
||||||
* @min_uV: Lowest voltage in range
|
* @min_uV: Lowest voltage in range
|
||||||
@ -220,7 +220,7 @@ struct regulator_ops {
|
|||||||
/* set regulator suspend operating mode (defined in consumer.h) */
|
/* set regulator suspend operating mode (defined in consumer.h) */
|
||||||
int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
|
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 *);
|
int (*set_pull_down) (struct regulator_dev *);
|
||||||
};
|
};
|
||||||
|
@ -64,6 +64,17 @@
|
|||||||
#define PFUZE3000_VLDO3 11
|
#define PFUZE3000_VLDO3 11
|
||||||
#define PFUZE3000_VLDO4 12
|
#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 regulator_init_data;
|
||||||
|
|
||||||
struct pfuze_regulator_platform_data {
|
struct pfuze_regulator_platform_data {
|
||||||
|
180
include/linux/soc/qcom/llcc-qcom.h
Normal file
180
include/linux/soc/qcom/llcc-qcom.h
Normal file
@ -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
|
51
include/soc/qcom/rpmh.h
Normal file
51
include/soc/qcom/rpmh.h
Normal file
@ -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__ */
|
56
include/soc/qcom/tcs.h
Normal file
56
include/soc/qcom/tcs.h
Normal file
@ -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__ */
|
Loading…
Reference in New Issue
Block a user