mirror of https://gitee.com/openkylin/linux.git
Merge branch 'regulator-4.19' into regulator-next
This commit is contained in:
commit
d22d59362b
|
@ -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.
|
|
@ -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:
|
||||
"motorola,cpcap-regulator"
|
||||
"motorola,mapphone-cpcap-regulator"
|
||||
"motorola,xoom-cpcap-regulator"
|
||||
|
||||
Required regulator properties:
|
||||
- "regulator-name"
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
PFUZE100 family of regulators
|
||||
|
||||
Required properties:
|
||||
- compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000"
|
||||
- compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000", "fsl,pfuze3001"
|
||||
- reg: I2C slave address
|
||||
|
||||
Optional properties:
|
||||
- fsl,pfuze-support-disable-sw: Boolean, if present disable all unused switch
|
||||
regulators to save power consumption. Attention, ensure that all important
|
||||
regulators (e.g. DDR ref, DDR supply) has set the "regulator-always-on"
|
||||
property. If not present, the switched regualtors are always on and can't be
|
||||
disabled. This binding is a workaround to keep backward compatibility with
|
||||
old dtb's which rely on the fact that the switched regulators are always on
|
||||
and don't mark them explicit as "regulator-always-on".
|
||||
|
||||
Required child node:
|
||||
- regulators: This is the list of child nodes that specify the regulator
|
||||
initialization data for defined regulators. Please refer to below doc
|
||||
|
@ -16,6 +25,8 @@ Required child node:
|
|||
sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6,coin
|
||||
--PFUZE3000
|
||||
sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4
|
||||
--PFUZE3001
|
||||
sw1,sw2,sw3,vsnvs,vldo1,vldo2,vccsd,v33,vldo3,vldo4
|
||||
|
||||
Each regulator is defined using the standard binding for regulators.
|
||||
|
||||
|
@ -303,3 +314,76 @@ Example 3: PFUZE3000
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
Example 4: PFUZE 3001
|
||||
|
||||
pfuze3001: pmic@8 {
|
||||
compatible = "fsl,pfuze3001";
|
||||
reg = <0x08>;
|
||||
|
||||
regulators {
|
||||
sw1_reg: sw1 {
|
||||
regulator-min-microvolt = <700000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
sw2_reg: sw2 {
|
||||
regulator-min-microvolt = <1500000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
sw3_reg: sw3 {
|
||||
regulator-min-microvolt = <900000>;
|
||||
regulator-max-microvolt = <1650000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
snvs_reg: vsnvs {
|
||||
regulator-min-microvolt = <1000000>;
|
||||
regulator-max-microvolt = <3000000>;
|
||||
regulator-boot-on;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vgen1_reg: vldo1 {
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vgen2_reg: vldo2 {
|
||||
regulator-min-microvolt = <800000>;
|
||||
regulator-max-microvolt = <1550000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vgen3_reg: vccsd {
|
||||
regulator-min-microvolt = <2850000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vgen4_reg: v33 {
|
||||
regulator-min-microvolt = <2850000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vgen5_reg: vldo3 {
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vgen6_reg: vldo4 {
|
||||
regulator-min-microvolt = <1800000>;
|
||||
regulator-max-microvolt = <3300000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
||||
BD71837MWV is a programmable Power Management
|
||||
IC (PMIC) for powering single-core, dual-core, and
|
||||
quad-core SoC’s such as NXP-i.MX 8M. It is optimized
|
||||
for low BOM cost and compact solution footprint. It
|
||||
integrates 8 Buck regulators and 7 LDO’s to provide all
|
||||
the power rails required by the SoC and the commonly
|
||||
used peripherals.
|
||||
|
||||
Required properties:
|
||||
- regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7"
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
...
|
||||
};
|
|
@ -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);
|
||||
|
||||
/**
|
||||
* device_link_remove - remove a link between two devices.
|
||||
* @consumer: Consumer end of the link.
|
||||
* @supplier: Supplier end of the link.
|
||||
*
|
||||
* The caller must ensure proper synchronization of this function with runtime
|
||||
* PM.
|
||||
*/
|
||||
void device_link_remove(void *consumer, struct device *supplier)
|
||||
{
|
||||
struct device_link *link;
|
||||
|
||||
if (WARN_ON(consumer == supplier))
|
||||
return;
|
||||
|
||||
device_links_write_lock();
|
||||
device_pm_lock();
|
||||
|
||||
list_for_each_entry(link, &supplier->links.consumers, s_node) {
|
||||
if (link->consumer == consumer) {
|
||||
kref_put(&link->kref, __device_link_del);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
device_pm_unlock();
|
||||
device_links_write_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_link_remove);
|
||||
|
||||
static void device_links_missing_supplier(struct device *dev)
|
||||
{
|
||||
struct device_link *link;
|
||||
|
|
|
@ -180,9 +180,9 @@ config REGULATOR_BCM590XX
|
|||
BCM590xx PMUs. This will enable support for the software
|
||||
controllable LDO/Switching regulators.
|
||||
|
||||
config REGULATOR_BD71837
|
||||
config REGULATOR_BD718XX
|
||||
tristate "ROHM BD71837 Power Regulator"
|
||||
depends on MFD_BD71837
|
||||
depends on MFD_ROHM_BD718XX
|
||||
help
|
||||
This driver supports voltage regulators on ROHM BD71837 PMIC.
|
||||
This will enable support for the software controllable buck
|
||||
|
@ -633,12 +633,12 @@ config REGULATOR_PCF50633
|
|||
on PCF50633
|
||||
|
||||
config REGULATOR_PFUZE100
|
||||
tristate "Freescale PFUZE100/200/3000 regulator driver"
|
||||
tristate "Freescale PFUZE100/200/3000/3001 regulator driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say y here to support the regulators found on the Freescale
|
||||
PFUZE100/200/3000 PMIC.
|
||||
PFUZE100/200/3000/3001 PMIC.
|
||||
|
||||
config REGULATOR_PV88060
|
||||
tristate "Powerventure Semiconductor PV88060 regulator"
|
||||
|
@ -682,6 +682,15 @@ config REGULATOR_QCOM_RPM
|
|||
Qualcomm RPM as a module. The module will be named
|
||||
"qcom_rpm-regulator".
|
||||
|
||||
config REGULATOR_QCOM_RPMH
|
||||
tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
|
||||
depends on QCOM_RPMH || COMPILE_TEST
|
||||
help
|
||||
This driver supports control of PMIC regulators via the RPMh hardware
|
||||
block found on Qualcomm Technologies Inc. SoCs. RPMh regulator
|
||||
control allows for voting on regulator state between multiple
|
||||
processors within the SoC.
|
||||
|
||||
config REGULATOR_QCOM_SMD_RPM
|
||||
tristate "Qualcomm SMD based RPM regulator driver"
|
||||
depends on QCOM_SMD_RPM
|
||||
|
@ -950,6 +959,14 @@ config REGULATOR_TWL4030
|
|||
This driver supports the voltage regulators provided by
|
||||
this family of companion chips.
|
||||
|
||||
config REGULATOR_UNIPHIER
|
||||
tristate "UniPhier regulator driver"
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
depends on OF && MFD_SYSCON
|
||||
default ARCH_UNIPHIER
|
||||
help
|
||||
Support for regulators implemented on Socionext UniPhier SoCs.
|
||||
|
||||
config REGULATOR_VCTRL
|
||||
tristate "Voltage controlled regulators"
|
||||
depends on OF
|
||||
|
|
|
@ -27,7 +27,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BD71837) += bd71837-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BD718XX) += bd71837-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
|
||||
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
|
||||
|
@ -78,6 +78,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
|
||||
|
@ -118,6 +119,7 @@ obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
|
|||
obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_UNIPHIER) += uniphier-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
|
||||
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
|
||||
|
|
|
@ -2,19 +2,18 @@
|
|||
// Copyright (C) 2018 ROHM Semiconductors
|
||||
// bd71837-regulator.c ROHM BD71837MWV regulator driver
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/rohm-bd718x7.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mfd/bd71837.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct bd71837_pmic {
|
||||
struct regulator_desc descs[BD71837_REGULATOR_CNT];
|
||||
|
@ -39,7 +38,7 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev,
|
|||
int id = rdev->desc->id;
|
||||
unsigned int ramp_value = BUCK_RAMPRATE_10P00MV;
|
||||
|
||||
dev_dbg(&(pmic->pdev->dev), "Buck[%d] Set Ramp = %d\n", id + 1,
|
||||
dev_dbg(&pmic->pdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1,
|
||||
ramp_delay);
|
||||
switch (ramp_delay) {
|
||||
case 1 ... 1250:
|
||||
|
@ -73,14 +72,10 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev,
|
|||
static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev,
|
||||
unsigned int sel)
|
||||
{
|
||||
int ret;
|
||||
if (regulator_is_enabled_regmap(rdev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = regulator_is_enabled_regmap(rdev);
|
||||
if (!ret)
|
||||
ret = regulator_set_voltage_sel_regmap(rdev, sel);
|
||||
else if (ret == 1)
|
||||
ret = -EBUSY;
|
||||
return ret;
|
||||
return regulator_set_voltage_sel_regmap(rdev, sel);
|
||||
}
|
||||
|
||||
static struct regulator_ops bd71837_ldo_regulator_ops = {
|
||||
|
@ -195,7 +190,7 @@ static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = {
|
|||
* LDO2
|
||||
* 0.8 or 0.9V
|
||||
*/
|
||||
const unsigned int ldo_2_volts[] = {
|
||||
static const unsigned int ldo_2_volts[] = {
|
||||
900000, 800000
|
||||
};
|
||||
|
||||
|
@ -495,7 +490,6 @@ struct reg_init {
|
|||
static int bd71837_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bd71837_pmic *pmic;
|
||||
struct bd71837_board *pdata;
|
||||
struct regulator_config config = { 0 };
|
||||
struct reg_init pmic_regulator_inits[] = {
|
||||
{
|
||||
|
@ -548,8 +542,7 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||
|
||||
int i, err;
|
||||
|
||||
pmic = devm_kzalloc(&pdev->dev, sizeof(struct bd71837_pmic),
|
||||
GFP_KERNEL);
|
||||
pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
|
||||
if (!pmic)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -564,7 +557,6 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
platform_set_drvdata(pdev, pmic);
|
||||
pdata = dev_get_platdata(pmic->mfd->dev);
|
||||
|
||||
/* Register LOCK release */
|
||||
err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK,
|
||||
|
@ -573,8 +565,8 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||
dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err);
|
||||
goto err;
|
||||
} else {
|
||||
dev_dbg(&pmic->pdev->dev, "%s: Unlocked lock register 0x%x\n",
|
||||
__func__, BD71837_REG_REGLOCK);
|
||||
dev_dbg(&pmic->pdev->dev, "Unlocked lock register 0x%x\n",
|
||||
BD71837_REG_REGLOCK);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) {
|
||||
|
@ -584,9 +576,6 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||
|
||||
desc = &pmic->descs[i];
|
||||
|
||||
if (pdata)
|
||||
config.init_data = pdata->init_data[i];
|
||||
|
||||
config.dev = pdev->dev.parent;
|
||||
config.driver_data = pmic;
|
||||
config.regmap = pmic->mfd->regmap;
|
||||
|
@ -619,8 +608,6 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||
pmic->rdev[i] = rdev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
|
@ -628,7 +615,6 @@ static int bd71837_probe(struct platform_device *pdev)
|
|||
static struct platform_driver bd71837_regulator = {
|
||||
.driver = {
|
||||
.name = "bd71837-pmic",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = bd71837_probe,
|
||||
};
|
||||
|
|
|
@ -30,6 +30,7 @@ struct bd9571mwv_reg {
|
|||
/* DDR Backup Power */
|
||||
u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */
|
||||
u8 bkup_mode_cnt_saved;
|
||||
bool bkup_mode_enabled;
|
||||
|
||||
/* Power switch type */
|
||||
bool rstbmode_level;
|
||||
|
@ -171,13 +172,60 @@ static int bd9571mwv_bkup_mode_write(struct bd9571mwv *bd, unsigned int mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t backup_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%s\n", bdreg->bkup_mode_enabled ? "on" : "off");
|
||||
}
|
||||
|
||||
static ssize_t backup_mode_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
||||
unsigned int mode;
|
||||
int ret;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
ret = kstrtobool(buf, &bdreg->bkup_mode_enabled);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!bdreg->rstbmode_level)
|
||||
return count;
|
||||
|
||||
/*
|
||||
* Configure DDR Backup Mode, to change the role of the accessory power
|
||||
* switch from a power switch to a wake-up switch, or vice versa
|
||||
*/
|
||||
ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK;
|
||||
if (bdreg->bkup_mode_enabled)
|
||||
mode |= bdreg->bkup_mode_cnt_keepon;
|
||||
|
||||
ret = bd9571mwv_bkup_mode_write(bdreg->bd, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(backup_mode);
|
||||
|
||||
static int bd9571mwv_suspend(struct device *dev)
|
||||
{
|
||||
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
||||
unsigned int mode;
|
||||
int ret;
|
||||
|
||||
if (!device_may_wakeup(dev))
|
||||
if (!bdreg->bkup_mode_enabled)
|
||||
return 0;
|
||||
|
||||
/* Save DDR Backup Mode */
|
||||
|
@ -204,7 +252,7 @@ static int bd9571mwv_resume(struct device *dev)
|
|||
{
|
||||
struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev);
|
||||
|
||||
if (!device_may_wakeup(dev))
|
||||
if (!bdreg->bkup_mode_enabled)
|
||||
return 0;
|
||||
|
||||
/* Restore DDR Backup Mode */
|
||||
|
@ -215,9 +263,15 @@ static const struct dev_pm_ops bd9571mwv_pm = {
|
|||
SET_SYSTEM_SLEEP_PM_OPS(bd9571mwv_suspend, bd9571mwv_resume)
|
||||
};
|
||||
|
||||
static int bd9571mwv_regulator_remove(struct platform_device *pdev)
|
||||
{
|
||||
device_remove_file(&pdev->dev, &dev_attr_backup_mode);
|
||||
return 0;
|
||||
}
|
||||
#define DEV_PM_OPS &bd9571mwv_pm
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#define bd9571mwv_regulator_remove NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static int bd9571mwv_regulator_probe(struct platform_device *pdev)
|
||||
|
@ -270,14 +324,21 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
if (bdreg->bkup_mode_cnt_keepon) {
|
||||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Wakeup is enabled by default in pulse mode, but needs
|
||||
* Backup mode is enabled by default in pulse mode, but needs
|
||||
* explicit user setup in level mode.
|
||||
*/
|
||||
device_set_wakeup_enable(&pdev->dev, bdreg->rstbmode_pulse);
|
||||
bdreg->bkup_mode_enabled = bdreg->rstbmode_pulse;
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_backup_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -294,6 +355,7 @@ static struct platform_driver bd9571mwv_regulator_driver = {
|
|||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
.probe = bd9571mwv_regulator_probe,
|
||||
.remove = bd9571mwv_regulator_remove,
|
||||
.id_table = bd9571mwv_regulator_id_table,
|
||||
};
|
||||
module_platform_driver(bd9571mwv_regulator_driver);
|
||||
|
|
|
@ -1740,6 +1740,8 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
|
|||
rdev->use_count = 0;
|
||||
}
|
||||
|
||||
device_link_add(dev, &rdev->dev, DL_FLAG_STATELESS);
|
||||
|
||||
return regulator;
|
||||
}
|
||||
|
||||
|
@ -1829,9 +1831,21 @@ static void _regulator_put(struct regulator *regulator)
|
|||
|
||||
debugfs_remove_recursive(regulator->debugfs);
|
||||
|
||||
/* remove any sysfs entries */
|
||||
if (regulator->dev)
|
||||
if (regulator->dev) {
|
||||
int count = 0;
|
||||
struct regulator *r;
|
||||
|
||||
list_for_each_entry(r, &rdev->consumer_list, list)
|
||||
if (r->dev == regulator->dev)
|
||||
count++;
|
||||
|
||||
if (count == 1)
|
||||
device_link_remove(regulator->dev, &rdev->dev);
|
||||
|
||||
/* remove any sysfs entries */
|
||||
sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
|
||||
}
|
||||
|
||||
regulator_lock(rdev);
|
||||
list_del(®ulator->list);
|
||||
|
||||
|
@ -4441,7 +4455,7 @@ void regulator_unregister(struct regulator_dev *rdev)
|
|||
EXPORT_SYMBOL_GPL(regulator_unregister);
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
static int _regulator_suspend_late(struct device *dev, void *data)
|
||||
static int _regulator_suspend(struct device *dev, void *data)
|
||||
{
|
||||
struct regulator_dev *rdev = dev_to_rdev(dev);
|
||||
suspend_state_t *state = data;
|
||||
|
@ -4455,20 +4469,20 @@ static int _regulator_suspend_late(struct device *dev, void *data)
|
|||
}
|
||||
|
||||
/**
|
||||
* regulator_suspend_late - prepare regulators for system wide suspend
|
||||
* regulator_suspend - prepare regulators for system wide suspend
|
||||
* @state: system suspend state
|
||||
*
|
||||
* Configure each regulator with it's suspend operating parameters for state.
|
||||
*/
|
||||
static int regulator_suspend_late(struct device *dev)
|
||||
static int regulator_suspend(struct device *dev)
|
||||
{
|
||||
suspend_state_t state = pm_suspend_target_state;
|
||||
|
||||
return class_for_each_device(®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;
|
||||
struct regulator_dev *rdev = dev_to_rdev(dev);
|
||||
|
@ -4481,35 +4495,35 @@ static int _regulator_resume_early(struct device *dev, void *data)
|
|||
|
||||
regulator_lock(rdev);
|
||||
|
||||
if (rdev->desc->ops->resume_early &&
|
||||
if (rdev->desc->ops->resume &&
|
||||
(rstate->enabled == ENABLE_IN_SUSPEND ||
|
||||
rstate->enabled == DISABLE_IN_SUSPEND))
|
||||
ret = rdev->desc->ops->resume_early(rdev);
|
||||
ret = rdev->desc->ops->resume(rdev);
|
||||
|
||||
regulator_unlock(rdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int regulator_resume_early(struct device *dev)
|
||||
static int regulator_resume(struct device *dev)
|
||||
{
|
||||
suspend_state_t state = pm_suspend_target_state;
|
||||
|
||||
return class_for_each_device(®ulator_class, NULL, &state,
|
||||
_regulator_resume_early);
|
||||
_regulator_resume);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_SUSPEND */
|
||||
|
||||
#define regulator_suspend_late NULL
|
||||
#define regulator_resume_early NULL
|
||||
#define regulator_suspend NULL
|
||||
#define regulator_resume NULL
|
||||
|
||||
#endif /* !CONFIG_SUSPEND */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static const struct dev_pm_ops __maybe_unused regulator_pm_ops = {
|
||||
.suspend_late = regulator_suspend_late,
|
||||
.resume_early = regulator_resume_early,
|
||||
.suspend = regulator_suspend,
|
||||
.resume = regulator_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -271,6 +271,29 @@ static struct regulator_ops cpcap_regulator_ops = {
|
|||
};
|
||||
|
||||
static const unsigned int unknown_val_tbl[] = { 0, };
|
||||
static const unsigned int sw2_sw4_val_tbl[] = { 612500, 625000, 637500,
|
||||
650000, 662500, 675000,
|
||||
687500, 700000, 712500,
|
||||
725000, 737500, 750000,
|
||||
762500, 775000, 787500,
|
||||
800000, 812500, 825000,
|
||||
837500, 850000, 862500,
|
||||
875000, 887500, 900000,
|
||||
912500, 925000, 937500,
|
||||
950000, 962500, 975000,
|
||||
987500, 1000000, 1012500,
|
||||
1025000, 1037500, 1050000,
|
||||
1062500, 1075000, 1087500,
|
||||
1100000, 1112500, 1125000,
|
||||
1137500, 1150000, 1162500,
|
||||
1175000, 1187500, 1200000,
|
||||
1212500, 1225000, 1237500,
|
||||
1250000, 1262500, 1275000,
|
||||
1287500, 1300000, 1312500,
|
||||
1325000, 1337500, 1350000,
|
||||
1362500, 1375000, 1387500,
|
||||
1400000, 1412500, 1425000,
|
||||
1437500, 1450000, 1462500, };
|
||||
static const unsigned int sw5_val_tbl[] = { 0, 5050000, };
|
||||
static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000,
|
||||
2900000, };
|
||||
|
@ -389,6 +412,82 @@ static struct cpcap_regulator omap4_regulators[] = {
|
|||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct cpcap_regulator xoom_regulators[] = {
|
||||
CPCAP_REG(SW1, CPCAP_REG_S1C1, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_SW1_SEL, unknown_val_tbl,
|
||||
0, 0, 0, 0, 0, 0),
|
||||
CPCAP_REG(SW2, CPCAP_REG_S2C1, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_SW2_SEL, sw2_sw4_val_tbl,
|
||||
0xf00, 0x7f, 0, 0x800, 0, 120),
|
||||
CPCAP_REG(SW3, CPCAP_REG_S3C, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_SW3_SEL, unknown_val_tbl,
|
||||
0, 0, 0, 0, 0, 0),
|
||||
CPCAP_REG(SW4, CPCAP_REG_S4C1, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_SW4_SEL, sw2_sw4_val_tbl,
|
||||
0xf00, 0x7f, 0, 0x900, 0, 100),
|
||||
CPCAP_REG(SW5, CPCAP_REG_S5C, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_SW5_SEL, sw5_val_tbl,
|
||||
0x2a, 0, 0, 0x22, 0, 0),
|
||||
CPCAP_REG(SW6, CPCAP_REG_S6C, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_SW6_SEL, unknown_val_tbl,
|
||||
0, 0, 0, 0, 0, 0),
|
||||
CPCAP_REG(VCAM, CPCAP_REG_VCAMC, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_VCAM_SEL, vcam_val_tbl,
|
||||
0x87, 0x30, 4, 0x7, 0, 420),
|
||||
CPCAP_REG(VCSI, CPCAP_REG_VCSIC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VCSI_SEL, vcsi_val_tbl,
|
||||
0x47, 0x10, 4, 0x7, 0, 350),
|
||||
CPCAP_REG(VDAC, CPCAP_REG_VDACC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VDAC_SEL, vdac_val_tbl,
|
||||
0x87, 0x30, 4, 0x3, 0, 420),
|
||||
CPCAP_REG(VDIG, CPCAP_REG_VDIGC, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_VDIG_SEL, vdig_val_tbl,
|
||||
0x87, 0x30, 4, 0x5, 0, 420),
|
||||
CPCAP_REG(VFUSE, CPCAP_REG_VFUSEC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VFUSE_SEL, vfuse_val_tbl,
|
||||
0x80, 0xf, 0, 0x80, 0, 420),
|
||||
CPCAP_REG(VHVIO, CPCAP_REG_VHVIOC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VHVIO_SEL, vhvio_val_tbl,
|
||||
0x17, 0, 0, 0x2, 0, 0),
|
||||
CPCAP_REG(VSDIO, CPCAP_REG_VSDIOC, CPCAP_REG_ASSIGN2,
|
||||
CPCAP_BIT_VSDIO_SEL, vsdio_val_tbl,
|
||||
0x87, 0x38, 3, 0x2, 0, 420),
|
||||
CPCAP_REG(VPLL, CPCAP_REG_VPLLC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VPLL_SEL, vpll_val_tbl,
|
||||
0x43, 0x18, 3, 0x1, 0, 420),
|
||||
CPCAP_REG(VRF1, CPCAP_REG_VRF1C, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VRF1_SEL, vrf1_val_tbl,
|
||||
0xac, 0x2, 1, 0xc, 0, 10),
|
||||
CPCAP_REG(VRF2, CPCAP_REG_VRF2C, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VRF2_SEL, vrf2_val_tbl,
|
||||
0x23, 0x8, 3, 0x3, 0, 10),
|
||||
CPCAP_REG(VRFREF, CPCAP_REG_VRFREFC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VRFREF_SEL, vrfref_val_tbl,
|
||||
0x23, 0x8, 3, 0x3, 0, 420),
|
||||
CPCAP_REG(VWLAN1, CPCAP_REG_VWLAN1C, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VWLAN1_SEL, vwlan1_val_tbl,
|
||||
0x47, 0x10, 4, 0x5, 0, 420),
|
||||
CPCAP_REG(VWLAN2, CPCAP_REG_VWLAN2C, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VWLAN2_SEL, vwlan2_val_tbl,
|
||||
0x20c, 0xc0, 6, 0x8, 0, 420),
|
||||
CPCAP_REG(VSIM, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
|
||||
0xffff, vsim_val_tbl,
|
||||
0x23, 0x8, 3, 0x3, 0, 420),
|
||||
CPCAP_REG(VSIMCARD, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3,
|
||||
0xffff, vsimcard_val_tbl,
|
||||
0x1e80, 0x8, 3, 0x1e00, 0, 420),
|
||||
CPCAP_REG(VVIB, CPCAP_REG_VVIBC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VVIB_SEL, vvib_val_tbl,
|
||||
0x1, 0xc, 2, 0, 0x1, 500),
|
||||
CPCAP_REG(VUSB, CPCAP_REG_VUSBC, CPCAP_REG_ASSIGN3,
|
||||
CPCAP_BIT_VUSB_SEL, vusb_val_tbl,
|
||||
0x11c, 0x40, 6, 0xc, 0, 0),
|
||||
CPCAP_REG(VAUDIO, CPCAP_REG_VAUDIOC, CPCAP_REG_ASSIGN4,
|
||||
CPCAP_BIT_VAUDIO_SEL, vaudio_val_tbl,
|
||||
0x16, 0x1, 0, 0x4, 0, 0),
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static const struct of_device_id cpcap_regulator_id_table[] = {
|
||||
{
|
||||
.compatible = "motorola,cpcap-regulator",
|
||||
|
@ -397,6 +496,10 @@ static const struct of_device_id cpcap_regulator_id_table[] = {
|
|||
.compatible = "motorola,mapphone-cpcap-regulator",
|
||||
.data = omap4_regulators,
|
||||
},
|
||||
{
|
||||
.compatible = "motorola,xoom-cpcap-regulator",
|
||||
.data = xoom_regulators,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table);
|
||||
|
|
|
@ -1,19 +1,9 @@
|
|||
/*
|
||||
* max14577.c - Regulator driver for the Maxim 14577/77836
|
||||
*
|
||||
* Copyright (C) 2013,2014 Samsung Electronics
|
||||
* Krzysztof Kozlowski <krzk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// max14577.c - Regulator driver for the Maxim 14577/77836
|
||||
//
|
||||
// Copyright (C) 2013,2014 Samsung Electronics
|
||||
// Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
/*
|
||||
* max77686.c - Regulator driver for the Maxim 77686
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Chiwoong Byun <woong.byun@samsung.com>
|
||||
* Jonghwa Lee <jonghwa3.lee@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This driver is based on max8997.c
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// max77686.c - Regulator driver for the Maxim 77686
|
||||
//
|
||||
// Copyright (C) 2012 Samsung Electronics
|
||||
// Chiwoong Byun <woong.byun@samsung.com>
|
||||
// Jonghwa Lee <jonghwa3.lee@samsung.com>
|
||||
//
|
||||
// This driver is based on max8997.c
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bug.h>
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
/*
|
||||
* max77693.c - Regulator driver for the Maxim 77693 and 77843
|
||||
*
|
||||
* Copyright (C) 2013-2015 Samsung Electronics
|
||||
* Jonghwa Lee <jonghwa3.lee@samsung.com>
|
||||
* Krzysztof Kozlowski <krzk@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This driver is based on max77686.c
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// max77693.c - Regulator driver for the Maxim 77693 and 77843
|
||||
//
|
||||
// Copyright (C) 2013-2015 Samsung Electronics
|
||||
// Jonghwa Lee <jonghwa3.lee@samsung.com>
|
||||
// Krzysztof Kozlowski <krzk@kernel.org>
|
||||
//
|
||||
// This driver is based on max77686.c
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
/*
|
||||
* max77802.c - Regulator driver for the Maxim 77802
|
||||
*
|
||||
* Copyright (C) 2013-2014 Google, Inc
|
||||
* Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics
|
||||
* Chiwoong Byun <woong.byun@samsung.com>
|
||||
* Jonghwa Lee <jonghwa3.lee@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* This driver is based on max8997.c
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// max77802.c - Regulator driver for the Maxim 77802
|
||||
//
|
||||
// Copyright (C) 2013-2014 Google, Inc
|
||||
// Simon Glass <sjg@chromium.org>
|
||||
//
|
||||
// Copyright (C) 2012 Samsung Electronics
|
||||
// Chiwoong Byun <woong.byun@samsung.com>
|
||||
// Jonghwa Lee <jonghwa3.lee@samsung.com>
|
||||
//
|
||||
// This driver is based on max8997.c
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bug.h>
|
||||
|
|
|
@ -1,25 +1,11 @@
|
|||
/*
|
||||
* max8997.c - Regulator driver for the Maxim 8997/8966
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics
|
||||
* MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This driver is based on max8998.c
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// max8997.c - Regulator driver for the Maxim 8997/8966
|
||||
//
|
||||
// Copyright (C) 2011 Samsung Electronics
|
||||
// MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
//
|
||||
// This driver is based on max8998.c
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/err.h>
|
||||
|
@ -165,8 +151,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev,
|
|||
int rid = rdev_get_id(rdev);
|
||||
int val;
|
||||
|
||||
if (rid >= ARRAY_SIZE(reg_voltage_map) ||
|
||||
rid < 0)
|
||||
if (rid < 0 || rid >= ARRAY_SIZE(reg_voltage_map))
|
||||
return -EINVAL;
|
||||
|
||||
desc = reg_voltage_map[rid];
|
||||
|
|
|
@ -1,24 +1,10 @@
|
|||
/*
|
||||
* max8998.c - Voltage regulator driver for the Maxim 8998
|
||||
*
|
||||
* Copyright (C) 2009-2010 Samsung Electronics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
* Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// max8998.c - Voltage regulator driver for the Maxim 8998
|
||||
//
|
||||
// Copyright (C) 2009-2010 Samsung Electronics
|
||||
// Kyungmin Park <kyungmin.park@samsung.com>
|
||||
// Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define PFUZE_FLAG_DISABLE_SW BIT(1)
|
||||
|
||||
#define PFUZE_NUMREGS 128
|
||||
#define PFUZE100_VOL_OFFSET 0
|
||||
#define PFUZE100_STANDBY_OFFSET 1
|
||||
|
@ -44,16 +46,18 @@
|
|||
#define PFUZE100_VGEN5VOL 0x70
|
||||
#define PFUZE100_VGEN6VOL 0x71
|
||||
|
||||
enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3 };
|
||||
enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, };
|
||||
|
||||
struct pfuze_regulator {
|
||||
struct regulator_desc desc;
|
||||
unsigned char stby_reg;
|
||||
unsigned char stby_mask;
|
||||
bool sw_reg;
|
||||
};
|
||||
|
||||
struct pfuze_chip {
|
||||
int chip_id;
|
||||
int flags;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR];
|
||||
|
@ -92,6 +96,7 @@ static const struct i2c_device_id pfuze_device_id[] = {
|
|||
{.name = "pfuze100", .driver_data = PFUZE100},
|
||||
{.name = "pfuze200", .driver_data = PFUZE200},
|
||||
{.name = "pfuze3000", .driver_data = PFUZE3000},
|
||||
{.name = "pfuze3001", .driver_data = PFUZE3001},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pfuze_device_id);
|
||||
|
@ -100,6 +105,7 @@ static const struct of_device_id pfuze_dt_ids[] = {
|
|||
{ .compatible = "fsl,pfuze100", .data = (void *)PFUZE100},
|
||||
{ .compatible = "fsl,pfuze200", .data = (void *)PFUZE200},
|
||||
{ .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000},
|
||||
{ .compatible = "fsl,pfuze3001", .data = (void *)PFUZE3001},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pfuze_dt_ids);
|
||||
|
@ -108,10 +114,28 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
|
|||
{
|
||||
struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev);
|
||||
int id = rdev_get_id(rdev);
|
||||
bool reg_has_ramp_delay;
|
||||
unsigned int ramp_bits;
|
||||
int ret;
|
||||
|
||||
if (id < PFUZE100_SWBST) {
|
||||
switch (pfuze100->chip_id) {
|
||||
case PFUZE3001:
|
||||
/* no dynamic voltage scaling for PF3001 */
|
||||
reg_has_ramp_delay = false;
|
||||
break;
|
||||
case PFUZE3000:
|
||||
reg_has_ramp_delay = (id < PFUZE3000_SWBST);
|
||||
break;
|
||||
case PFUZE200:
|
||||
reg_has_ramp_delay = (id < PFUZE200_SWBST);
|
||||
break;
|
||||
case PFUZE100:
|
||||
default:
|
||||
reg_has_ramp_delay = (id < PFUZE100_SWBST);
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg_has_ramp_delay) {
|
||||
ramp_delay = 12500 / ramp_delay;
|
||||
ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3);
|
||||
ret = regmap_update_bits(pfuze100->regmap,
|
||||
|
@ -119,8 +143,9 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
|
|||
0xc0, ramp_bits << 6);
|
||||
if (ret < 0)
|
||||
dev_err(pfuze100->dev, "ramp failed, err %d\n", ret);
|
||||
} else
|
||||
} else {
|
||||
ret = -EACCES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -142,6 +167,14 @@ static const struct regulator_ops pfuze100_fixed_regulator_ops = {
|
|||
};
|
||||
|
||||
static const struct regulator_ops pfuze100_sw_regulator_ops = {
|
||||
.list_voltage = regulator_list_voltage_linear,
|
||||
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
||||
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
||||
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
||||
.set_ramp_delay = pfuze100_set_ramp_delay,
|
||||
};
|
||||
|
||||
static const struct regulator_ops pfuze100_sw_disable_regulator_ops = {
|
||||
.enable = regulator_enable_regmap,
|
||||
.disable = regulator_disable_regmap,
|
||||
.is_enabled = regulator_is_enabled_regmap,
|
||||
|
@ -192,13 +225,11 @@ static const struct regulator_ops pfuze100_swb_regulator_ops = {
|
|||
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
|
||||
.vsel_mask = 0x3f, \
|
||||
.enable_reg = (base) + PFUZE100_MODE_OFFSET, \
|
||||
.enable_val = 0xc, \
|
||||
.disable_val = 0x0, \
|
||||
.enable_mask = 0xf, \
|
||||
.enable_time = 500, \
|
||||
}, \
|
||||
.stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \
|
||||
.stby_mask = 0x3f, \
|
||||
.sw_reg = true, \
|
||||
}
|
||||
|
||||
#define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages) \
|
||||
|
@ -361,6 +392,19 @@ static struct pfuze_regulator pfuze3000_regulators[] = {
|
|||
PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
|
||||
};
|
||||
|
||||
static struct pfuze_regulator pfuze3001_regulators[] = {
|
||||
PFUZE100_SWB_REG(PFUZE3001, SW1, PFUZE100_SW1ABVOL, 0x1f, pfuze3000_sw1a),
|
||||
PFUZE100_SWB_REG(PFUZE3001, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo),
|
||||
PFUZE3000_SW3_REG(PFUZE3001, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000),
|
||||
PFUZE100_SWB_REG(PFUZE3001, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
|
||||
PFUZE100_VGEN_REG(PFUZE3001, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000),
|
||||
PFUZE100_VGEN_REG(PFUZE3001, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000),
|
||||
PFUZE3000_VCC_REG(PFUZE3001, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000),
|
||||
PFUZE3000_VCC_REG(PFUZE3001, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000),
|
||||
PFUZE100_VGEN_REG(PFUZE3001, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
|
||||
PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* PFUZE100 */
|
||||
static struct of_regulator_match pfuze100_matches[] = {
|
||||
|
@ -418,6 +462,21 @@ static struct of_regulator_match pfuze3000_matches[] = {
|
|||
{ .name = "vldo4", },
|
||||
};
|
||||
|
||||
/* PFUZE3001 */
|
||||
static struct of_regulator_match pfuze3001_matches[] = {
|
||||
|
||||
{ .name = "sw1", },
|
||||
{ .name = "sw2", },
|
||||
{ .name = "sw3", },
|
||||
{ .name = "vsnvs", },
|
||||
{ .name = "vldo1", },
|
||||
{ .name = "vldo2", },
|
||||
{ .name = "vccsd", },
|
||||
{ .name = "v33", },
|
||||
{ .name = "vldo3", },
|
||||
{ .name = "vldo4", },
|
||||
};
|
||||
|
||||
static struct of_regulator_match *pfuze_matches;
|
||||
|
||||
static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
|
||||
|
@ -430,6 +489,9 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
|
|||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
if (of_property_read_bool(np, "fsl,pfuze-support-disable-sw"))
|
||||
chip->flags |= PFUZE_FLAG_DISABLE_SW;
|
||||
|
||||
parent = of_get_child_by_name(np, "regulators");
|
||||
if (!parent) {
|
||||
dev_err(dev, "regulators node not found\n");
|
||||
|
@ -437,6 +499,11 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
|
|||
}
|
||||
|
||||
switch (chip->chip_id) {
|
||||
case PFUZE3001:
|
||||
pfuze_matches = pfuze3001_matches;
|
||||
ret = of_regulator_match(dev, parent, pfuze3001_matches,
|
||||
ARRAY_SIZE(pfuze3001_matches));
|
||||
break;
|
||||
case PFUZE3000:
|
||||
pfuze_matches = pfuze3000_matches;
|
||||
ret = of_regulator_match(dev, parent, pfuze3000_matches,
|
||||
|
@ -508,7 +575,8 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip)
|
|||
*/
|
||||
dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8");
|
||||
} else if ((value & 0x0f) != pfuze_chip->chip_id &&
|
||||
(value & 0xf0) >> 4 != pfuze_chip->chip_id) {
|
||||
(value & 0xf0) >> 4 != pfuze_chip->chip_id &&
|
||||
(value != pfuze_chip->chip_id)) {
|
||||
/* device id NOT match with your setting */
|
||||
dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
|
||||
return -ENODEV;
|
||||
|
@ -588,6 +656,13 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
|
|||
|
||||
/* use the right regulators after identify the right device */
|
||||
switch (pfuze_chip->chip_id) {
|
||||
case PFUZE3001:
|
||||
pfuze_chip->pfuze_regulators = pfuze3001_regulators;
|
||||
regulator_num = ARRAY_SIZE(pfuze3001_regulators);
|
||||
sw_check_start = PFUZE3001_SW2;
|
||||
sw_check_end = PFUZE3001_SW2;
|
||||
sw_hi = 1 << 3;
|
||||
break;
|
||||
case PFUZE3000:
|
||||
pfuze_chip->pfuze_regulators = pfuze3000_regulators;
|
||||
regulator_num = ARRAY_SIZE(pfuze3000_regulators);
|
||||
|
@ -611,7 +686,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
|
|||
}
|
||||
dev_info(&client->dev, "pfuze%s found.\n",
|
||||
(pfuze_chip->chip_id == PFUZE100) ? "100" :
|
||||
((pfuze_chip->chip_id == PFUZE200) ? "200" : "3000"));
|
||||
(((pfuze_chip->chip_id == PFUZE200) ? "200" :
|
||||
((pfuze_chip->chip_id == PFUZE3000) ? "3000" : "3001"))));
|
||||
|
||||
memcpy(pfuze_chip->regulator_descs, pfuze_chip->pfuze_regulators,
|
||||
sizeof(pfuze_chip->regulator_descs));
|
||||
|
@ -636,7 +712,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
|
|||
if (i >= sw_check_start && i <= sw_check_end) {
|
||||
regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val);
|
||||
if (val & sw_hi) {
|
||||
if (pfuze_chip->chip_id == PFUZE3000) {
|
||||
if (pfuze_chip->chip_id == PFUZE3000 ||
|
||||
pfuze_chip->chip_id == PFUZE3001) {
|
||||
desc->volt_table = pfuze3000_sw2hi;
|
||||
desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi);
|
||||
} else {
|
||||
|
@ -647,6 +724,21 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow SW regulators to turn off. Checking it trough a flag is
|
||||
* a workaround to keep the backward compatibility with existing
|
||||
* old dtb's which may relay on the fact that we didn't disable
|
||||
* the switched regulator till yet.
|
||||
*/
|
||||
if (pfuze_chip->flags & PFUZE_FLAG_DISABLE_SW) {
|
||||
if (pfuze_chip->regulator_descs[i].sw_reg) {
|
||||
desc->ops = &pfuze100_sw_disable_regulator_ops;
|
||||
desc->enable_val = 0x8;
|
||||
desc->disable_val = 0x0;
|
||||
desc->enable_time = 500;
|
||||
}
|
||||
}
|
||||
|
||||
config.dev = &client->dev;
|
||||
config.init_data = init_data;
|
||||
config.driver_data = pfuze_chip;
|
||||
|
@ -675,5 +767,5 @@ static struct i2c_driver pfuze_driver = {
|
|||
module_i2c_driver(pfuze_driver);
|
||||
|
||||
MODULE_AUTHOR("Robin Gong <b38343@freescale.com>");
|
||||
MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000 PMIC");
|
||||
MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000/3001 PMIC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -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_CLEAR_MASK 0x7efc00
|
||||
|
||||
static struct regmap *saw_regmap = NULL;
|
||||
static struct regmap *saw_regmap;
|
||||
|
||||
static void spmi_saw_set_vdd(void *data)
|
||||
{
|
||||
|
@ -1728,7 +1728,7 @@ static const struct spmi_regulator_data pmi8994_regulators[] = {
|
|||
{ "s2", 0x1700, "vdd_s2", },
|
||||
{ "s3", 0x1a00, "vdd_s3", },
|
||||
{ "l1", 0x4000, "vdd_l1", },
|
||||
{ }
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct of_device_id qcom_spmi_regulator_match[] = {
|
||||
|
@ -1752,7 +1752,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
|
|||
const char *name;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device_node *syscon;
|
||||
struct device_node *syscon, *reg_node;
|
||||
struct property *reg_prop;
|
||||
int ret, lenp;
|
||||
struct list_head *vreg_list;
|
||||
|
||||
|
@ -1774,16 +1775,19 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
|
|||
syscon = of_parse_phandle(node, "qcom,saw-reg", 0);
|
||||
saw_regmap = syscon_node_to_regmap(syscon);
|
||||
of_node_put(syscon);
|
||||
if (IS_ERR(regmap))
|
||||
if (IS_ERR(saw_regmap))
|
||||
dev_err(dev, "ERROR reading SAW regmap\n");
|
||||
}
|
||||
|
||||
for (reg = match->data; reg->name; reg++) {
|
||||
|
||||
if (saw_regmap && \
|
||||
of_find_property(of_find_node_by_name(node, reg->name), \
|
||||
"qcom,saw-slave", &lenp)) {
|
||||
continue;
|
||||
if (saw_regmap) {
|
||||
reg_node = of_get_child_by_name(node, reg->name);
|
||||
reg_prop = of_find_property(reg_node, "qcom,saw-slave",
|
||||
&lenp);
|
||||
of_node_put(reg_node);
|
||||
if (reg_prop)
|
||||
continue;
|
||||
}
|
||||
|
||||
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
|
||||
|
@ -1816,13 +1820,17 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
continue;
|
||||
|
||||
if (saw_regmap && \
|
||||
of_find_property(of_find_node_by_name(node, reg->name), \
|
||||
"qcom,saw-leader", &lenp)) {
|
||||
spmi_saw_ops = *(vreg->desc.ops);
|
||||
spmi_saw_ops.set_voltage_sel = \
|
||||
spmi_regulator_saw_set_voltage;
|
||||
vreg->desc.ops = &spmi_saw_ops;
|
||||
if (saw_regmap) {
|
||||
reg_node = of_get_child_by_name(node, reg->name);
|
||||
reg_prop = of_find_property(reg_node, "qcom,saw-leader",
|
||||
&lenp);
|
||||
of_node_put(reg_node);
|
||||
if (reg_prop) {
|
||||
spmi_saw_ops = *(vreg->desc.ops);
|
||||
spmi_saw_ops.set_voltage_sel =
|
||||
spmi_regulator_saw_set_voltage;
|
||||
vreg->desc.ops = &spmi_saw_ops;
|
||||
}
|
||||
}
|
||||
|
||||
config.dev = dev;
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Copyright (c) 2013 Samsung Electronics Co., Ltd
|
||||
// http://www.samsung.com
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
@ -1,20 +1,7 @@
|
|||
/*
|
||||
* s2mps11.c
|
||||
*
|
||||
* Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Copyright (c) 2012-2014 Samsung Electronics Co., Ltd
|
||||
// http://www.samsung.com
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/err.h>
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
/*
|
||||
* s5m8767.c
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Copyright (c) 2011 Samsung Electronics Co., Ltd
|
||||
// http://www.samsung.com
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
|
|
@ -232,6 +232,8 @@ static int tps65217_regulator_probe(struct platform_device *pdev)
|
|||
tps->strobes = devm_kcalloc(&pdev->dev,
|
||||
TPS65217_NUM_REGULATOR, sizeof(u8),
|
||||
GFP_KERNEL);
|
||||
if (!tps->strobes)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, tps);
|
||||
|
||||
|
|
|
@ -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
|
||||
devices to the output pins.
|
||||
|
||||
config QCOM_LLCC
|
||||
tristate "Qualcomm Technologies, Inc. LLCC driver"
|
||||
depends on ARCH_QCOM
|
||||
help
|
||||
Qualcomm Technologies, Inc. platform specific
|
||||
Last Level Cache Controller(LLCC) driver. This provides interfaces
|
||||
to clients that use the LLCC. Say yes here to enable LLCC slice
|
||||
driver.
|
||||
|
||||
config QCOM_SDM845_LLCC
|
||||
tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver"
|
||||
depends on QCOM_LLCC
|
||||
help
|
||||
Say yes here to enable the LLCC driver for SDM845. This provides
|
||||
data required to configure LLCC so that clients can start using the
|
||||
LLCC slices.
|
||||
|
||||
config QCOM_MDT_LOADER
|
||||
tristate
|
||||
select QCOM_SCM
|
||||
|
@ -75,6 +92,16 @@ config QCOM_RMTFS_MEM
|
|||
|
||||
Say y here if you intend to boot the modem remoteproc.
|
||||
|
||||
config QCOM_RPMH
|
||||
bool "Qualcomm RPM-Hardened (RPMH) Communication"
|
||||
depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST
|
||||
help
|
||||
Support for communication with the hardened-RPM blocks in
|
||||
Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
|
||||
internal bus to transmit state requests for shared resources. A set
|
||||
of hardware components aggregate requests for these resources and
|
||||
help apply the aggregated state on the resource.
|
||||
|
||||
config QCOM_SMEM
|
||||
tristate "Qualcomm Shared Memory Manager (SMEM)"
|
||||
depends on ARCH_QCOM
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
CFLAGS_rpmh-rsc.o := -I$(src)
|
||||
obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o
|
||||
obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
|
||||
obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
|
||||
|
@ -8,6 +9,9 @@ obj-$(CONFIG_QCOM_PM) += spm.o
|
|||
obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o
|
||||
qmi_helpers-y += qmi_encdec.o qmi_interface.o
|
||||
obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o
|
||||
obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o
|
||||
qcom_rpmh-y += rpmh-rsc.o
|
||||
qcom_rpmh-y += rpmh.o
|
||||
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
|
||||
obj-$(CONFIG_QCOM_SMEM) += smem.o
|
||||
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
|
||||
|
@ -15,3 +19,5 @@ obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
|
|||
obj-$(CONFIG_QCOM_SMSM) += smsm.o
|
||||
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
|
||||
obj-$(CONFIG_QCOM_APR) += apr.o
|
||||
obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o
|
||||
obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
rmtfs_mem->dev.parent = &pdev->dev;
|
||||
rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups;
|
||||
rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
|
||||
|
||||
rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr,
|
||||
rmtfs_mem->size, MEMREMAP_WC);
|
||||
|
@ -206,8 +207,6 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev)
|
|||
goto put_device;
|
||||
}
|
||||
|
||||
rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device;
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,vmid", &vmid);
|
||||
if (ret < 0 && ret != -EINVAL) {
|
||||
dev_err(&pdev->dev, "failed to parse qcom,vmid\n");
|
||||
|
|
|
@ -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__ */
|
|
@ -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);
|
|
@ -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);
|
||||
cached = phdr_to_last_cached_entry(phdr);
|
||||
|
||||
if (smem->global_partition) {
|
||||
dev_err(smem->dev, "Already found the global partition\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (hdr < end) {
|
||||
if (hdr->canary != SMEM_PRIVATE_CANARY)
|
||||
goto bad_canary;
|
||||
|
@ -736,6 +731,11 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem)
|
|||
bool found = false;
|
||||
int i;
|
||||
|
||||
if (smem->global_partition) {
|
||||
dev_err(smem->dev, "Already found the global partition\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ptable = qcom_smem_get_ptable(smem);
|
||||
if (IS_ERR(ptable))
|
||||
return PTR_ERR(ptable);
|
||||
|
|
|
@ -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
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 *supplier, u32 flags);
|
||||
void device_link_del(struct device_link *link);
|
||||
void device_link_remove(void *consumer, struct device *supplier);
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
|
||||
|
|
|
@ -87,6 +87,10 @@ static inline int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr,
|
|||
static inline int
|
||||
qcom_scm_pas_auth_and_reset(u32 peripheral) { return -ENODEV; }
|
||||
static inline int qcom_scm_pas_shutdown(u32 peripheral) { return -ENODEV; }
|
||||
static inline int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz,
|
||||
unsigned int *src,
|
||||
struct qcom_scm_vmperm *newvm,
|
||||
int dest_cnt) { return -ENODEV; }
|
||||
static inline void qcom_scm_cpu_power_down(u32 flags) {}
|
||||
static inline u32 qcom_scm_get_version(void) { return 0; }
|
||||
static inline u32
|
||||
|
|
|
@ -46,7 +46,7 @@ enum regulator_status {
|
|||
/**
|
||||
* struct regulator_linear_range - specify linear voltage ranges
|
||||
*
|
||||
* Specify a range of voltages for regulator_map_linar_range() and
|
||||
* Specify a range of voltages for regulator_map_linear_range() and
|
||||
* regulator_list_linear_range().
|
||||
*
|
||||
* @min_uV: Lowest voltage in range
|
||||
|
@ -220,7 +220,7 @@ struct regulator_ops {
|
|||
/* set regulator suspend operating mode (defined in consumer.h) */
|
||||
int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
|
||||
|
||||
int (*resume_early)(struct regulator_dev *rdev);
|
||||
int (*resume)(struct regulator_dev *rdev);
|
||||
|
||||
int (*set_pull_down) (struct regulator_dev *);
|
||||
};
|
||||
|
|
|
@ -64,6 +64,17 @@
|
|||
#define PFUZE3000_VLDO3 11
|
||||
#define PFUZE3000_VLDO4 12
|
||||
|
||||
#define PFUZE3001_SW1 0
|
||||
#define PFUZE3001_SW2 1
|
||||
#define PFUZE3001_SW3 2
|
||||
#define PFUZE3001_VSNVS 3
|
||||
#define PFUZE3001_VLDO1 4
|
||||
#define PFUZE3001_VLDO2 5
|
||||
#define PFUZE3001_VCCSD 6
|
||||
#define PFUZE3001_V33 7
|
||||
#define PFUZE3001_VLDO3 8
|
||||
#define PFUZE3001_VLDO4 9
|
||||
|
||||
struct regulator_init_data;
|
||||
|
||||
struct pfuze_regulator_platform_data {
|
||||
|
|
|
@ -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
|
|
@ -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__ */
|
|
@ -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