diff options
author | Mark Brown <broonie@kernel.org> | 2018-08-10 12:31:24 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2018-08-10 12:31:24 -0400 |
commit | d22d59362b7b2c749245f1269d447011c76ca41d (patch) | |
tree | b3fc0673f62394dc5ed417eac6bad485a28baf25 | |
parent | a8afa92ec0d9312b23fd291aa8db95da266f2d5f (diff) | |
parent | 46fc033eba42f5a4fb583b2ab53f0a9918468452 (diff) |
Merge branch 'regulator-4.19' into regulator-next
49 files changed, 4163 insertions, 269 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator b/Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator new file mode 100644 index 000000000000..4d63a7904b94 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-bd9571mwv-regulator | |||
@@ -0,0 +1,27 @@ | |||
1 | What: /sys/bus/i2c/devices/.../bd9571mwv-regulator.*.auto/backup_mode | ||
2 | Date: Jul 2018 | ||
3 | KernelVersion: 4.19 | ||
4 | Contact: Geert Uytterhoeven <geert+renesas@glider.be> | ||
5 | Description: Read/write the current state of DDR Backup Mode, which controls | ||
6 | if DDR power rails will be kept powered during system suspend. | ||
7 | ("on"/"1" = enabled, "off"/"0" = disabled). | ||
8 | Two types of power switches (or control signals) can be used: | ||
9 | A. With a momentary power switch (or pulse signal), DDR | ||
10 | Backup Mode is enabled by default when available, as the | ||
11 | PMIC will be configured only during system suspend. | ||
12 | B. With a toggle power switch (or level signal), the | ||
13 | following steps must be followed exactly: | ||
14 | 1. Configure PMIC for backup mode, to change the role of | ||
15 | the accessory power switch from a power switch to a | ||
16 | wake-up switch, | ||
17 | 2. Switch accessory power switch off, to prepare for | ||
18 | system suspend, which is a manual step not controlled | ||
19 | by software, | ||
20 | 3. Suspend system, | ||
21 | 4. Switch accessory power switch on, to resume the | ||
22 | system. | ||
23 | DDR Backup Mode must be explicitly enabled by the user, | ||
24 | to invoke step 1. | ||
25 | See also Documentation/devicetree/bindings/mfd/bd9571mwv.txt. | ||
26 | Users: User space applications for embedded boards equipped with a | ||
27 | BD9571MWV PMIC. | ||
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt new file mode 100644 index 000000000000..5e85749262ae --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | == Introduction== | ||
2 | |||
3 | LLCC (Last Level Cache Controller) provides last level of cache memory in SOC, | ||
4 | that can be shared by multiple clients. Clients here are different cores in the | ||
5 | SOC, the idea is to minimize the local caches at the clients and migrate to | ||
6 | common pool of memory. Cache memory is divided into partitions called slices | ||
7 | which are assigned to clients. Clients can query the slice details, activate | ||
8 | and deactivate them. | ||
9 | |||
10 | Properties: | ||
11 | - compatible: | ||
12 | Usage: required | ||
13 | Value type: <string> | ||
14 | Definition: must be "qcom,sdm845-llcc" | ||
15 | |||
16 | - reg: | ||
17 | Usage: required | ||
18 | Value Type: <prop-encoded-array> | ||
19 | Definition: Start address and the the size of the register region. | ||
20 | |||
21 | Example: | ||
22 | |||
23 | cache-controller@1100000 { | ||
24 | compatible = "qcom,sdm845-llcc"; | ||
25 | reg = <0x1100000 0x250000>; | ||
26 | }; | ||
diff --git a/Documentation/devicetree/bindings/regulator/cpcap-regulator.txt b/Documentation/devicetree/bindings/regulator/cpcap-regulator.txt index 675f4437ce92..36f5e2f5cc0f 100644 --- a/Documentation/devicetree/bindings/regulator/cpcap-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/cpcap-regulator.txt | |||
@@ -5,6 +5,7 @@ Requires node properties: | |||
5 | - "compatible" value one of: | 5 | - "compatible" value one of: |
6 | "motorola,cpcap-regulator" | 6 | "motorola,cpcap-regulator" |
7 | "motorola,mapphone-cpcap-regulator" | 7 | "motorola,mapphone-cpcap-regulator" |
8 | "motorola,xoom-cpcap-regulator" | ||
8 | 9 | ||
9 | Required regulator properties: | 10 | Required regulator properties: |
10 | - "regulator-name" | 11 | - "regulator-name" |
diff --git a/Documentation/devicetree/bindings/regulator/pfuze100.txt b/Documentation/devicetree/bindings/regulator/pfuze100.txt index f0ada3b14d70..c7610718adff 100644 --- a/Documentation/devicetree/bindings/regulator/pfuze100.txt +++ b/Documentation/devicetree/bindings/regulator/pfuze100.txt | |||
@@ -1,9 +1,18 @@ | |||
1 | PFUZE100 family of regulators | 1 | PFUZE100 family of regulators |
2 | 2 | ||
3 | Required properties: | 3 | Required properties: |
4 | - compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000" | 4 | - compatible: "fsl,pfuze100", "fsl,pfuze200", "fsl,pfuze3000", "fsl,pfuze3001" |
5 | - reg: I2C slave address | 5 | - reg: I2C slave address |
6 | 6 | ||
7 | Optional properties: | ||
8 | - fsl,pfuze-support-disable-sw: Boolean, if present disable all unused switch | ||
9 | regulators to save power consumption. Attention, ensure that all important | ||
10 | regulators (e.g. DDR ref, DDR supply) has set the "regulator-always-on" | ||
11 | property. If not present, the switched regualtors are always on and can't be | ||
12 | disabled. This binding is a workaround to keep backward compatibility with | ||
13 | old dtb's which rely on the fact that the switched regulators are always on | ||
14 | and don't mark them explicit as "regulator-always-on". | ||
15 | |||
7 | Required child node: | 16 | Required child node: |
8 | - regulators: This is the list of child nodes that specify the regulator | 17 | - regulators: This is the list of child nodes that specify the regulator |
9 | initialization data for defined regulators. Please refer to below doc | 18 | initialization data for defined regulators. Please refer to below doc |
@@ -16,6 +25,8 @@ Required child node: | |||
16 | sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6,coin | 25 | sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6,coin |
17 | --PFUZE3000 | 26 | --PFUZE3000 |
18 | sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4 | 27 | sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4 |
28 | --PFUZE3001 | ||
29 | sw1,sw2,sw3,vsnvs,vldo1,vldo2,vccsd,v33,vldo3,vldo4 | ||
19 | 30 | ||
20 | Each regulator is defined using the standard binding for regulators. | 31 | Each regulator is defined using the standard binding for regulators. |
21 | 32 | ||
@@ -303,3 +314,76 @@ Example 3: PFUZE3000 | |||
303 | }; | 314 | }; |
304 | }; | 315 | }; |
305 | }; | 316 | }; |
317 | |||
318 | Example 4: PFUZE 3001 | ||
319 | |||
320 | pfuze3001: pmic@8 { | ||
321 | compatible = "fsl,pfuze3001"; | ||
322 | reg = <0x08>; | ||
323 | |||
324 | regulators { | ||
325 | sw1_reg: sw1 { | ||
326 | regulator-min-microvolt = <700000>; | ||
327 | regulator-max-microvolt = <3300000>; | ||
328 | regulator-boot-on; | ||
329 | regulator-always-on; | ||
330 | }; | ||
331 | |||
332 | sw2_reg: sw2 { | ||
333 | regulator-min-microvolt = <1500000>; | ||
334 | regulator-max-microvolt = <3300000>; | ||
335 | regulator-boot-on; | ||
336 | regulator-always-on; | ||
337 | }; | ||
338 | |||
339 | sw3_reg: sw3 { | ||
340 | regulator-min-microvolt = <900000>; | ||
341 | regulator-max-microvolt = <1650000>; | ||
342 | regulator-boot-on; | ||
343 | regulator-always-on; | ||
344 | }; | ||
345 | |||
346 | snvs_reg: vsnvs { | ||
347 | regulator-min-microvolt = <1000000>; | ||
348 | regulator-max-microvolt = <3000000>; | ||
349 | regulator-boot-on; | ||
350 | regulator-always-on; | ||
351 | }; | ||
352 | |||
353 | vgen1_reg: vldo1 { | ||
354 | regulator-min-microvolt = <1800000>; | ||
355 | regulator-max-microvolt = <3300000>; | ||
356 | regulator-always-on; | ||
357 | }; | ||
358 | |||
359 | vgen2_reg: vldo2 { | ||
360 | regulator-min-microvolt = <800000>; | ||
361 | regulator-max-microvolt = <1550000>; | ||
362 | regulator-always-on; | ||
363 | }; | ||
364 | |||
365 | vgen3_reg: vccsd { | ||
366 | regulator-min-microvolt = <2850000>; | ||
367 | regulator-max-microvolt = <3300000>; | ||
368 | regulator-always-on; | ||
369 | }; | ||
370 | |||
371 | vgen4_reg: v33 { | ||
372 | regulator-min-microvolt = <2850000>; | ||
373 | regulator-max-microvolt = <3300000>; | ||
374 | regulator-always-on; | ||
375 | }; | ||
376 | |||
377 | vgen5_reg: vldo3 { | ||
378 | regulator-min-microvolt = <1800000>; | ||
379 | regulator-max-microvolt = <3300000>; | ||
380 | regulator-always-on; | ||
381 | }; | ||
382 | |||
383 | vgen6_reg: vldo4 { | ||
384 | regulator-min-microvolt = <1800000>; | ||
385 | regulator-max-microvolt = <3300000>; | ||
386 | regulator-always-on; | ||
387 | }; | ||
388 | }; | ||
389 | }; | ||
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt new file mode 100644 index 000000000000..7ef2dbe48e8a --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt | |||
@@ -0,0 +1,160 @@ | |||
1 | Qualcomm Technologies, Inc. RPMh Regulators | ||
2 | |||
3 | rpmh-regulator devices support PMIC regulator management via the Voltage | ||
4 | Regulator Manager (VRM) and Oscillator Buffer (XOB) RPMh accelerators. The APPS | ||
5 | processor communicates with these hardware blocks via a Resource State | ||
6 | Coordinator (RSC) using command packets. The VRM allows changing three | ||
7 | parameters for a given regulator: enable state, output voltage, and operating | ||
8 | mode. The XOB allows changing only a single parameter for a given regulator: | ||
9 | its enable state. Despite its name, the XOB is capable of controlling the | ||
10 | enable state of any PMIC peripheral. It is used for clock buffers, low-voltage | ||
11 | switches, and LDO/SMPS regulators which have a fixed voltage and mode. | ||
12 | |||
13 | ======================= | ||
14 | Required Node Structure | ||
15 | ======================= | ||
16 | |||
17 | RPMh regulators must be described in two levels of device nodes. The first | ||
18 | level describes the PMIC containing the regulators and must reside within an | ||
19 | RPMh device node. The second level describes each regulator within the PMIC | ||
20 | which is to be used on the board. Each of these regulators maps to a single | ||
21 | RPMh resource. | ||
22 | |||
23 | The names used for regulator nodes must match those supported by a given PMIC. | ||
24 | Supported regulator node names: | ||
25 | PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2 | ||
26 | PMI8998: bob | ||
27 | PM8005: smps1 - smps4 | ||
28 | |||
29 | ======================== | ||
30 | First Level Nodes - PMIC | ||
31 | ======================== | ||
32 | |||
33 | - compatible | ||
34 | Usage: required | ||
35 | Value type: <string> | ||
36 | Definition: Must be one of: "qcom,pm8998-rpmh-regulators", | ||
37 | "qcom,pmi8998-rpmh-regulators" or | ||
38 | "qcom,pm8005-rpmh-regulators". | ||
39 | |||
40 | - qcom,pmic-id | ||
41 | Usage: required | ||
42 | Value type: <string> | ||
43 | Definition: RPMh resource name suffix used for the regulators found on | ||
44 | this PMIC. Typical values: "a", "b", "c", "d", "e", "f". | ||
45 | |||
46 | - vdd-s1-supply | ||
47 | - vdd-s2-supply | ||
48 | - vdd-s3-supply | ||
49 | - vdd-s4-supply | ||
50 | Usage: optional (PM8998 and PM8005 only) | ||
51 | Value type: <phandle> | ||
52 | Definition: phandle of the parent supply regulator of one or more of the | ||
53 | regulators for this PMIC. | ||
54 | |||
55 | - vdd-s5-supply | ||
56 | - vdd-s6-supply | ||
57 | - vdd-s7-supply | ||
58 | - vdd-s8-supply | ||
59 | - vdd-s9-supply | ||
60 | - vdd-s10-supply | ||
61 | - vdd-s11-supply | ||
62 | - vdd-s12-supply | ||
63 | - vdd-s13-supply | ||
64 | - vdd-l1-l27-supply | ||
65 | - vdd-l2-l8-l17-supply | ||
66 | - vdd-l3-l11-supply | ||
67 | - vdd-l4-l5-supply | ||
68 | - vdd-l6-supply | ||
69 | - vdd-l7-l12-l14-l15-supply | ||
70 | - vdd-l9-supply | ||
71 | - vdd-l10-l23-l25-supply | ||
72 | - vdd-l13-l19-l21-supply | ||
73 | - vdd-l16-l28-supply | ||
74 | - vdd-l18-l22-supply | ||
75 | - vdd-l20-l24-supply | ||
76 | - vdd-l26-supply | ||
77 | - vin-lvs-1-2-supply | ||
78 | Usage: optional (PM8998 only) | ||
79 | Value type: <phandle> | ||
80 | Definition: phandle of the parent supply regulator of one or more of the | ||
81 | regulators for this PMIC. | ||
82 | |||
83 | - vdd-bob-supply | ||
84 | Usage: optional (PMI8998 only) | ||
85 | Value type: <phandle> | ||
86 | Definition: BOB regulator parent supply phandle | ||
87 | |||
88 | =============================== | ||
89 | Second Level Nodes - Regulators | ||
90 | =============================== | ||
91 | |||
92 | - qcom,always-wait-for-ack | ||
93 | Usage: optional | ||
94 | Value type: <empty> | ||
95 | Definition: Boolean flag which indicates that the application processor | ||
96 | must wait for an ACK or a NACK from RPMh for every request | ||
97 | sent for this regulator including those which are for a | ||
98 | strictly lower power state. | ||
99 | |||
100 | Other properties defined in Documentation/devicetree/bindings/regulator.txt | ||
101 | may also be used. regulator-initial-mode and regulator-allowed-modes may be | ||
102 | specified for VRM regulators using mode values from | ||
103 | include/dt-bindings/regulator/qcom,rpmh-regulator.h. regulator-allow-bypass | ||
104 | may be specified for BOB type regulators managed via VRM. | ||
105 | regulator-allow-set-load may be specified for LDO type regulators managed via | ||
106 | VRM. | ||
107 | |||
108 | ======== | ||
109 | Examples | ||
110 | ======== | ||
111 | |||
112 | #include <dt-bindings/regulator/qcom,rpmh-regulator.h> | ||
113 | |||
114 | &apps_rsc { | ||
115 | pm8998-rpmh-regulators { | ||
116 | compatible = "qcom,pm8998-rpmh-regulators"; | ||
117 | qcom,pmic-id = "a"; | ||
118 | |||
119 | vdd-l7-l12-l14-l15-supply = <&pm8998_s5>; | ||
120 | |||
121 | smps2 { | ||
122 | regulator-min-microvolt = <1100000>; | ||
123 | regulator-max-microvolt = <1100000>; | ||
124 | }; | ||
125 | |||
126 | pm8998_s5: smps5 { | ||
127 | regulator-min-microvolt = <1904000>; | ||
128 | regulator-max-microvolt = <2040000>; | ||
129 | }; | ||
130 | |||
131 | ldo7 { | ||
132 | regulator-min-microvolt = <1800000>; | ||
133 | regulator-max-microvolt = <1800000>; | ||
134 | regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; | ||
135 | regulator-allowed-modes = | ||
136 | <RPMH_REGULATOR_MODE_LPM | ||
137 | RPMH_REGULATOR_MODE_HPM>; | ||
138 | regulator-allow-set-load; | ||
139 | }; | ||
140 | |||
141 | lvs1 { | ||
142 | regulator-min-microvolt = <1800000>; | ||
143 | regulator-max-microvolt = <1800000>; | ||
144 | }; | ||
145 | }; | ||
146 | |||
147 | pmi8998-rpmh-regulators { | ||
148 | compatible = "qcom,pmi8998-rpmh-regulators"; | ||
149 | qcom,pmic-id = "b"; | ||
150 | |||
151 | bob { | ||
152 | regulator-min-microvolt = <3312000>; | ||
153 | regulator-max-microvolt = <3600000>; | ||
154 | regulator-allowed-modes = | ||
155 | <RPMH_REGULATOR_MODE_AUTO | ||
156 | RPMH_REGULATOR_MODE_HPM>; | ||
157 | regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>; | ||
158 | }; | ||
159 | }; | ||
160 | }; | ||
diff --git a/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt index 4edf3137d9f7..76ead07072b1 100644 --- a/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/rohm,bd71837-regulator.txt | |||
@@ -1,13 +1,5 @@ | |||
1 | ROHM BD71837 Power Management Integrated Circuit (PMIC) regulator bindings | 1 | ROHM BD71837 Power Management Integrated Circuit (PMIC) regulator bindings |
2 | 2 | ||
3 | BD71837MWV is a programmable Power Management | ||
4 | IC (PMIC) for powering single-core, dual-core, and | ||
5 | quad-core SoC’s such as NXP-i.MX 8M. It is optimized | ||
6 | for low BOM cost and compact solution footprint. It | ||
7 | integrates 8 Buck regulators and 7 LDO’s to provide all | ||
8 | the power rails required by the SoC and the commonly | ||
9 | used peripherals. | ||
10 | |||
11 | Required properties: | 3 | Required properties: |
12 | - regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7" | 4 | - regulator-name: should be "buck1", ..., "buck8" and "ldo1", ..., "ldo7" |
13 | 5 | ||
diff --git a/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt b/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt new file mode 100644 index 000000000000..c9919f4b92d2 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/uniphier-regulator.txt | |||
@@ -0,0 +1,57 @@ | |||
1 | Socionext UniPhier Regulator Controller | ||
2 | |||
3 | This describes the devicetree bindings for regulator controller implemented | ||
4 | on Socionext UniPhier SoCs. | ||
5 | |||
6 | USB3 Controller | ||
7 | --------------- | ||
8 | |||
9 | This regulator controls VBUS and belongs to USB3 glue layer. Before using | ||
10 | the regulator, it is necessary to control the clocks and resets to enable | ||
11 | this layer. These clocks and resets should be described in each property. | ||
12 | |||
13 | Required properties: | ||
14 | - compatible: Should be | ||
15 | "socionext,uniphier-pro4-usb3-regulator" - for Pro4 SoC | ||
16 | "socionext,uniphier-pxs2-usb3-regulator" - for PXs2 SoC | ||
17 | "socionext,uniphier-ld20-usb3-regulator" - for LD20 SoC | ||
18 | "socionext,uniphier-pxs3-usb3-regulator" - for PXs3 SoC | ||
19 | - reg: Specifies offset and length of the register set for the device. | ||
20 | - clocks: A list of phandles to the clock gate for USB3 glue layer. | ||
21 | According to the clock-names, appropriate clocks are required. | ||
22 | - clock-names: Should contain | ||
23 | "gio", "link" - for Pro4 SoC | ||
24 | "link" - for others | ||
25 | - resets: A list of phandles to the reset control for USB3 glue layer. | ||
26 | According to the reset-names, appropriate resets are required. | ||
27 | - reset-names: Should contain | ||
28 | "gio", "link" - for Pro4 SoC | ||
29 | "link" - for others | ||
30 | |||
31 | See Documentation/devicetree/bindings/regulator/regulator.txt | ||
32 | for more details about the regulator properties. | ||
33 | |||
34 | Example: | ||
35 | |||
36 | usb-glue@65b00000 { | ||
37 | compatible = "socionext,uniphier-ld20-dwc3-glue", | ||
38 | "simple-mfd"; | ||
39 | #address-cells = <1>; | ||
40 | #size-cells = <1>; | ||
41 | ranges = <0 0x65b00000 0x400>; | ||
42 | |||
43 | usb_vbus0: regulators@100 { | ||
44 | compatible = "socionext,uniphier-ld20-usb3-regulator"; | ||
45 | reg = <0x100 0x10>; | ||
46 | clock-names = "link"; | ||
47 | clocks = <&sys_clk 14>; | ||
48 | reset-names = "link"; | ||
49 | resets = <&sys_rst 14>; | ||
50 | }; | ||
51 | |||
52 | phy { | ||
53 | ... | ||
54 | phy-supply = <&usb_vbus0>; | ||
55 | }; | ||
56 | ... | ||
57 | }; | ||
diff --git a/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt b/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt new file mode 100644 index 000000000000..9b86d1eff219 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt | |||
@@ -0,0 +1,137 @@ | |||
1 | RPMH RSC: | ||
2 | ------------ | ||
3 | |||
4 | Resource Power Manager Hardened (RPMH) is the mechanism for communicating with | ||
5 | the hardened resource accelerators on Qualcomm SoCs. Requests to the resources | ||
6 | can be written to the Trigger Command Set (TCS) registers and using a (addr, | ||
7 | val) pair and triggered. Messages in the TCS are then sent in sequence over an | ||
8 | internal bus. | ||
9 | |||
10 | The hardware block (Direct Resource Voter or DRV) is a part of the h/w entity | ||
11 | (Resource State Coordinator a.k.a RSC) that can handle multiple sleep and | ||
12 | active/wake resource requests. Multiple such DRVs can exist in a SoC and can | ||
13 | be written to from Linux. The structure of each DRV follows the same template | ||
14 | with a few variations that are captured by the properties here. | ||
15 | |||
16 | A TCS may be triggered from Linux or triggered by the F/W after all the CPUs | ||
17 | have powered off to facilitate idle power saving. TCS could be classified as - | ||
18 | |||
19 | ACTIVE /* Triggered by Linux */ | ||
20 | SLEEP /* Triggered by F/W */ | ||
21 | WAKE /* Triggered by F/W */ | ||
22 | CONTROL /* Triggered by F/W */ | ||
23 | |||
24 | The order in which they are described in the DT, should match the hardware | ||
25 | configuration. | ||
26 | |||
27 | Requests can be made for the state of a resource, when the subsystem is active | ||
28 | or idle. When all subsystems like Modem, GPU, CPU are idle, the resource state | ||
29 | will be an aggregate of the sleep votes from each of those subsystems. Clients | ||
30 | may request a sleep value for their shared resources in addition to the active | ||
31 | mode requests. | ||
32 | |||
33 | Properties: | ||
34 | |||
35 | - compatible: | ||
36 | Usage: required | ||
37 | Value type: <string> | ||
38 | Definition: Should be "qcom,rpmh-rsc". | ||
39 | |||
40 | - reg: | ||
41 | Usage: required | ||
42 | Value type: <prop-encoded-array> | ||
43 | Definition: The first register specifies the base address of the | ||
44 | DRV(s). The number of DRVs in the dependent on the RSC. | ||
45 | The tcs-offset specifies the start address of the | ||
46 | TCS in the DRVs. | ||
47 | |||
48 | - reg-names: | ||
49 | Usage: required | ||
50 | Value type: <string> | ||
51 | Definition: Maps the register specified in the reg property. Must be | ||
52 | "drv-0", "drv-1", "drv-2" etc and "tcs-offset". The | ||
53 | |||
54 | - interrupts: | ||
55 | Usage: required | ||
56 | Value type: <prop-encoded-interrupt> | ||
57 | Definition: The interrupt that trips when a message complete/response | ||
58 | is received for this DRV from the accelerators. | ||
59 | |||
60 | - qcom,drv-id: | ||
61 | Usage: required | ||
62 | Value type: <u32> | ||
63 | Definition: The id of the DRV in the RSC block that will be used by | ||
64 | this controller. | ||
65 | |||
66 | - qcom,tcs-config: | ||
67 | Usage: required | ||
68 | Value type: <prop-encoded-array> | ||
69 | Definition: The tuple defining the configuration of TCS. | ||
70 | Must have 2 cells which describe each TCS type. | ||
71 | <type number_of_tcs>. | ||
72 | The order of the TCS must match the hardware | ||
73 | configuration. | ||
74 | - Cell #1 (TCS Type): TCS types to be specified - | ||
75 | ACTIVE_TCS | ||
76 | SLEEP_TCS | ||
77 | WAKE_TCS | ||
78 | CONTROL_TCS | ||
79 | - Cell #2 (Number of TCS): <u32> | ||
80 | |||
81 | - label: | ||
82 | Usage: optional | ||
83 | Value type: <string> | ||
84 | Definition: Name for the RSC. The name would be used in trace logs. | ||
85 | |||
86 | Drivers that want to use the RSC to communicate with RPMH must specify their | ||
87 | bindings as child nodes of the RSC controllers they wish to communicate with. | ||
88 | |||
89 | Example 1: | ||
90 | |||
91 | For a TCS whose RSC base address is is 0x179C0000 and is at a DRV id of 2, the | ||
92 | register offsets for DRV2 start at 0D00, the register calculations are like | ||
93 | this - | ||
94 | DRV0: 0x179C0000 | ||
95 | DRV2: 0x179C0000 + 0x10000 = 0x179D0000 | ||
96 | DRV2: 0x179C0000 + 0x10000 * 2 = 0x179E0000 | ||
97 | TCS-OFFSET: 0xD00 | ||
98 | |||
99 | apps_rsc: rsc@179c0000 { | ||
100 | label = "apps_rsc"; | ||
101 | compatible = "qcom,rpmh-rsc"; | ||
102 | reg = <0x179c0000 0x10000>, | ||
103 | <0x179d0000 0x10000>, | ||
104 | <0x179e0000 0x10000>; | ||
105 | reg-names = "drv-0", "drv-1", "drv-2"; | ||
106 | interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>, | ||
107 | <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, | ||
108 | <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; | ||
109 | qcom,tcs-offset = <0xd00>; | ||
110 | qcom,drv-id = <2>; | ||
111 | qcom,tcs-config = <ACTIVE_TCS 2>, | ||
112 | <SLEEP_TCS 3>, | ||
113 | <WAKE_TCS 3>, | ||
114 | <CONTROL_TCS 1>; | ||
115 | }; | ||
116 | |||
117 | Example 2: | ||
118 | |||
119 | For a TCS whose RSC base address is 0xAF20000 and is at DRV id of 0, the | ||
120 | register offsets for DRV0 start at 01C00, the register calculations are like | ||
121 | this - | ||
122 | DRV0: 0xAF20000 | ||
123 | TCS-OFFSET: 0x1C00 | ||
124 | |||
125 | disp_rsc: rsc@af20000 { | ||
126 | label = "disp_rsc"; | ||
127 | compatible = "qcom,rpmh-rsc"; | ||
128 | reg = <0xaf20000 0x10000>; | ||
129 | reg-names = "drv-0"; | ||
130 | interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>; | ||
131 | qcom,tcs-offset = <0x1c00>; | ||
132 | qcom,drv-id = <0>; | ||
133 | qcom,tcs-config = <ACTIVE_TCS 0>, | ||
134 | <SLEEP_TCS 1>, | ||
135 | <WAKE_TCS 1>, | ||
136 | <CONTROL_TCS 0>; | ||
137 | }; | ||
diff --git a/drivers/base/core.c b/drivers/base/core.c index df3e1a44707a..2ab316d85651 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -372,6 +372,36 @@ void device_link_del(struct device_link *link) | |||
372 | } | 372 | } |
373 | EXPORT_SYMBOL_GPL(device_link_del); | 373 | EXPORT_SYMBOL_GPL(device_link_del); |
374 | 374 | ||
375 | /** | ||
376 | * device_link_remove - remove a link between two devices. | ||
377 | * @consumer: Consumer end of the link. | ||
378 | * @supplier: Supplier end of the link. | ||
379 | * | ||
380 | * The caller must ensure proper synchronization of this function with runtime | ||
381 | * PM. | ||
382 | */ | ||
383 | void device_link_remove(void *consumer, struct device *supplier) | ||
384 | { | ||
385 | struct device_link *link; | ||
386 | |||
387 | if (WARN_ON(consumer == supplier)) | ||
388 | return; | ||
389 | |||
390 | device_links_write_lock(); | ||
391 | device_pm_lock(); | ||
392 | |||
393 | list_for_each_entry(link, &supplier->links.consumers, s_node) { | ||
394 | if (link->consumer == consumer) { | ||
395 | kref_put(&link->kref, __device_link_del); | ||
396 | break; | ||
397 | } | ||
398 | } | ||
399 | |||
400 | device_pm_unlock(); | ||
401 | device_links_write_unlock(); | ||
402 | } | ||
403 | EXPORT_SYMBOL_GPL(device_link_remove); | ||
404 | |||
375 | static void device_links_missing_supplier(struct device *dev) | 405 | static void device_links_missing_supplier(struct device *dev) |
376 | { | 406 | { |
377 | struct device_link *link; | 407 | struct device_link *link; |
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 5dbccf5f3037..329cdd33ed62 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig | |||
@@ -180,9 +180,9 @@ config REGULATOR_BCM590XX | |||
180 | BCM590xx PMUs. This will enable support for the software | 180 | BCM590xx PMUs. This will enable support for the software |
181 | controllable LDO/Switching regulators. | 181 | controllable LDO/Switching regulators. |
182 | 182 | ||
183 | config REGULATOR_BD71837 | 183 | config REGULATOR_BD718XX |
184 | tristate "ROHM BD71837 Power Regulator" | 184 | tristate "ROHM BD71837 Power Regulator" |
185 | depends on MFD_BD71837 | 185 | depends on MFD_ROHM_BD718XX |
186 | help | 186 | help |
187 | This driver supports voltage regulators on ROHM BD71837 PMIC. | 187 | This driver supports voltage regulators on ROHM BD71837 PMIC. |
188 | This will enable support for the software controllable buck | 188 | This will enable support for the software controllable buck |
@@ -633,12 +633,12 @@ config REGULATOR_PCF50633 | |||
633 | on PCF50633 | 633 | on PCF50633 |
634 | 634 | ||
635 | config REGULATOR_PFUZE100 | 635 | config REGULATOR_PFUZE100 |
636 | tristate "Freescale PFUZE100/200/3000 regulator driver" | 636 | tristate "Freescale PFUZE100/200/3000/3001 regulator driver" |
637 | depends on I2C | 637 | depends on I2C |
638 | select REGMAP_I2C | 638 | select REGMAP_I2C |
639 | help | 639 | help |
640 | Say y here to support the regulators found on the Freescale | 640 | Say y here to support the regulators found on the Freescale |
641 | PFUZE100/200/3000 PMIC. | 641 | PFUZE100/200/3000/3001 PMIC. |
642 | 642 | ||
643 | config REGULATOR_PV88060 | 643 | config REGULATOR_PV88060 |
644 | tristate "Powerventure Semiconductor PV88060 regulator" | 644 | tristate "Powerventure Semiconductor PV88060 regulator" |
@@ -682,6 +682,15 @@ config REGULATOR_QCOM_RPM | |||
682 | Qualcomm RPM as a module. The module will be named | 682 | Qualcomm RPM as a module. The module will be named |
683 | "qcom_rpm-regulator". | 683 | "qcom_rpm-regulator". |
684 | 684 | ||
685 | config REGULATOR_QCOM_RPMH | ||
686 | tristate "Qualcomm Technologies, Inc. RPMh regulator driver" | ||
687 | depends on QCOM_RPMH || COMPILE_TEST | ||
688 | help | ||
689 | This driver supports control of PMIC regulators via the RPMh hardware | ||
690 | block found on Qualcomm Technologies Inc. SoCs. RPMh regulator | ||
691 | control allows for voting on regulator state between multiple | ||
692 | processors within the SoC. | ||
693 | |||
685 | config REGULATOR_QCOM_SMD_RPM | 694 | config REGULATOR_QCOM_SMD_RPM |
686 | tristate "Qualcomm SMD based RPM regulator driver" | 695 | tristate "Qualcomm SMD based RPM regulator driver" |
687 | depends on QCOM_SMD_RPM | 696 | depends on QCOM_SMD_RPM |
@@ -950,6 +959,14 @@ config REGULATOR_TWL4030 | |||
950 | This driver supports the voltage regulators provided by | 959 | This driver supports the voltage regulators provided by |
951 | this family of companion chips. | 960 | this family of companion chips. |
952 | 961 | ||
962 | config REGULATOR_UNIPHIER | ||
963 | tristate "UniPhier regulator driver" | ||
964 | depends on ARCH_UNIPHIER || COMPILE_TEST | ||
965 | depends on OF && MFD_SYSCON | ||
966 | default ARCH_UNIPHIER | ||
967 | help | ||
968 | Support for regulators implemented on Socionext UniPhier SoCs. | ||
969 | |||
953 | config REGULATOR_VCTRL | 970 | config REGULATOR_VCTRL |
954 | tristate "Voltage controlled regulators" | 971 | tristate "Voltage controlled regulators" |
955 | depends on OF | 972 | depends on OF |
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bd818ceb7c72..801d9a34a203 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile | |||
@@ -27,7 +27,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o | |||
27 | obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o | 27 | obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o |
28 | obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o | 28 | obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o |
29 | obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o | 29 | obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o |
30 | obj-$(CONFIG_REGULATOR_BD71837) += bd71837-regulator.o | 30 | obj-$(CONFIG_REGULATOR_BD718XX) += bd71837-regulator.o |
31 | obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o | 31 | obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o |
32 | obj-$(CONFIG_REGULATOR_DA903X) += da903x.o | 32 | obj-$(CONFIG_REGULATOR_DA903X) += da903x.o |
33 | obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o | 33 | obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o |
@@ -78,6 +78,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o | |||
78 | obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o | 78 | obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o |
79 | obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o | 79 | obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o |
80 | obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o | 80 | obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o |
81 | obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o | ||
81 | obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o | 82 | obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o |
82 | obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o | 83 | obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o |
83 | obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o | 84 | obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o |
@@ -118,6 +119,7 @@ obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o | |||
118 | obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o | 119 | obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o |
119 | obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o | 120 | obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o |
120 | obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o | 121 | obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o |
122 | obj-$(CONFIG_REGULATOR_UNIPHIER) += uniphier-regulator.o | ||
121 | obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o | 123 | obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o |
122 | obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o | 124 | obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o |
123 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o | 125 | obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o |
diff --git a/drivers/regulator/bd71837-regulator.c b/drivers/regulator/bd71837-regulator.c index 6eae4d0432a2..0f8ac8dec3e1 100644 --- a/drivers/regulator/bd71837-regulator.c +++ b/drivers/regulator/bd71837-regulator.c | |||
@@ -2,19 +2,18 @@ | |||
2 | // Copyright (C) 2018 ROHM Semiconductors | 2 | // Copyright (C) 2018 ROHM Semiconductors |
3 | // bd71837-regulator.c ROHM BD71837MWV regulator driver | 3 | // bd71837-regulator.c ROHM BD71837MWV regulator driver |
4 | 4 | ||
5 | #include <linux/kernel.h> | 5 | #include <linux/delay.h> |
6 | #include <linux/module.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/err.h> | 6 | #include <linux/err.h> |
7 | #include <linux/gpio.h> | ||
9 | #include <linux/interrupt.h> | 8 | #include <linux/interrupt.h> |
9 | #include <linux/kernel.h> | ||
10 | #include <linux/mfd/rohm-bd718x7.h> | ||
11 | #include <linux/module.h> | ||
10 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
11 | #include <linux/regulator/driver.h> | 13 | #include <linux/regulator/driver.h> |
12 | #include <linux/regulator/machine.h> | 14 | #include <linux/regulator/machine.h> |
13 | #include <linux/delay.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/mfd/bd71837.h> | ||
17 | #include <linux/regulator/of_regulator.h> | 15 | #include <linux/regulator/of_regulator.h> |
16 | #include <linux/slab.h> | ||
18 | 17 | ||
19 | struct bd71837_pmic { | 18 | struct bd71837_pmic { |
20 | struct regulator_desc descs[BD71837_REGULATOR_CNT]; | 19 | struct regulator_desc descs[BD71837_REGULATOR_CNT]; |
@@ -39,7 +38,7 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev, | |||
39 | int id = rdev->desc->id; | 38 | int id = rdev->desc->id; |
40 | unsigned int ramp_value = BUCK_RAMPRATE_10P00MV; | 39 | unsigned int ramp_value = BUCK_RAMPRATE_10P00MV; |
41 | 40 | ||
42 | dev_dbg(&(pmic->pdev->dev), "Buck[%d] Set Ramp = %d\n", id + 1, | 41 | dev_dbg(&pmic->pdev->dev, "Buck[%d] Set Ramp = %d\n", id + 1, |
43 | ramp_delay); | 42 | ramp_delay); |
44 | switch (ramp_delay) { | 43 | switch (ramp_delay) { |
45 | case 1 ... 1250: | 44 | case 1 ... 1250: |
@@ -73,14 +72,10 @@ static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev, | |||
73 | static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev, | 72 | static int bd71837_set_voltage_sel_restricted(struct regulator_dev *rdev, |
74 | unsigned int sel) | 73 | unsigned int sel) |
75 | { | 74 | { |
76 | int ret; | 75 | if (regulator_is_enabled_regmap(rdev)) |
77 | 76 | return -EBUSY; | |
78 | ret = regulator_is_enabled_regmap(rdev); | 77 | |
79 | if (!ret) | 78 | return regulator_set_voltage_sel_regmap(rdev, sel); |
80 | ret = regulator_set_voltage_sel_regmap(rdev, sel); | ||
81 | else if (ret == 1) | ||
82 | ret = -EBUSY; | ||
83 | return ret; | ||
84 | } | 79 | } |
85 | 80 | ||
86 | static struct regulator_ops bd71837_ldo_regulator_ops = { | 81 | static struct regulator_ops bd71837_ldo_regulator_ops = { |
@@ -195,7 +190,7 @@ static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = { | |||
195 | * LDO2 | 190 | * LDO2 |
196 | * 0.8 or 0.9V | 191 | * 0.8 or 0.9V |
197 | */ | 192 | */ |
198 | const unsigned int ldo_2_volts[] = { | 193 | static const unsigned int ldo_2_volts[] = { |
199 | 900000, 800000 | 194 | 900000, 800000 |
200 | }; | 195 | }; |
201 | 196 | ||
@@ -495,7 +490,6 @@ struct reg_init { | |||
495 | static int bd71837_probe(struct platform_device *pdev) | 490 | static int bd71837_probe(struct platform_device *pdev) |
496 | { | 491 | { |
497 | struct bd71837_pmic *pmic; | 492 | struct bd71837_pmic *pmic; |
498 | struct bd71837_board *pdata; | ||
499 | struct regulator_config config = { 0 }; | 493 | struct regulator_config config = { 0 }; |
500 | struct reg_init pmic_regulator_inits[] = { | 494 | struct reg_init pmic_regulator_inits[] = { |
501 | { | 495 | { |
@@ -548,8 +542,7 @@ static int bd71837_probe(struct platform_device *pdev) | |||
548 | 542 | ||
549 | int i, err; | 543 | int i, err; |
550 | 544 | ||
551 | pmic = devm_kzalloc(&pdev->dev, sizeof(struct bd71837_pmic), | 545 | pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); |
552 | GFP_KERNEL); | ||
553 | if (!pmic) | 546 | if (!pmic) |
554 | return -ENOMEM; | 547 | return -ENOMEM; |
555 | 548 | ||
@@ -564,7 +557,6 @@ static int bd71837_probe(struct platform_device *pdev) | |||
564 | goto err; | 557 | goto err; |
565 | } | 558 | } |
566 | platform_set_drvdata(pdev, pmic); | 559 | platform_set_drvdata(pdev, pmic); |
567 | pdata = dev_get_platdata(pmic->mfd->dev); | ||
568 | 560 | ||
569 | /* Register LOCK release */ | 561 | /* Register LOCK release */ |
570 | err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK, | 562 | err = regmap_update_bits(pmic->mfd->regmap, BD71837_REG_REGLOCK, |
@@ -573,8 +565,8 @@ static int bd71837_probe(struct platform_device *pdev) | |||
573 | dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err); | 565 | dev_err(&pmic->pdev->dev, "Failed to unlock PMIC (%d)\n", err); |
574 | goto err; | 566 | goto err; |
575 | } else { | 567 | } else { |
576 | dev_dbg(&pmic->pdev->dev, "%s: Unlocked lock register 0x%x\n", | 568 | dev_dbg(&pmic->pdev->dev, "Unlocked lock register 0x%x\n", |
577 | __func__, BD71837_REG_REGLOCK); | 569 | BD71837_REG_REGLOCK); |
578 | } | 570 | } |
579 | 571 | ||
580 | for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) { | 572 | for (i = 0; i < ARRAY_SIZE(pmic_regulator_inits); i++) { |
@@ -584,9 +576,6 @@ static int bd71837_probe(struct platform_device *pdev) | |||
584 | 576 | ||
585 | desc = &pmic->descs[i]; | 577 | desc = &pmic->descs[i]; |
586 | 578 | ||
587 | if (pdata) | ||
588 | config.init_data = pdata->init_data[i]; | ||
589 | |||
590 | config.dev = pdev->dev.parent; | 579 | config.dev = pdev->dev.parent; |
591 | config.driver_data = pmic; | 580 | config.driver_data = pmic; |
592 | config.regmap = pmic->mfd->regmap; | 581 | config.regmap = pmic->mfd->regmap; |
@@ -619,8 +608,6 @@ static int bd71837_probe(struct platform_device *pdev) | |||
619 | pmic->rdev[i] = rdev; | 608 | pmic->rdev[i] = rdev; |
620 | } | 609 | } |
621 | 610 | ||
622 | return 0; | ||
623 | |||
624 | err: | 611 | err: |
625 | return err; | 612 | return err; |
626 | } | 613 | } |
@@ -628,7 +615,6 @@ err: | |||
628 | static struct platform_driver bd71837_regulator = { | 615 | static struct platform_driver bd71837_regulator = { |
629 | .driver = { | 616 | .driver = { |
630 | .name = "bd71837-pmic", | 617 | .name = "bd71837-pmic", |
631 | .owner = THIS_MODULE, | ||
632 | }, | 618 | }, |
633 | .probe = bd71837_probe, | 619 | .probe = bd71837_probe, |
634 | }; | 620 | }; |
diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c index be574eb444eb..274c5ed7cd73 100644 --- a/drivers/regulator/bd9571mwv-regulator.c +++ b/drivers/regulator/bd9571mwv-regulator.c | |||
@@ -30,6 +30,7 @@ struct bd9571mwv_reg { | |||
30 | /* DDR Backup Power */ | 30 | /* DDR Backup Power */ |
31 | u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */ | 31 | u8 bkup_mode_cnt_keepon; /* from "rohm,ddr-backup-power" */ |
32 | u8 bkup_mode_cnt_saved; | 32 | u8 bkup_mode_cnt_saved; |
33 | bool bkup_mode_enabled; | ||
33 | 34 | ||
34 | /* Power switch type */ | 35 | /* Power switch type */ |
35 | bool rstbmode_level; | 36 | bool rstbmode_level; |
@@ -171,13 +172,60 @@ static int bd9571mwv_bkup_mode_write(struct bd9571mwv *bd, unsigned int mode) | |||
171 | return 0; | 172 | return 0; |
172 | } | 173 | } |
173 | 174 | ||
175 | static ssize_t backup_mode_show(struct device *dev, | ||
176 | struct device_attribute *attr, char *buf) | ||
177 | { | ||
178 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); | ||
179 | |||
180 | return sprintf(buf, "%s\n", bdreg->bkup_mode_enabled ? "on" : "off"); | ||
181 | } | ||
182 | |||
183 | static ssize_t backup_mode_store(struct device *dev, | ||
184 | struct device_attribute *attr, | ||
185 | const char *buf, size_t count) | ||
186 | { | ||
187 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); | ||
188 | unsigned int mode; | ||
189 | int ret; | ||
190 | |||
191 | if (!count) | ||
192 | return 0; | ||
193 | |||
194 | ret = kstrtobool(buf, &bdreg->bkup_mode_enabled); | ||
195 | if (ret) | ||
196 | return ret; | ||
197 | |||
198 | if (!bdreg->rstbmode_level) | ||
199 | return count; | ||
200 | |||
201 | /* | ||
202 | * Configure DDR Backup Mode, to change the role of the accessory power | ||
203 | * switch from a power switch to a wake-up switch, or vice versa | ||
204 | */ | ||
205 | ret = bd9571mwv_bkup_mode_read(bdreg->bd, &mode); | ||
206 | if (ret) | ||
207 | return ret; | ||
208 | |||
209 | mode &= ~BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK; | ||
210 | if (bdreg->bkup_mode_enabled) | ||
211 | mode |= bdreg->bkup_mode_cnt_keepon; | ||
212 | |||
213 | ret = bd9571mwv_bkup_mode_write(bdreg->bd, mode); | ||
214 | if (ret) | ||
215 | return ret; | ||
216 | |||
217 | return count; | ||
218 | } | ||
219 | |||
220 | static DEVICE_ATTR_RW(backup_mode); | ||
221 | |||
174 | static int bd9571mwv_suspend(struct device *dev) | 222 | static int bd9571mwv_suspend(struct device *dev) |
175 | { | 223 | { |
176 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); | 224 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); |
177 | unsigned int mode; | 225 | unsigned int mode; |
178 | int ret; | 226 | int ret; |
179 | 227 | ||
180 | if (!device_may_wakeup(dev)) | 228 | if (!bdreg->bkup_mode_enabled) |
181 | return 0; | 229 | return 0; |
182 | 230 | ||
183 | /* Save DDR Backup Mode */ | 231 | /* Save DDR Backup Mode */ |
@@ -204,7 +252,7 @@ static int bd9571mwv_resume(struct device *dev) | |||
204 | { | 252 | { |
205 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); | 253 | struct bd9571mwv_reg *bdreg = dev_get_drvdata(dev); |
206 | 254 | ||
207 | if (!device_may_wakeup(dev)) | 255 | if (!bdreg->bkup_mode_enabled) |
208 | return 0; | 256 | return 0; |
209 | 257 | ||
210 | /* Restore DDR Backup Mode */ | 258 | /* Restore DDR Backup Mode */ |
@@ -215,9 +263,15 @@ static const struct dev_pm_ops bd9571mwv_pm = { | |||
215 | SET_SYSTEM_SLEEP_PM_OPS(bd9571mwv_suspend, bd9571mwv_resume) | 263 | SET_SYSTEM_SLEEP_PM_OPS(bd9571mwv_suspend, bd9571mwv_resume) |
216 | }; | 264 | }; |
217 | 265 | ||
266 | static int bd9571mwv_regulator_remove(struct platform_device *pdev) | ||
267 | { | ||
268 | device_remove_file(&pdev->dev, &dev_attr_backup_mode); | ||
269 | return 0; | ||
270 | } | ||
218 | #define DEV_PM_OPS &bd9571mwv_pm | 271 | #define DEV_PM_OPS &bd9571mwv_pm |
219 | #else | 272 | #else |
220 | #define DEV_PM_OPS NULL | 273 | #define DEV_PM_OPS NULL |
274 | #define bd9571mwv_regulator_remove NULL | ||
221 | #endif /* CONFIG_PM_SLEEP */ | 275 | #endif /* CONFIG_PM_SLEEP */ |
222 | 276 | ||
223 | static int bd9571mwv_regulator_probe(struct platform_device *pdev) | 277 | static int bd9571mwv_regulator_probe(struct platform_device *pdev) |
@@ -270,14 +324,21 @@ static int bd9571mwv_regulator_probe(struct platform_device *pdev) | |||
270 | return -EINVAL; | 324 | return -EINVAL; |
271 | } | 325 | } |
272 | 326 | ||
327 | #ifdef CONFIG_PM_SLEEP | ||
273 | if (bdreg->bkup_mode_cnt_keepon) { | 328 | if (bdreg->bkup_mode_cnt_keepon) { |
274 | device_set_wakeup_capable(&pdev->dev, true); | 329 | int ret; |
330 | |||
275 | /* | 331 | /* |
276 | * Wakeup is enabled by default in pulse mode, but needs | 332 | * Backup mode is enabled by default in pulse mode, but needs |
277 | * explicit user setup in level mode. | 333 | * explicit user setup in level mode. |
278 | */ | 334 | */ |
279 | device_set_wakeup_enable(&pdev->dev, bdreg->rstbmode_pulse); | 335 | bdreg->bkup_mode_enabled = bdreg->rstbmode_pulse; |
336 | |||
337 | ret = device_create_file(&pdev->dev, &dev_attr_backup_mode); | ||
338 | if (ret) | ||
339 | return ret; | ||
280 | } | 340 | } |
341 | #endif /* CONFIG_PM_SLEEP */ | ||
281 | 342 | ||
282 | return 0; | 343 | return 0; |
283 | } | 344 | } |
@@ -294,6 +355,7 @@ static struct platform_driver bd9571mwv_regulator_driver = { | |||
294 | .pm = DEV_PM_OPS, | 355 | .pm = DEV_PM_OPS, |
295 | }, | 356 | }, |
296 | .probe = bd9571mwv_regulator_probe, | 357 | .probe = bd9571mwv_regulator_probe, |
358 | .remove = bd9571mwv_regulator_remove, | ||
297 | .id_table = bd9571mwv_regulator_id_table, | 359 | .id_table = bd9571mwv_regulator_id_table, |
298 | }; | 360 | }; |
299 | module_platform_driver(bd9571mwv_regulator_driver); | 361 | module_platform_driver(bd9571mwv_regulator_driver); |
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6ed568b96c0e..bb1324f93143 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c | |||
@@ -1740,6 +1740,8 @@ struct regulator *_regulator_get(struct device *dev, const char *id, | |||
1740 | rdev->use_count = 0; | 1740 | rdev->use_count = 0; |
1741 | } | 1741 | } |
1742 | 1742 | ||
1743 | device_link_add(dev, &rdev->dev, DL_FLAG_STATELESS); | ||
1744 | |||
1743 | return regulator; | 1745 | return regulator; |
1744 | } | 1746 | } |
1745 | 1747 | ||
@@ -1829,9 +1831,21 @@ static void _regulator_put(struct regulator *regulator) | |||
1829 | 1831 | ||
1830 | debugfs_remove_recursive(regulator->debugfs); | 1832 | debugfs_remove_recursive(regulator->debugfs); |
1831 | 1833 | ||
1832 | /* remove any sysfs entries */ | 1834 | if (regulator->dev) { |
1833 | if (regulator->dev) | 1835 | int count = 0; |
1836 | struct regulator *r; | ||
1837 | |||
1838 | list_for_each_entry(r, &rdev->consumer_list, list) | ||
1839 | if (r->dev == regulator->dev) | ||
1840 | count++; | ||
1841 | |||
1842 | if (count == 1) | ||
1843 | device_link_remove(regulator->dev, &rdev->dev); | ||
1844 | |||
1845 | /* remove any sysfs entries */ | ||
1834 | sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); | 1846 | sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); |
1847 | } | ||
1848 | |||
1835 | regulator_lock(rdev); | 1849 | regulator_lock(rdev); |
1836 | list_del(®ulator->list); | 1850 | list_del(®ulator->list); |
1837 | 1851 | ||
@@ -4441,7 +4455,7 @@ void regulator_unregister(struct regulator_dev *rdev) | |||
4441 | EXPORT_SYMBOL_GPL(regulator_unregister); | 4455 | EXPORT_SYMBOL_GPL(regulator_unregister); |
4442 | 4456 | ||
4443 | #ifdef CONFIG_SUSPEND | 4457 | #ifdef CONFIG_SUSPEND |
4444 | static int _regulator_suspend_late(struct device *dev, void *data) | 4458 | static int _regulator_suspend(struct device *dev, void *data) |
4445 | { | 4459 | { |
4446 | struct regulator_dev *rdev = dev_to_rdev(dev); | 4460 | struct regulator_dev *rdev = dev_to_rdev(dev); |
4447 | suspend_state_t *state = data; | 4461 | suspend_state_t *state = data; |
@@ -4455,20 +4469,20 @@ static int _regulator_suspend_late(struct device *dev, void *data) | |||
4455 | } | 4469 | } |
4456 | 4470 | ||
4457 | /** | 4471 | /** |
4458 | * regulator_suspend_late - prepare regulators for system wide suspend | 4472 | * regulator_suspend - prepare regulators for system wide suspend |
4459 | * @state: system suspend state | 4473 | * @state: system suspend state |
4460 | * | 4474 | * |
4461 | * Configure each regulator with it's suspend operating parameters for state. | 4475 | * Configure each regulator with it's suspend operating parameters for state. |
4462 | */ | 4476 | */ |
4463 | static int regulator_suspend_late(struct device *dev) | 4477 | static int regulator_suspend(struct device *dev) |
4464 | { | 4478 | { |
4465 | suspend_state_t state = pm_suspend_target_state; | 4479 | suspend_state_t state = pm_suspend_target_state; |
4466 | 4480 | ||
4467 | return class_for_each_device(®ulator_class, NULL, &state, | 4481 | return class_for_each_device(®ulator_class, NULL, &state, |
4468 | _regulator_suspend_late); | 4482 | _regulator_suspend); |
4469 | } | 4483 | } |
4470 | 4484 | ||
4471 | static int _regulator_resume_early(struct device *dev, void *data) | 4485 | static int _regulator_resume(struct device *dev, void *data) |
4472 | { | 4486 | { |
4473 | int ret = 0; | 4487 | int ret = 0; |
4474 | struct regulator_dev *rdev = dev_to_rdev(dev); | 4488 | struct regulator_dev *rdev = dev_to_rdev(dev); |
@@ -4481,35 +4495,35 @@ static int _regulator_resume_early(struct device *dev, void *data) | |||
4481 | 4495 | ||
4482 | regulator_lock(rdev); | 4496 | regulator_lock(rdev); |
4483 | 4497 | ||
4484 | if (rdev->desc->ops->resume_early && | 4498 | if (rdev->desc->ops->resume && |
4485 | (rstate->enabled == ENABLE_IN_SUSPEND || | 4499 | (rstate->enabled == ENABLE_IN_SUSPEND || |
4486 | rstate->enabled == DISABLE_IN_SUSPEND)) | 4500 | rstate->enabled == DISABLE_IN_SUSPEND)) |
4487 | ret = rdev->desc->ops->resume_early(rdev); | 4501 | ret = rdev->desc->ops->resume(rdev); |
4488 | 4502 | ||
4489 | regulator_unlock(rdev); | 4503 | regulator_unlock(rdev); |
4490 | 4504 | ||
4491 | return ret; | 4505 | return ret; |
4492 | } | 4506 | } |
4493 | 4507 | ||
4494 | static int regulator_resume_early(struct device *dev) | 4508 | static int regulator_resume(struct device *dev) |
4495 | { | 4509 | { |
4496 | suspend_state_t state = pm_suspend_target_state; | 4510 | suspend_state_t state = pm_suspend_target_state; |
4497 | 4511 | ||
4498 | return class_for_each_device(®ulator_class, NULL, &state, | 4512 | return class_for_each_device(®ulator_class, NULL, &state, |
4499 | _regulator_resume_early); | 4513 | _regulator_resume); |
4500 | } | 4514 | } |
4501 | 4515 | ||
4502 | #else /* !CONFIG_SUSPEND */ | 4516 | #else /* !CONFIG_SUSPEND */ |
4503 | 4517 | ||
4504 | #define regulator_suspend_late NULL | 4518 | #define regulator_suspend NULL |
4505 | #define regulator_resume_early NULL | 4519 | #define regulator_resume NULL |
4506 | 4520 | ||
4507 | #endif /* !CONFIG_SUSPEND */ | 4521 | #endif /* !CONFIG_SUSPEND */ |
4508 | 4522 | ||
4509 | #ifdef CONFIG_PM | 4523 | #ifdef CONFIG_PM |
4510 | static const struct dev_pm_ops __maybe_unused regulator_pm_ops = { | 4524 | static const struct dev_pm_ops __maybe_unused regulator_pm_ops = { |
4511 | .suspend_late = regulator_suspend_late, | 4525 | .suspend = regulator_suspend, |
4512 | .resume_early = regulator_resume_early, | 4526 | .resume = regulator_resume, |
4513 | }; | 4527 | }; |
4514 | #endif | 4528 | #endif |
4515 | 4529 | ||
diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c index bd910fe123d9..2131457937b7 100644 --- a/drivers/regulator/cpcap-regulator.c +++ b/drivers/regulator/cpcap-regulator.c | |||
@@ -271,6 +271,29 @@ static struct regulator_ops cpcap_regulator_ops = { | |||
271 | }; | 271 | }; |
272 | 272 | ||
273 | static const unsigned int unknown_val_tbl[] = { 0, }; | 273 | static const unsigned int unknown_val_tbl[] = { 0, }; |
274 | static const unsigned int sw2_sw4_val_tbl[] = { 612500, 625000, 637500, | ||
275 | 650000, 662500, 675000, | ||
276 | 687500, 700000, 712500, | ||
277 | 725000, 737500, 750000, | ||
278 | 762500, 775000, 787500, | ||
279 | 800000, 812500, 825000, | ||
280 | 837500, 850000, 862500, | ||
281 | 875000, 887500, 900000, | ||
282 | 912500, 925000, 937500, | ||
283 | 950000, 962500, 975000, | ||
284 | 987500, 1000000, 1012500, | ||
285 | 1025000, 1037500, 1050000, | ||
286 | 1062500, 1075000, 1087500, | ||
287 | 1100000, 1112500, 1125000, | ||
288 | 1137500, 1150000, 1162500, | ||
289 | 1175000, 1187500, 1200000, | ||
290 | 1212500, 1225000, 1237500, | ||
291 | 1250000, 1262500, 1275000, | ||
292 | 1287500, 1300000, 1312500, | ||
293 | 1325000, 1337500, 1350000, | ||
294 | 1362500, 1375000, 1387500, | ||
295 | 1400000, 1412500, 1425000, | ||
296 | 1437500, 1450000, 1462500, }; | ||
274 | static const unsigned int sw5_val_tbl[] = { 0, 5050000, }; | 297 | static const unsigned int sw5_val_tbl[] = { 0, 5050000, }; |
275 | static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000, | 298 | static const unsigned int vcam_val_tbl[] = { 2600000, 2700000, 2800000, |
276 | 2900000, }; | 299 | 2900000, }; |
@@ -389,6 +412,82 @@ static struct cpcap_regulator omap4_regulators[] = { | |||
389 | { /* sentinel */ }, | 412 | { /* sentinel */ }, |
390 | }; | 413 | }; |
391 | 414 | ||
415 | static struct cpcap_regulator xoom_regulators[] = { | ||
416 | CPCAP_REG(SW1, CPCAP_REG_S1C1, CPCAP_REG_ASSIGN2, | ||
417 | CPCAP_BIT_SW1_SEL, unknown_val_tbl, | ||
418 | 0, 0, 0, 0, 0, 0), | ||
419 | CPCAP_REG(SW2, CPCAP_REG_S2C1, CPCAP_REG_ASSIGN2, | ||
420 | CPCAP_BIT_SW2_SEL, sw2_sw4_val_tbl, | ||
421 | 0xf00, 0x7f, 0, 0x800, 0, 120), | ||
422 | CPCAP_REG(SW3, CPCAP_REG_S3C, CPCAP_REG_ASSIGN2, | ||
423 | CPCAP_BIT_SW3_SEL, unknown_val_tbl, | ||
424 | 0, 0, 0, 0, 0, 0), | ||
425 | CPCAP_REG(SW4, CPCAP_REG_S4C1, CPCAP_REG_ASSIGN2, | ||
426 | CPCAP_BIT_SW4_SEL, sw2_sw4_val_tbl, | ||
427 | 0xf00, 0x7f, 0, 0x900, 0, 100), | ||
428 | CPCAP_REG(SW5, CPCAP_REG_S5C, CPCAP_REG_ASSIGN2, | ||
429 | CPCAP_BIT_SW5_SEL, sw5_val_tbl, | ||
430 | 0x2a, 0, 0, 0x22, 0, 0), | ||
431 | CPCAP_REG(SW6, CPCAP_REG_S6C, CPCAP_REG_ASSIGN2, | ||
432 | CPCAP_BIT_SW6_SEL, unknown_val_tbl, | ||
433 | 0, 0, 0, 0, 0, 0), | ||
434 | CPCAP_REG(VCAM, CPCAP_REG_VCAMC, CPCAP_REG_ASSIGN2, | ||
435 | CPCAP_BIT_VCAM_SEL, vcam_val_tbl, | ||
436 | 0x87, 0x30, 4, 0x7, 0, 420), | ||
437 | CPCAP_REG(VCSI, CPCAP_REG_VCSIC, CPCAP_REG_ASSIGN3, | ||
438 | CPCAP_BIT_VCSI_SEL, vcsi_val_tbl, | ||
439 | 0x47, 0x10, 4, 0x7, 0, 350), | ||
440 | CPCAP_REG(VDAC, CPCAP_REG_VDACC, CPCAP_REG_ASSIGN3, | ||
441 | CPCAP_BIT_VDAC_SEL, vdac_val_tbl, | ||
442 | 0x87, 0x30, 4, 0x3, 0, 420), | ||
443 | CPCAP_REG(VDIG, CPCAP_REG_VDIGC, CPCAP_REG_ASSIGN2, | ||
444 | CPCAP_BIT_VDIG_SEL, vdig_val_tbl, | ||
445 | 0x87, 0x30, 4, 0x5, 0, 420), | ||
446 | CPCAP_REG(VFUSE, CPCAP_REG_VFUSEC, CPCAP_REG_ASSIGN3, | ||
447 | CPCAP_BIT_VFUSE_SEL, vfuse_val_tbl, | ||
448 | 0x80, 0xf, 0, 0x80, 0, 420), | ||
449 | CPCAP_REG(VHVIO, CPCAP_REG_VHVIOC, CPCAP_REG_ASSIGN3, | ||
450 | CPCAP_BIT_VHVIO_SEL, vhvio_val_tbl, | ||
451 | 0x17, 0, 0, 0x2, 0, 0), | ||
452 | CPCAP_REG(VSDIO, CPCAP_REG_VSDIOC, CPCAP_REG_ASSIGN2, | ||
453 | CPCAP_BIT_VSDIO_SEL, vsdio_val_tbl, | ||
454 | 0x87, 0x38, 3, 0x2, 0, 420), | ||
455 | CPCAP_REG(VPLL, CPCAP_REG_VPLLC, CPCAP_REG_ASSIGN3, | ||
456 | CPCAP_BIT_VPLL_SEL, vpll_val_tbl, | ||
457 | 0x43, 0x18, 3, 0x1, 0, 420), | ||
458 | CPCAP_REG(VRF1, CPCAP_REG_VRF1C, CPCAP_REG_ASSIGN3, | ||
459 | CPCAP_BIT_VRF1_SEL, vrf1_val_tbl, | ||
460 | 0xac, 0x2, 1, 0xc, 0, 10), | ||
461 | CPCAP_REG(VRF2, CPCAP_REG_VRF2C, CPCAP_REG_ASSIGN3, | ||
462 | CPCAP_BIT_VRF2_SEL, vrf2_val_tbl, | ||
463 | 0x23, 0x8, 3, 0x3, 0, 10), | ||
464 | CPCAP_REG(VRFREF, CPCAP_REG_VRFREFC, CPCAP_REG_ASSIGN3, | ||
465 | CPCAP_BIT_VRFREF_SEL, vrfref_val_tbl, | ||
466 | 0x23, 0x8, 3, 0x3, 0, 420), | ||
467 | CPCAP_REG(VWLAN1, CPCAP_REG_VWLAN1C, CPCAP_REG_ASSIGN3, | ||
468 | CPCAP_BIT_VWLAN1_SEL, vwlan1_val_tbl, | ||
469 | 0x47, 0x10, 4, 0x5, 0, 420), | ||
470 | CPCAP_REG(VWLAN2, CPCAP_REG_VWLAN2C, CPCAP_REG_ASSIGN3, | ||
471 | CPCAP_BIT_VWLAN2_SEL, vwlan2_val_tbl, | ||
472 | 0x20c, 0xc0, 6, 0x8, 0, 420), | ||
473 | CPCAP_REG(VSIM, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3, | ||
474 | 0xffff, vsim_val_tbl, | ||
475 | 0x23, 0x8, 3, 0x3, 0, 420), | ||
476 | CPCAP_REG(VSIMCARD, CPCAP_REG_VSIMC, CPCAP_REG_ASSIGN3, | ||
477 | 0xffff, vsimcard_val_tbl, | ||
478 | 0x1e80, 0x8, 3, 0x1e00, 0, 420), | ||
479 | CPCAP_REG(VVIB, CPCAP_REG_VVIBC, CPCAP_REG_ASSIGN3, | ||
480 | CPCAP_BIT_VVIB_SEL, vvib_val_tbl, | ||
481 | 0x1, 0xc, 2, 0, 0x1, 500), | ||
482 | CPCAP_REG(VUSB, CPCAP_REG_VUSBC, CPCAP_REG_ASSIGN3, | ||
483 | CPCAP_BIT_VUSB_SEL, vusb_val_tbl, | ||
484 | 0x11c, 0x40, 6, 0xc, 0, 0), | ||
485 | CPCAP_REG(VAUDIO, CPCAP_REG_VAUDIOC, CPCAP_REG_ASSIGN4, | ||
486 | CPCAP_BIT_VAUDIO_SEL, vaudio_val_tbl, | ||
487 | 0x16, 0x1, 0, 0x4, 0, 0), | ||
488 | { /* sentinel */ }, | ||
489 | }; | ||
490 | |||
392 | static const struct of_device_id cpcap_regulator_id_table[] = { | 491 | static const struct of_device_id cpcap_regulator_id_table[] = { |
393 | { | 492 | { |
394 | .compatible = "motorola,cpcap-regulator", | 493 | .compatible = "motorola,cpcap-regulator", |
@@ -397,6 +496,10 @@ static const struct of_device_id cpcap_regulator_id_table[] = { | |||
397 | .compatible = "motorola,mapphone-cpcap-regulator", | 496 | .compatible = "motorola,mapphone-cpcap-regulator", |
398 | .data = omap4_regulators, | 497 | .data = omap4_regulators, |
399 | }, | 498 | }, |
499 | { | ||
500 | .compatible = "motorola,xoom-cpcap-regulator", | ||
501 | .data = xoom_regulators, | ||
502 | }, | ||
400 | {}, | 503 | {}, |
401 | }; | 504 | }; |
402 | MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table); | 505 | MODULE_DEVICE_TABLE(of, cpcap_regulator_id_table); |
diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index 0db288ce319c..bc7f4751bf9c 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c | |||
@@ -1,19 +1,9 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * max14577.c - Regulator driver for the Maxim 14577/77836 | 2 | // |
3 | * | 3 | // max14577.c - Regulator driver for the Maxim 14577/77836 |
4 | * Copyright (C) 2013,2014 Samsung Electronics | 4 | // |
5 | * Krzysztof Kozlowski <krzk@kernel.org> | 5 | // Copyright (C) 2013,2014 Samsung Electronics |
6 | * | 6 | // Krzysztof Kozlowski <krzk@kernel.org> |
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | 7 | ||
18 | #include <linux/module.h> | 8 | #include <linux/module.h> |
19 | #include <linux/platform_device.h> | 9 | #include <linux/platform_device.h> |
diff --git a/drivers/regulator/max77686-regulator.c b/drivers/regulator/max77686-regulator.c index c301f3733475..bee060937f56 100644 --- a/drivers/regulator/max77686-regulator.c +++ b/drivers/regulator/max77686-regulator.c | |||
@@ -1,26 +1,12 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * max77686.c - Regulator driver for the Maxim 77686 | 2 | // |
3 | * | 3 | // max77686.c - Regulator driver for the Maxim 77686 |
4 | * Copyright (C) 2012 Samsung Electronics | 4 | // |
5 | * Chiwoong Byun <woong.byun@samsung.com> | 5 | // Copyright (C) 2012 Samsung Electronics |
6 | * Jonghwa Lee <jonghwa3.lee@samsung.com> | 6 | // Chiwoong Byun <woong.byun@samsung.com> |
7 | * | 7 | // Jonghwa Lee <jonghwa3.lee@samsung.com> |
8 | * This program is free software; you can redistribute it and/or modify | 8 | // |
9 | * it under the terms of the GNU General Public License as published by | 9 | // This driver is based on max8997.c |
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * This driver is based on max8997.c | ||
23 | */ | ||
24 | 10 | ||
25 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
26 | #include <linux/bug.h> | 12 | #include <linux/bug.h> |
diff --git a/drivers/regulator/max77693-regulator.c b/drivers/regulator/max77693-regulator.c index e7000e777292..077ecbbfdf76 100644 --- a/drivers/regulator/max77693-regulator.c +++ b/drivers/regulator/max77693-regulator.c | |||
@@ -1,26 +1,12 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * max77693.c - Regulator driver for the Maxim 77693 and 77843 | 2 | // |
3 | * | 3 | // max77693.c - Regulator driver for the Maxim 77693 and 77843 |
4 | * Copyright (C) 2013-2015 Samsung Electronics | 4 | // |
5 | * Jonghwa Lee <jonghwa3.lee@samsung.com> | 5 | // Copyright (C) 2013-2015 Samsung Electronics |
6 | * Krzysztof Kozlowski <krzk@kernel.org> | 6 | // Jonghwa Lee <jonghwa3.lee@samsung.com> |
7 | * | 7 | // Krzysztof Kozlowski <krzk@kernel.org> |
8 | * This program is free software; you can redistribute it and/or modify | 8 | // |
9 | * it under the terms of the GNU General Public License as published by | 9 | // This driver is based on max77686.c |
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | * | ||
22 | * This driver is based on max77686.c | ||
23 | */ | ||
24 | 10 | ||
25 | #include <linux/err.h> | 11 | #include <linux/err.h> |
26 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
diff --git a/drivers/regulator/max77802-regulator.c b/drivers/regulator/max77802-regulator.c index b6261903818c..c30cf5c9f2de 100644 --- a/drivers/regulator/max77802-regulator.c +++ b/drivers/regulator/max77802-regulator.c | |||
@@ -1,25 +1,15 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * max77802.c - Regulator driver for the Maxim 77802 | 2 | // |
3 | * | 3 | // max77802.c - Regulator driver for the Maxim 77802 |
4 | * Copyright (C) 2013-2014 Google, Inc | 4 | // |
5 | * Simon Glass <sjg@chromium.org> | 5 | // Copyright (C) 2013-2014 Google, Inc |
6 | * | 6 | // Simon Glass <sjg@chromium.org> |
7 | * Copyright (C) 2012 Samsung Electronics | 7 | // |
8 | * Chiwoong Byun <woong.byun@samsung.com> | 8 | // Copyright (C) 2012 Samsung Electronics |
9 | * Jonghwa Lee <jonghwa3.lee@samsung.com> | 9 | // Chiwoong Byun <woong.byun@samsung.com> |
10 | * | 10 | // Jonghwa Lee <jonghwa3.lee@samsung.com> |
11 | * This program is free software; you can redistribute it and/or modify | 11 | // |
12 | * it under the terms of the GNU General Public License as published by | 12 | // This driver is based on max8997.c |
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * This driver is based on max8997.c | ||
22 | */ | ||
23 | 13 | ||
24 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
25 | #include <linux/bug.h> | 15 | #include <linux/bug.h> |
diff --git a/drivers/regulator/max8997-regulator.c b/drivers/regulator/max8997-regulator.c index a8ea30ee18a6..ad0c806b0737 100644 --- a/drivers/regulator/max8997-regulator.c +++ b/drivers/regulator/max8997-regulator.c | |||
@@ -1,25 +1,11 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * max8997.c - Regulator driver for the Maxim 8997/8966 | 2 | // |
3 | * | 3 | // max8997.c - Regulator driver for the Maxim 8997/8966 |
4 | * Copyright (C) 2011 Samsung Electronics | 4 | // |
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | 5 | // Copyright (C) 2011 Samsung Electronics |
6 | * | 6 | // MyungJoo Ham <myungjoo.ham@samsung.com> |
7 | * This program is free software; you can redistribute it and/or modify | 7 | // |
8 | * it under the terms of the GNU General Public License as published by | 8 | // This driver is based on max8998.c |
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | * This driver is based on max8998.c | ||
22 | */ | ||
23 | 9 | ||
24 | #include <linux/bug.h> | 10 | #include <linux/bug.h> |
25 | #include <linux/err.h> | 11 | #include <linux/err.h> |
@@ -165,8 +151,7 @@ static int max8997_list_voltage(struct regulator_dev *rdev, | |||
165 | int rid = rdev_get_id(rdev); | 151 | int rid = rdev_get_id(rdev); |
166 | int val; | 152 | int val; |
167 | 153 | ||
168 | if (rid >= ARRAY_SIZE(reg_voltage_map) || | 154 | if (rid < 0 || rid >= ARRAY_SIZE(reg_voltage_map)) |
169 | rid < 0) | ||
170 | return -EINVAL; | 155 | return -EINVAL; |
171 | 156 | ||
172 | desc = reg_voltage_map[rid]; | 157 | desc = reg_voltage_map[rid]; |
diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 6b9f262ebbb0..271bb736f3f5 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c | |||
@@ -1,24 +1,10 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * max8998.c - Voltage regulator driver for the Maxim 8998 | 2 | // |
3 | * | 3 | // max8998.c - Voltage regulator driver for the Maxim 8998 |
4 | * Copyright (C) 2009-2010 Samsung Electronics | 4 | // |
5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | // Copyright (C) 2009-2010 Samsung Electronics |
6 | * Marek Szyprowski <m.szyprowski@samsung.com> | 6 | // Kyungmin Park <kyungmin.park@samsung.com> |
7 | * | 7 | // Marek Szyprowski <m.szyprowski@samsung.com> |
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | 8 | ||
23 | #include <linux/module.h> | 9 | #include <linux/module.h> |
24 | #include <linux/init.h> | 10 | #include <linux/init.h> |
diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 8d9dbcc775ea..31c3a236120a 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/regmap.h> | 18 | #include <linux/regmap.h> |
19 | 19 | ||
20 | #define PFUZE_FLAG_DISABLE_SW BIT(1) | ||
21 | |||
20 | #define PFUZE_NUMREGS 128 | 22 | #define PFUZE_NUMREGS 128 |
21 | #define PFUZE100_VOL_OFFSET 0 | 23 | #define PFUZE100_VOL_OFFSET 0 |
22 | #define PFUZE100_STANDBY_OFFSET 1 | 24 | #define PFUZE100_STANDBY_OFFSET 1 |
@@ -44,16 +46,18 @@ | |||
44 | #define PFUZE100_VGEN5VOL 0x70 | 46 | #define PFUZE100_VGEN5VOL 0x70 |
45 | #define PFUZE100_VGEN6VOL 0x71 | 47 | #define PFUZE100_VGEN6VOL 0x71 |
46 | 48 | ||
47 | enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3 }; | 49 | enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3, PFUZE3001 = 0x31, }; |
48 | 50 | ||
49 | struct pfuze_regulator { | 51 | struct pfuze_regulator { |
50 | struct regulator_desc desc; | 52 | struct regulator_desc desc; |
51 | unsigned char stby_reg; | 53 | unsigned char stby_reg; |
52 | unsigned char stby_mask; | 54 | unsigned char stby_mask; |
55 | bool sw_reg; | ||
53 | }; | 56 | }; |
54 | 57 | ||
55 | struct pfuze_chip { | 58 | struct pfuze_chip { |
56 | int chip_id; | 59 | int chip_id; |
60 | int flags; | ||
57 | struct regmap *regmap; | 61 | struct regmap *regmap; |
58 | struct device *dev; | 62 | struct device *dev; |
59 | struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR]; | 63 | struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR]; |
@@ -92,6 +96,7 @@ static const struct i2c_device_id pfuze_device_id[] = { | |||
92 | {.name = "pfuze100", .driver_data = PFUZE100}, | 96 | {.name = "pfuze100", .driver_data = PFUZE100}, |
93 | {.name = "pfuze200", .driver_data = PFUZE200}, | 97 | {.name = "pfuze200", .driver_data = PFUZE200}, |
94 | {.name = "pfuze3000", .driver_data = PFUZE3000}, | 98 | {.name = "pfuze3000", .driver_data = PFUZE3000}, |
99 | {.name = "pfuze3001", .driver_data = PFUZE3001}, | ||
95 | { } | 100 | { } |
96 | }; | 101 | }; |
97 | MODULE_DEVICE_TABLE(i2c, pfuze_device_id); | 102 | MODULE_DEVICE_TABLE(i2c, pfuze_device_id); |
@@ -100,6 +105,7 @@ static const struct of_device_id pfuze_dt_ids[] = { | |||
100 | { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, | 105 | { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, |
101 | { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, | 106 | { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, |
102 | { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000}, | 107 | { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000}, |
108 | { .compatible = "fsl,pfuze3001", .data = (void *)PFUZE3001}, | ||
103 | { } | 109 | { } |
104 | }; | 110 | }; |
105 | MODULE_DEVICE_TABLE(of, pfuze_dt_ids); | 111 | MODULE_DEVICE_TABLE(of, pfuze_dt_ids); |
@@ -108,10 +114,28 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) | |||
108 | { | 114 | { |
109 | struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev); | 115 | struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev); |
110 | int id = rdev_get_id(rdev); | 116 | int id = rdev_get_id(rdev); |
117 | bool reg_has_ramp_delay; | ||
111 | unsigned int ramp_bits; | 118 | unsigned int ramp_bits; |
112 | int ret; | 119 | int ret; |
113 | 120 | ||
114 | if (id < PFUZE100_SWBST) { | 121 | switch (pfuze100->chip_id) { |
122 | case PFUZE3001: | ||
123 | /* no dynamic voltage scaling for PF3001 */ | ||
124 | reg_has_ramp_delay = false; | ||
125 | break; | ||
126 | case PFUZE3000: | ||
127 | reg_has_ramp_delay = (id < PFUZE3000_SWBST); | ||
128 | break; | ||
129 | case PFUZE200: | ||
130 | reg_has_ramp_delay = (id < PFUZE200_SWBST); | ||
131 | break; | ||
132 | case PFUZE100: | ||
133 | default: | ||
134 | reg_has_ramp_delay = (id < PFUZE100_SWBST); | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | if (reg_has_ramp_delay) { | ||
115 | ramp_delay = 12500 / ramp_delay; | 139 | ramp_delay = 12500 / ramp_delay; |
116 | ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3); | 140 | ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3); |
117 | ret = regmap_update_bits(pfuze100->regmap, | 141 | ret = regmap_update_bits(pfuze100->regmap, |
@@ -119,8 +143,9 @@ static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) | |||
119 | 0xc0, ramp_bits << 6); | 143 | 0xc0, ramp_bits << 6); |
120 | if (ret < 0) | 144 | if (ret < 0) |
121 | dev_err(pfuze100->dev, "ramp failed, err %d\n", ret); | 145 | dev_err(pfuze100->dev, "ramp failed, err %d\n", ret); |
122 | } else | 146 | } else { |
123 | ret = -EACCES; | 147 | ret = -EACCES; |
148 | } | ||
124 | 149 | ||
125 | return ret; | 150 | return ret; |
126 | } | 151 | } |
@@ -142,6 +167,14 @@ static const struct regulator_ops pfuze100_fixed_regulator_ops = { | |||
142 | }; | 167 | }; |
143 | 168 | ||
144 | static const struct regulator_ops pfuze100_sw_regulator_ops = { | 169 | static const struct regulator_ops pfuze100_sw_regulator_ops = { |
170 | .list_voltage = regulator_list_voltage_linear, | ||
171 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | ||
172 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | ||
173 | .set_voltage_time_sel = regulator_set_voltage_time_sel, | ||
174 | .set_ramp_delay = pfuze100_set_ramp_delay, | ||
175 | }; | ||
176 | |||
177 | static const struct regulator_ops pfuze100_sw_disable_regulator_ops = { | ||
145 | .enable = regulator_enable_regmap, | 178 | .enable = regulator_enable_regmap, |
146 | .disable = regulator_disable_regmap, | 179 | .disable = regulator_disable_regmap, |
147 | .is_enabled = regulator_is_enabled_regmap, | 180 | .is_enabled = regulator_is_enabled_regmap, |
@@ -192,13 +225,11 @@ static const struct regulator_ops pfuze100_swb_regulator_ops = { | |||
192 | .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ | 225 | .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ |
193 | .vsel_mask = 0x3f, \ | 226 | .vsel_mask = 0x3f, \ |
194 | .enable_reg = (base) + PFUZE100_MODE_OFFSET, \ | 227 | .enable_reg = (base) + PFUZE100_MODE_OFFSET, \ |
195 | .enable_val = 0xc, \ | ||
196 | .disable_val = 0x0, \ | ||
197 | .enable_mask = 0xf, \ | 228 | .enable_mask = 0xf, \ |
198 | .enable_time = 500, \ | ||
199 | }, \ | 229 | }, \ |
200 | .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ | 230 | .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ |
201 | .stby_mask = 0x3f, \ | 231 | .stby_mask = 0x3f, \ |
232 | .sw_reg = true, \ | ||
202 | } | 233 | } |
203 | 234 | ||
204 | #define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages) \ | 235 | #define PFUZE100_SWB_REG(_chip, _name, base, mask, voltages) \ |
@@ -361,6 +392,19 @@ static struct pfuze_regulator pfuze3000_regulators[] = { | |||
361 | PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), | 392 | PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), |
362 | }; | 393 | }; |
363 | 394 | ||
395 | static struct pfuze_regulator pfuze3001_regulators[] = { | ||
396 | PFUZE100_SWB_REG(PFUZE3001, SW1, PFUZE100_SW1ABVOL, 0x1f, pfuze3000_sw1a), | ||
397 | PFUZE100_SWB_REG(PFUZE3001, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo), | ||
398 | PFUZE3000_SW3_REG(PFUZE3001, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000), | ||
399 | PFUZE100_SWB_REG(PFUZE3001, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), | ||
400 | PFUZE100_VGEN_REG(PFUZE3001, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000), | ||
401 | PFUZE100_VGEN_REG(PFUZE3001, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), | ||
402 | PFUZE3000_VCC_REG(PFUZE3001, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000), | ||
403 | PFUZE3000_VCC_REG(PFUZE3001, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000), | ||
404 | PFUZE100_VGEN_REG(PFUZE3001, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), | ||
405 | PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), | ||
406 | }; | ||
407 | |||
364 | #ifdef CONFIG_OF | 408 | #ifdef CONFIG_OF |
365 | /* PFUZE100 */ | 409 | /* PFUZE100 */ |
366 | static struct of_regulator_match pfuze100_matches[] = { | 410 | static struct of_regulator_match pfuze100_matches[] = { |
@@ -418,6 +462,21 @@ static struct of_regulator_match pfuze3000_matches[] = { | |||
418 | { .name = "vldo4", }, | 462 | { .name = "vldo4", }, |
419 | }; | 463 | }; |
420 | 464 | ||
465 | /* PFUZE3001 */ | ||
466 | static struct of_regulator_match pfuze3001_matches[] = { | ||
467 | |||
468 | { .name = "sw1", }, | ||
469 | { .name = "sw2", }, | ||
470 | { .name = "sw3", }, | ||
471 | { .name = "vsnvs", }, | ||
472 | { .name = "vldo1", }, | ||
473 | { .name = "vldo2", }, | ||
474 | { .name = "vccsd", }, | ||
475 | { .name = "v33", }, | ||
476 | { .name = "vldo3", }, | ||
477 | { .name = "vldo4", }, | ||
478 | }; | ||
479 | |||
421 | static struct of_regulator_match *pfuze_matches; | 480 | static struct of_regulator_match *pfuze_matches; |
422 | 481 | ||
423 | static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) | 482 | static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) |
@@ -430,6 +489,9 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) | |||
430 | if (!np) | 489 | if (!np) |
431 | return -EINVAL; | 490 | return -EINVAL; |
432 | 491 | ||
492 | if (of_property_read_bool(np, "fsl,pfuze-support-disable-sw")) | ||
493 | chip->flags |= PFUZE_FLAG_DISABLE_SW; | ||
494 | |||
433 | parent = of_get_child_by_name(np, "regulators"); | 495 | parent = of_get_child_by_name(np, "regulators"); |
434 | if (!parent) { | 496 | if (!parent) { |
435 | dev_err(dev, "regulators node not found\n"); | 497 | dev_err(dev, "regulators node not found\n"); |
@@ -437,6 +499,11 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) | |||
437 | } | 499 | } |
438 | 500 | ||
439 | switch (chip->chip_id) { | 501 | switch (chip->chip_id) { |
502 | case PFUZE3001: | ||
503 | pfuze_matches = pfuze3001_matches; | ||
504 | ret = of_regulator_match(dev, parent, pfuze3001_matches, | ||
505 | ARRAY_SIZE(pfuze3001_matches)); | ||
506 | break; | ||
440 | case PFUZE3000: | 507 | case PFUZE3000: |
441 | pfuze_matches = pfuze3000_matches; | 508 | pfuze_matches = pfuze3000_matches; |
442 | ret = of_regulator_match(dev, parent, pfuze3000_matches, | 509 | ret = of_regulator_match(dev, parent, pfuze3000_matches, |
@@ -508,7 +575,8 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip) | |||
508 | */ | 575 | */ |
509 | dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8"); | 576 | dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8"); |
510 | } else if ((value & 0x0f) != pfuze_chip->chip_id && | 577 | } else if ((value & 0x0f) != pfuze_chip->chip_id && |
511 | (value & 0xf0) >> 4 != pfuze_chip->chip_id) { | 578 | (value & 0xf0) >> 4 != pfuze_chip->chip_id && |
579 | (value != pfuze_chip->chip_id)) { | ||
512 | /* device id NOT match with your setting */ | 580 | /* device id NOT match with your setting */ |
513 | dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); | 581 | dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); |
514 | return -ENODEV; | 582 | return -ENODEV; |
@@ -588,6 +656,13 @@ static int pfuze100_regulator_probe(struct i2c_client *client, | |||
588 | 656 | ||
589 | /* use the right regulators after identify the right device */ | 657 | /* use the right regulators after identify the right device */ |
590 | switch (pfuze_chip->chip_id) { | 658 | switch (pfuze_chip->chip_id) { |
659 | case PFUZE3001: | ||
660 | pfuze_chip->pfuze_regulators = pfuze3001_regulators; | ||
661 | regulator_num = ARRAY_SIZE(pfuze3001_regulators); | ||
662 | sw_check_start = PFUZE3001_SW2; | ||
663 | sw_check_end = PFUZE3001_SW2; | ||
664 | sw_hi = 1 << 3; | ||
665 | break; | ||
591 | case PFUZE3000: | 666 | case PFUZE3000: |
592 | pfuze_chip->pfuze_regulators = pfuze3000_regulators; | 667 | pfuze_chip->pfuze_regulators = pfuze3000_regulators; |
593 | regulator_num = ARRAY_SIZE(pfuze3000_regulators); | 668 | regulator_num = ARRAY_SIZE(pfuze3000_regulators); |
@@ -611,7 +686,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client, | |||
611 | } | 686 | } |
612 | dev_info(&client->dev, "pfuze%s found.\n", | 687 | dev_info(&client->dev, "pfuze%s found.\n", |
613 | (pfuze_chip->chip_id == PFUZE100) ? "100" : | 688 | (pfuze_chip->chip_id == PFUZE100) ? "100" : |
614 | ((pfuze_chip->chip_id == PFUZE200) ? "200" : "3000")); | 689 | (((pfuze_chip->chip_id == PFUZE200) ? "200" : |
690 | ((pfuze_chip->chip_id == PFUZE3000) ? "3000" : "3001")))); | ||
615 | 691 | ||
616 | memcpy(pfuze_chip->regulator_descs, pfuze_chip->pfuze_regulators, | 692 | memcpy(pfuze_chip->regulator_descs, pfuze_chip->pfuze_regulators, |
617 | sizeof(pfuze_chip->regulator_descs)); | 693 | sizeof(pfuze_chip->regulator_descs)); |
@@ -636,7 +712,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client, | |||
636 | if (i >= sw_check_start && i <= sw_check_end) { | 712 | if (i >= sw_check_start && i <= sw_check_end) { |
637 | regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val); | 713 | regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val); |
638 | if (val & sw_hi) { | 714 | if (val & sw_hi) { |
639 | if (pfuze_chip->chip_id == PFUZE3000) { | 715 | if (pfuze_chip->chip_id == PFUZE3000 || |
716 | pfuze_chip->chip_id == PFUZE3001) { | ||
640 | desc->volt_table = pfuze3000_sw2hi; | 717 | desc->volt_table = pfuze3000_sw2hi; |
641 | desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi); | 718 | desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi); |
642 | } else { | 719 | } else { |
@@ -647,6 +724,21 @@ static int pfuze100_regulator_probe(struct i2c_client *client, | |||
647 | } | 724 | } |
648 | } | 725 | } |
649 | 726 | ||
727 | /* | ||
728 | * Allow SW regulators to turn off. Checking it trough a flag is | ||
729 | * a workaround to keep the backward compatibility with existing | ||
730 | * old dtb's which may relay on the fact that we didn't disable | ||
731 | * the switched regulator till yet. | ||
732 | */ | ||
733 | if (pfuze_chip->flags & PFUZE_FLAG_DISABLE_SW) { | ||
734 | if (pfuze_chip->regulator_descs[i].sw_reg) { | ||
735 | desc->ops = &pfuze100_sw_disable_regulator_ops; | ||
736 | desc->enable_val = 0x8; | ||
737 | desc->disable_val = 0x0; | ||
738 | desc->enable_time = 500; | ||
739 | } | ||
740 | } | ||
741 | |||
650 | config.dev = &client->dev; | 742 | config.dev = &client->dev; |
651 | config.init_data = init_data; | 743 | config.init_data = init_data; |
652 | config.driver_data = pfuze_chip; | 744 | config.driver_data = pfuze_chip; |
@@ -675,5 +767,5 @@ static struct i2c_driver pfuze_driver = { | |||
675 | module_i2c_driver(pfuze_driver); | 767 | module_i2c_driver(pfuze_driver); |
676 | 768 | ||
677 | MODULE_AUTHOR("Robin Gong <b38343@freescale.com>"); | 769 | MODULE_AUTHOR("Robin Gong <b38343@freescale.com>"); |
678 | MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000 PMIC"); | 770 | MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100/200/3000/3001 PMIC"); |
679 | MODULE_LICENSE("GPL v2"); | 771 | MODULE_LICENSE("GPL v2"); |
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c new file mode 100644 index 000000000000..9f27daebd8c8 --- /dev/null +++ b/drivers/regulator/qcom-rpmh-regulator.c | |||
@@ -0,0 +1,769 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (c) 2018, The Linux Foundation. All rights reserved. | ||
3 | |||
4 | #define pr_fmt(fmt) "%s: " fmt, __func__ | ||
5 | |||
6 | #include <linux/err.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/of.h> | ||
10 | #include <linux/of_device.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/regulator/driver.h> | ||
15 | #include <linux/regulator/machine.h> | ||
16 | #include <linux/regulator/of_regulator.h> | ||
17 | |||
18 | #include <soc/qcom/cmd-db.h> | ||
19 | #include <soc/qcom/rpmh.h> | ||
20 | |||
21 | #include <dt-bindings/regulator/qcom,rpmh-regulator.h> | ||
22 | |||
23 | /** | ||
24 | * enum rpmh_regulator_type - supported RPMh accelerator types | ||
25 | * %VRM: RPMh VRM accelerator which supports voting on enable, voltage, | ||
26 | * and mode of LDO, SMPS, and BOB type PMIC regulators. | ||
27 | * %XOB: RPMh XOB accelerator which supports voting on the enable state | ||
28 | * of PMIC regulators. | ||
29 | */ | ||
30 | enum rpmh_regulator_type { | ||
31 | VRM, | ||
32 | XOB, | ||
33 | }; | ||
34 | |||
35 | #define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0 | ||
36 | #define RPMH_REGULATOR_REG_ENABLE 0x4 | ||
37 | #define RPMH_REGULATOR_REG_VRM_MODE 0x8 | ||
38 | |||
39 | #define PMIC4_LDO_MODE_RETENTION 4 | ||
40 | #define PMIC4_LDO_MODE_LPM 5 | ||
41 | #define PMIC4_LDO_MODE_HPM 7 | ||
42 | |||
43 | #define PMIC4_SMPS_MODE_RETENTION 4 | ||
44 | #define PMIC4_SMPS_MODE_PFM 5 | ||
45 | #define PMIC4_SMPS_MODE_AUTO 6 | ||
46 | #define PMIC4_SMPS_MODE_PWM 7 | ||
47 | |||
48 | #define PMIC4_BOB_MODE_PASS 0 | ||
49 | #define PMIC4_BOB_MODE_PFM 1 | ||
50 | #define PMIC4_BOB_MODE_AUTO 2 | ||
51 | #define PMIC4_BOB_MODE_PWM 3 | ||
52 | |||
53 | /** | ||
54 | * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations | ||
55 | * @regulator_type: RPMh accelerator type used to manage this | ||
56 | * regulator | ||
57 | * @ops: Pointer to regulator ops callback structure | ||
58 | * @voltage_range: The single range of voltages supported by this | ||
59 | * PMIC regulator type | ||
60 | * @n_voltages: The number of unique voltage set points defined | ||
61 | * by voltage_range | ||
62 | * @hpm_min_load_uA: Minimum load current in microamps that requires | ||
63 | * high power mode (HPM) operation. This is used | ||
64 | * for LDO hardware type regulators only. | ||
65 | * @pmic_mode_map: Array indexed by regulator framework mode | ||
66 | * containing PMIC hardware modes. Must be large | ||
67 | * enough to index all framework modes supported | ||
68 | * by this regulator hardware type. | ||
69 | * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined | ||
70 | * in device tree to a regulator framework mode | ||
71 | */ | ||
72 | struct rpmh_vreg_hw_data { | ||
73 | enum rpmh_regulator_type regulator_type; | ||
74 | const struct regulator_ops *ops; | ||
75 | const struct regulator_linear_range voltage_range; | ||
76 | int n_voltages; | ||
77 | int hpm_min_load_uA; | ||
78 | const int *pmic_mode_map; | ||
79 | unsigned int (*of_map_mode)(unsigned int mode); | ||
80 | }; | ||
81 | |||
82 | /** | ||
83 | * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a | ||
84 | * single regulator device | ||
85 | * @dev: Device pointer for the top-level PMIC RPMh | ||
86 | * regulator parent device. This is used as a | ||
87 | * handle in RPMh write requests. | ||
88 | * @addr: Base address of the regulator resource within | ||
89 | * an RPMh accelerator | ||
90 | * @rdesc: Regulator descriptor | ||
91 | * @hw_data: PMIC regulator configuration data for this RPMh | ||
92 | * regulator | ||
93 | * @always_wait_for_ack: Boolean flag indicating if a request must always | ||
94 | * wait for an ACK from RPMh before continuing even | ||
95 | * if it corresponds to a strictly lower power | ||
96 | * state (e.g. enabled --> disabled). | ||
97 | * @enabled: Flag indicating if the regulator is enabled or | ||
98 | * not | ||
99 | * @bypassed: Boolean indicating if the regulator is in | ||
100 | * bypass (pass-through) mode or not. This is | ||
101 | * only used by BOB rpmh-regulator resources. | ||
102 | * @voltage_selector: Selector used for get_voltage_sel() and | ||
103 | * set_voltage_sel() callbacks | ||
104 | * @mode: RPMh VRM regulator current framework mode | ||
105 | */ | ||
106 | struct rpmh_vreg { | ||
107 | struct device *dev; | ||
108 | u32 addr; | ||
109 | struct regulator_desc rdesc; | ||
110 | const struct rpmh_vreg_hw_data *hw_data; | ||
111 | bool always_wait_for_ack; | ||
112 | |||
113 | int enabled; | ||
114 | bool bypassed; | ||
115 | int voltage_selector; | ||
116 | unsigned int mode; | ||
117 | }; | ||
118 | |||
119 | /** | ||
120 | * struct rpmh_vreg_init_data - initialization data for an RPMh regulator | ||
121 | * @name: Name for the regulator which also corresponds | ||
122 | * to the device tree subnode name of the regulator | ||
123 | * @resource_name: RPMh regulator resource name format string. | ||
124 | * This must include exactly one field: '%s' which | ||
125 | * is filled at run-time with the PMIC ID provided | ||
126 | * by device tree property qcom,pmic-id. Example: | ||
127 | * "ldo%s1" for RPMh resource "ldoa1". | ||
128 | * @supply_name: Parent supply regulator name | ||
129 | * @hw_data: Configuration data for this PMIC regulator type | ||
130 | */ | ||
131 | struct rpmh_vreg_init_data { | ||
132 | const char *name; | ||
133 | const char *resource_name; | ||
134 | const char *supply_name; | ||
135 | const struct rpmh_vreg_hw_data *hw_data; | ||
136 | }; | ||
137 | |||
138 | /** | ||
139 | * rpmh_regulator_send_request() - send the request to RPMh | ||
140 | * @vreg: Pointer to the RPMh regulator | ||
141 | * @cmd: Pointer to the RPMh command to send | ||
142 | * @wait_for_ack: Boolean indicating if execution must wait until the | ||
143 | * request has been acknowledged as complete | ||
144 | * | ||
145 | * Return: 0 on success, errno on failure | ||
146 | */ | ||
147 | static int rpmh_regulator_send_request(struct rpmh_vreg *vreg, | ||
148 | struct tcs_cmd *cmd, bool wait_for_ack) | ||
149 | { | ||
150 | int ret; | ||
151 | |||
152 | if (wait_for_ack || vreg->always_wait_for_ack) | ||
153 | ret = rpmh_write(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, 1); | ||
154 | else | ||
155 | ret = rpmh_write_async(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, | ||
156 | 1); | ||
157 | |||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | static int _rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, | ||
162 | unsigned int selector, bool wait_for_ack) | ||
163 | { | ||
164 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
165 | struct tcs_cmd cmd = { | ||
166 | .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE, | ||
167 | }; | ||
168 | int ret; | ||
169 | |||
170 | /* VRM voltage control register is set with voltage in millivolts. */ | ||
171 | cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev, | ||
172 | selector), 1000); | ||
173 | |||
174 | ret = rpmh_regulator_send_request(vreg, &cmd, wait_for_ack); | ||
175 | if (!ret) | ||
176 | vreg->voltage_selector = selector; | ||
177 | |||
178 | return ret; | ||
179 | } | ||
180 | |||
181 | static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, | ||
182 | unsigned int selector) | ||
183 | { | ||
184 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
185 | |||
186 | if (vreg->enabled == -EINVAL) { | ||
187 | /* | ||
188 | * Cache the voltage and send it later when the regulator is | ||
189 | * enabled or disabled. | ||
190 | */ | ||
191 | vreg->voltage_selector = selector; | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | return _rpmh_regulator_vrm_set_voltage_sel(rdev, selector, | ||
196 | selector > vreg->voltage_selector); | ||
197 | } | ||
198 | |||
199 | static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev) | ||
200 | { | ||
201 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
202 | |||
203 | return vreg->voltage_selector; | ||
204 | } | ||
205 | |||
206 | static int rpmh_regulator_is_enabled(struct regulator_dev *rdev) | ||
207 | { | ||
208 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
209 | |||
210 | return vreg->enabled; | ||
211 | } | ||
212 | |||
213 | static int rpmh_regulator_set_enable_state(struct regulator_dev *rdev, | ||
214 | bool enable) | ||
215 | { | ||
216 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
217 | struct tcs_cmd cmd = { | ||
218 | .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE, | ||
219 | .data = enable, | ||
220 | }; | ||
221 | int ret; | ||
222 | |||
223 | if (vreg->enabled == -EINVAL && | ||
224 | vreg->voltage_selector != -ENOTRECOVERABLE) { | ||
225 | ret = _rpmh_regulator_vrm_set_voltage_sel(rdev, | ||
226 | vreg->voltage_selector, true); | ||
227 | if (ret < 0) | ||
228 | return ret; | ||
229 | } | ||
230 | |||
231 | ret = rpmh_regulator_send_request(vreg, &cmd, enable); | ||
232 | if (!ret) | ||
233 | vreg->enabled = enable; | ||
234 | |||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | static int rpmh_regulator_enable(struct regulator_dev *rdev) | ||
239 | { | ||
240 | return rpmh_regulator_set_enable_state(rdev, true); | ||
241 | } | ||
242 | |||
243 | static int rpmh_regulator_disable(struct regulator_dev *rdev) | ||
244 | { | ||
245 | return rpmh_regulator_set_enable_state(rdev, false); | ||
246 | } | ||
247 | |||
248 | static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg, | ||
249 | unsigned int mode, bool bypassed) | ||
250 | { | ||
251 | struct tcs_cmd cmd = { | ||
252 | .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE, | ||
253 | }; | ||
254 | int pmic_mode; | ||
255 | |||
256 | if (mode > REGULATOR_MODE_STANDBY) | ||
257 | return -EINVAL; | ||
258 | |||
259 | pmic_mode = vreg->hw_data->pmic_mode_map[mode]; | ||
260 | if (pmic_mode < 0) | ||
261 | return pmic_mode; | ||
262 | |||
263 | if (bypassed) | ||
264 | cmd.data = PMIC4_BOB_MODE_PASS; | ||
265 | else | ||
266 | cmd.data = pmic_mode; | ||
267 | |||
268 | return rpmh_regulator_send_request(vreg, &cmd, true); | ||
269 | } | ||
270 | |||
271 | static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev, | ||
272 | unsigned int mode) | ||
273 | { | ||
274 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
275 | int ret; | ||
276 | |||
277 | if (mode == vreg->mode) | ||
278 | return 0; | ||
279 | |||
280 | ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed); | ||
281 | if (!ret) | ||
282 | vreg->mode = mode; | ||
283 | |||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev) | ||
288 | { | ||
289 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
290 | |||
291 | return vreg->mode; | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * rpmh_regulator_vrm_set_load() - set the regulator mode based upon the load | ||
296 | * current requested | ||
297 | * @rdev: Regulator device pointer for the rpmh-regulator | ||
298 | * @load_uA: Aggregated load current in microamps | ||
299 | * | ||
300 | * This function is used in the regulator_ops for VRM type RPMh regulator | ||
301 | * devices. | ||
302 | * | ||
303 | * Return: 0 on success, errno on failure | ||
304 | */ | ||
305 | static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA) | ||
306 | { | ||
307 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
308 | unsigned int mode; | ||
309 | |||
310 | if (load_uA >= vreg->hw_data->hpm_min_load_uA) | ||
311 | mode = REGULATOR_MODE_NORMAL; | ||
312 | else | ||
313 | mode = REGULATOR_MODE_IDLE; | ||
314 | |||
315 | return rpmh_regulator_vrm_set_mode(rdev, mode); | ||
316 | } | ||
317 | |||
318 | static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev, | ||
319 | bool enable) | ||
320 | { | ||
321 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
322 | int ret; | ||
323 | |||
324 | if (vreg->bypassed == enable) | ||
325 | return 0; | ||
326 | |||
327 | ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, enable); | ||
328 | if (!ret) | ||
329 | vreg->bypassed = enable; | ||
330 | |||
331 | return ret; | ||
332 | } | ||
333 | |||
334 | static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev, | ||
335 | bool *enable) | ||
336 | { | ||
337 | struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); | ||
338 | |||
339 | *enable = vreg->bypassed; | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static const struct regulator_ops rpmh_regulator_vrm_ops = { | ||
345 | .enable = rpmh_regulator_enable, | ||
346 | .disable = rpmh_regulator_disable, | ||
347 | .is_enabled = rpmh_regulator_is_enabled, | ||
348 | .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, | ||
349 | .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, | ||
350 | .list_voltage = regulator_list_voltage_linear_range, | ||
351 | .set_mode = rpmh_regulator_vrm_set_mode, | ||
352 | .get_mode = rpmh_regulator_vrm_get_mode, | ||
353 | }; | ||
354 | |||
355 | static const struct regulator_ops rpmh_regulator_vrm_drms_ops = { | ||
356 | .enable = rpmh_regulator_enable, | ||
357 | .disable = rpmh_regulator_disable, | ||
358 | .is_enabled = rpmh_regulator_is_enabled, | ||
359 | .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, | ||
360 | .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, | ||
361 | .list_voltage = regulator_list_voltage_linear_range, | ||
362 | .set_mode = rpmh_regulator_vrm_set_mode, | ||
363 | .get_mode = rpmh_regulator_vrm_get_mode, | ||
364 | .set_load = rpmh_regulator_vrm_set_load, | ||
365 | }; | ||
366 | |||
367 | static const struct regulator_ops rpmh_regulator_vrm_bypass_ops = { | ||
368 | .enable = rpmh_regulator_enable, | ||
369 | .disable = rpmh_regulator_disable, | ||
370 | .is_enabled = rpmh_regulator_is_enabled, | ||
371 | .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, | ||
372 | .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, | ||
373 | .list_voltage = regulator_list_voltage_linear_range, | ||
374 | .set_mode = rpmh_regulator_vrm_set_mode, | ||
375 | .get_mode = rpmh_regulator_vrm_get_mode, | ||
376 | .set_bypass = rpmh_regulator_vrm_set_bypass, | ||
377 | .get_bypass = rpmh_regulator_vrm_get_bypass, | ||
378 | }; | ||
379 | |||
380 | static const struct regulator_ops rpmh_regulator_xob_ops = { | ||
381 | .enable = rpmh_regulator_enable, | ||
382 | .disable = rpmh_regulator_disable, | ||
383 | .is_enabled = rpmh_regulator_is_enabled, | ||
384 | }; | ||
385 | |||
386 | /** | ||
387 | * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator | ||
388 | * vreg: Pointer to the individual rpmh-regulator resource | ||
389 | * dev: Pointer to the top level rpmh-regulator PMIC device | ||
390 | * node: Pointer to the individual rpmh-regulator resource | ||
391 | * device node | ||
392 | * pmic_id: String used to identify the top level rpmh-regulator | ||
393 | * PMIC device on the board | ||
394 | * pmic_rpmh_data: Pointer to a null-terminated array of rpmh-regulator | ||
395 | * resources defined for the top level PMIC device | ||
396 | * | ||
397 | * Return: 0 on success, errno on failure | ||
398 | */ | ||
399 | static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev, | ||
400 | struct device_node *node, const char *pmic_id, | ||
401 | const struct rpmh_vreg_init_data *pmic_rpmh_data) | ||
402 | { | ||
403 | struct regulator_config reg_config = {}; | ||
404 | char rpmh_resource_name[20] = ""; | ||
405 | const struct rpmh_vreg_init_data *rpmh_data; | ||
406 | struct regulator_init_data *init_data; | ||
407 | struct regulator_dev *rdev; | ||
408 | int ret; | ||
409 | |||
410 | vreg->dev = dev; | ||
411 | |||
412 | for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++) | ||
413 | if (!strcmp(rpmh_data->name, node->name)) | ||
414 | break; | ||
415 | |||
416 | if (!rpmh_data->name) { | ||
417 | dev_err(dev, "Unknown regulator %s\n", node->name); | ||
418 | return -EINVAL; | ||
419 | } | ||
420 | |||
421 | scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name), | ||
422 | rpmh_data->resource_name, pmic_id); | ||
423 | |||
424 | vreg->addr = cmd_db_read_addr(rpmh_resource_name); | ||
425 | if (!vreg->addr) { | ||
426 | dev_err(dev, "%s: could not find RPMh address for resource %s\n", | ||
427 | node->name, rpmh_resource_name); | ||
428 | return -ENODEV; | ||
429 | } | ||
430 | |||
431 | vreg->rdesc.name = rpmh_data->name; | ||
432 | vreg->rdesc.supply_name = rpmh_data->supply_name; | ||
433 | vreg->hw_data = rpmh_data->hw_data; | ||
434 | |||
435 | vreg->enabled = -EINVAL; | ||
436 | vreg->voltage_selector = -ENOTRECOVERABLE; | ||
437 | vreg->mode = REGULATOR_MODE_INVALID; | ||
438 | |||
439 | if (rpmh_data->hw_data->n_voltages) { | ||
440 | vreg->rdesc.linear_ranges = &rpmh_data->hw_data->voltage_range; | ||
441 | vreg->rdesc.n_linear_ranges = 1; | ||
442 | vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages; | ||
443 | } | ||
444 | |||
445 | vreg->always_wait_for_ack = of_property_read_bool(node, | ||
446 | "qcom,always-wait-for-ack"); | ||
447 | |||
448 | vreg->rdesc.owner = THIS_MODULE; | ||
449 | vreg->rdesc.type = REGULATOR_VOLTAGE; | ||
450 | vreg->rdesc.ops = vreg->hw_data->ops; | ||
451 | vreg->rdesc.of_map_mode = vreg->hw_data->of_map_mode; | ||
452 | |||
453 | init_data = of_get_regulator_init_data(dev, node, &vreg->rdesc); | ||
454 | if (!init_data) | ||
455 | return -ENOMEM; | ||
456 | |||
457 | if (rpmh_data->hw_data->regulator_type == XOB && | ||
458 | init_data->constraints.min_uV && | ||
459 | init_data->constraints.min_uV == init_data->constraints.max_uV) { | ||
460 | vreg->rdesc.fixed_uV = init_data->constraints.min_uV; | ||
461 | vreg->rdesc.n_voltages = 1; | ||
462 | } | ||
463 | |||
464 | reg_config.dev = dev; | ||
465 | reg_config.init_data = init_data; | ||
466 | reg_config.of_node = node; | ||
467 | reg_config.driver_data = vreg; | ||
468 | |||
469 | rdev = devm_regulator_register(dev, &vreg->rdesc, ®_config); | ||
470 | if (IS_ERR(rdev)) { | ||
471 | ret = PTR_ERR(rdev); | ||
472 | dev_err(dev, "%s: devm_regulator_register() failed, ret=%d\n", | ||
473 | node->name, ret); | ||
474 | return ret; | ||
475 | } | ||
476 | |||
477 | dev_dbg(dev, "%s regulator registered for RPMh resource %s @ 0x%05X\n", | ||
478 | node->name, rpmh_resource_name, vreg->addr); | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = { | ||
484 | [REGULATOR_MODE_INVALID] = -EINVAL, | ||
485 | [REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION, | ||
486 | [REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM, | ||
487 | [REGULATOR_MODE_NORMAL] = PMIC4_LDO_MODE_HPM, | ||
488 | [REGULATOR_MODE_FAST] = -EINVAL, | ||
489 | }; | ||
490 | |||
491 | static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode) | ||
492 | { | ||
493 | unsigned int mode; | ||
494 | |||
495 | switch (rpmh_mode) { | ||
496 | case RPMH_REGULATOR_MODE_HPM: | ||
497 | mode = REGULATOR_MODE_NORMAL; | ||
498 | break; | ||
499 | case RPMH_REGULATOR_MODE_LPM: | ||
500 | mode = REGULATOR_MODE_IDLE; | ||
501 | break; | ||
502 | case RPMH_REGULATOR_MODE_RET: | ||
503 | mode = REGULATOR_MODE_STANDBY; | ||
504 | break; | ||
505 | default: | ||
506 | mode = REGULATOR_MODE_INVALID; | ||
507 | } | ||
508 | |||
509 | return mode; | ||
510 | } | ||
511 | |||
512 | static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = { | ||
513 | [REGULATOR_MODE_INVALID] = -EINVAL, | ||
514 | [REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION, | ||
515 | [REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM, | ||
516 | [REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO, | ||
517 | [REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM, | ||
518 | }; | ||
519 | |||
520 | static unsigned int | ||
521 | rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode) | ||
522 | { | ||
523 | unsigned int mode; | ||
524 | |||
525 | switch (rpmh_mode) { | ||
526 | case RPMH_REGULATOR_MODE_HPM: | ||
527 | mode = REGULATOR_MODE_FAST; | ||
528 | break; | ||
529 | case RPMH_REGULATOR_MODE_AUTO: | ||
530 | mode = REGULATOR_MODE_NORMAL; | ||
531 | break; | ||
532 | case RPMH_REGULATOR_MODE_LPM: | ||
533 | mode = REGULATOR_MODE_IDLE; | ||
534 | break; | ||
535 | case RPMH_REGULATOR_MODE_RET: | ||
536 | mode = REGULATOR_MODE_STANDBY; | ||
537 | break; | ||
538 | default: | ||
539 | mode = REGULATOR_MODE_INVALID; | ||
540 | } | ||
541 | |||
542 | return mode; | ||
543 | } | ||
544 | |||
545 | static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = { | ||
546 | [REGULATOR_MODE_INVALID] = -EINVAL, | ||
547 | [REGULATOR_MODE_STANDBY] = -EINVAL, | ||
548 | [REGULATOR_MODE_IDLE] = PMIC4_BOB_MODE_PFM, | ||
549 | [REGULATOR_MODE_NORMAL] = PMIC4_BOB_MODE_AUTO, | ||
550 | [REGULATOR_MODE_FAST] = PMIC4_BOB_MODE_PWM, | ||
551 | }; | ||
552 | |||
553 | static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode) | ||
554 | { | ||
555 | unsigned int mode; | ||
556 | |||
557 | switch (rpmh_mode) { | ||
558 | case RPMH_REGULATOR_MODE_HPM: | ||
559 | mode = REGULATOR_MODE_FAST; | ||
560 | break; | ||
561 | case RPMH_REGULATOR_MODE_AUTO: | ||
562 | mode = REGULATOR_MODE_NORMAL; | ||
563 | break; | ||
564 | case RPMH_REGULATOR_MODE_LPM: | ||
565 | mode = REGULATOR_MODE_IDLE; | ||
566 | break; | ||
567 | default: | ||
568 | mode = REGULATOR_MODE_INVALID; | ||
569 | } | ||
570 | |||
571 | return mode; | ||
572 | } | ||
573 | |||
574 | static const struct rpmh_vreg_hw_data pmic4_pldo = { | ||
575 | .regulator_type = VRM, | ||
576 | .ops = &rpmh_regulator_vrm_drms_ops, | ||
577 | .voltage_range = REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000), | ||
578 | .n_voltages = 256, | ||
579 | .hpm_min_load_uA = 10000, | ||
580 | .pmic_mode_map = pmic_mode_map_pmic4_ldo, | ||
581 | .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, | ||
582 | }; | ||
583 | |||
584 | static const struct rpmh_vreg_hw_data pmic4_pldo_lv = { | ||
585 | .regulator_type = VRM, | ||
586 | .ops = &rpmh_regulator_vrm_drms_ops, | ||
587 | .voltage_range = REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000), | ||
588 | .n_voltages = 128, | ||
589 | .hpm_min_load_uA = 10000, | ||
590 | .pmic_mode_map = pmic_mode_map_pmic4_ldo, | ||
591 | .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, | ||
592 | }; | ||
593 | |||
594 | static const struct rpmh_vreg_hw_data pmic4_nldo = { | ||
595 | .regulator_type = VRM, | ||
596 | .ops = &rpmh_regulator_vrm_drms_ops, | ||
597 | .voltage_range = REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000), | ||
598 | .n_voltages = 128, | ||
599 | .hpm_min_load_uA = 30000, | ||
600 | .pmic_mode_map = pmic_mode_map_pmic4_ldo, | ||
601 | .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, | ||
602 | }; | ||
603 | |||
604 | static const struct rpmh_vreg_hw_data pmic4_hfsmps3 = { | ||
605 | .regulator_type = VRM, | ||
606 | .ops = &rpmh_regulator_vrm_ops, | ||
607 | .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), | ||
608 | .n_voltages = 216, | ||
609 | .pmic_mode_map = pmic_mode_map_pmic4_smps, | ||
610 | .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, | ||
611 | }; | ||
612 | |||
613 | static const struct rpmh_vreg_hw_data pmic4_ftsmps426 = { | ||
614 | .regulator_type = VRM, | ||
615 | .ops = &rpmh_regulator_vrm_ops, | ||
616 | .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000), | ||
617 | .n_voltages = 259, | ||
618 | .pmic_mode_map = pmic_mode_map_pmic4_smps, | ||
619 | .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, | ||
620 | }; | ||
621 | |||
622 | static const struct rpmh_vreg_hw_data pmic4_bob = { | ||
623 | .regulator_type = VRM, | ||
624 | .ops = &rpmh_regulator_vrm_bypass_ops, | ||
625 | .voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000), | ||
626 | .n_voltages = 84, | ||
627 | .pmic_mode_map = pmic_mode_map_pmic4_bob, | ||
628 | .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode, | ||
629 | }; | ||
630 | |||
631 | static const struct rpmh_vreg_hw_data pmic4_lvs = { | ||
632 | .regulator_type = XOB, | ||
633 | .ops = &rpmh_regulator_xob_ops, | ||
634 | /* LVS hardware does not support voltage or mode configuration. */ | ||
635 | }; | ||
636 | |||
637 | #define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ | ||
638 | { \ | ||
639 | .name = _name, \ | ||
640 | .resource_name = _resource_name, \ | ||
641 | .hw_data = _hw_data, \ | ||
642 | .supply_name = _supply_name, \ | ||
643 | } | ||
644 | |||
645 | static const struct rpmh_vreg_init_data pm8998_vreg_data[] = { | ||
646 | RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), | ||
647 | RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), | ||
648 | RPMH_VREG("smps3", "smp%s3", &pmic4_hfsmps3, "vdd-s3"), | ||
649 | RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"), | ||
650 | RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"), | ||
651 | RPMH_VREG("smps6", "smp%s6", &pmic4_ftsmps426, "vdd-s6"), | ||
652 | RPMH_VREG("smps7", "smp%s7", &pmic4_ftsmps426, "vdd-s7"), | ||
653 | RPMH_VREG("smps8", "smp%s8", &pmic4_ftsmps426, "vdd-s8"), | ||
654 | RPMH_VREG("smps9", "smp%s9", &pmic4_ftsmps426, "vdd-s9"), | ||
655 | RPMH_VREG("smps10", "smp%s10", &pmic4_ftsmps426, "vdd-s10"), | ||
656 | RPMH_VREG("smps11", "smp%s11", &pmic4_ftsmps426, "vdd-s11"), | ||
657 | RPMH_VREG("smps12", "smp%s12", &pmic4_ftsmps426, "vdd-s12"), | ||
658 | RPMH_VREG("smps13", "smp%s13", &pmic4_ftsmps426, "vdd-s13"), | ||
659 | RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l27"), | ||
660 | RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l8-l17"), | ||
661 | RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l3-l11"), | ||
662 | RPMH_VREG("ldo4", "ldo%s4", &pmic4_nldo, "vdd-l4-l5"), | ||
663 | RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l4-l5"), | ||
664 | RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l6"), | ||
665 | RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), | ||
666 | RPMH_VREG("ldo8", "ldo%s8", &pmic4_nldo, "vdd-l2-l8-l17"), | ||
667 | RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo, "vdd-l9"), | ||
668 | RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo, "vdd-l10-l23-l25"), | ||
669 | RPMH_VREG("ldo11", "ldo%s11", &pmic4_nldo, "vdd-l3-l11"), | ||
670 | RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), | ||
671 | RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo, "vdd-l13-l19-l21"), | ||
672 | RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), | ||
673 | RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), | ||
674 | RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l16-l28"), | ||
675 | RPMH_VREG("ldo17", "ldo%s17", &pmic4_nldo, "vdd-l2-l8-l17"), | ||
676 | RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l18-l22"), | ||
677 | RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l13-l19-l21"), | ||
678 | RPMH_VREG("ldo20", "ldo%s20", &pmic4_pldo, "vdd-l20-l24"), | ||
679 | RPMH_VREG("ldo21", "ldo%s21", &pmic4_pldo, "vdd-l13-l19-l21"), | ||
680 | RPMH_VREG("ldo22", "ldo%s22", &pmic4_pldo, "vdd-l18-l22"), | ||
681 | RPMH_VREG("ldo23", "ldo%s23", &pmic4_pldo, "vdd-l10-l23-l25"), | ||
682 | RPMH_VREG("ldo24", "ldo%s24", &pmic4_pldo, "vdd-l20-l24"), | ||
683 | RPMH_VREG("ldo25", "ldo%s25", &pmic4_pldo, "vdd-l10-l23-l25"), | ||
684 | RPMH_VREG("ldo26", "ldo%s26", &pmic4_nldo, "vdd-l26"), | ||
685 | RPMH_VREG("ldo27", "ldo%s27", &pmic4_nldo, "vdd-l1-l27"), | ||
686 | RPMH_VREG("ldo28", "ldo%s28", &pmic4_pldo, "vdd-l16-l28"), | ||
687 | RPMH_VREG("lvs1", "vs%s1", &pmic4_lvs, "vin-lvs-1-2"), | ||
688 | RPMH_VREG("lvs2", "vs%s2", &pmic4_lvs, "vin-lvs-1-2"), | ||
689 | {}, | ||
690 | }; | ||
691 | |||
692 | static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = { | ||
693 | RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"), | ||
694 | {}, | ||
695 | }; | ||
696 | |||
697 | static const struct rpmh_vreg_init_data pm8005_vreg_data[] = { | ||
698 | RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), | ||
699 | RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), | ||
700 | RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"), | ||
701 | RPMH_VREG("smps4", "smp%s4", &pmic4_ftsmps426, "vdd-s4"), | ||
702 | {}, | ||
703 | }; | ||
704 | |||
705 | static int rpmh_regulator_probe(struct platform_device *pdev) | ||
706 | { | ||
707 | struct device *dev = &pdev->dev; | ||
708 | const struct rpmh_vreg_init_data *vreg_data; | ||
709 | struct device_node *node; | ||
710 | struct rpmh_vreg *vreg; | ||
711 | const char *pmic_id; | ||
712 | int ret; | ||
713 | |||
714 | vreg_data = of_device_get_match_data(dev); | ||
715 | if (!vreg_data) | ||
716 | return -ENODEV; | ||
717 | |||
718 | ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id); | ||
719 | if (ret < 0) { | ||
720 | dev_err(dev, "qcom,pmic-id missing in DT node\n"); | ||
721 | return ret; | ||
722 | } | ||
723 | |||
724 | for_each_available_child_of_node(dev->of_node, node) { | ||
725 | vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); | ||
726 | if (!vreg) { | ||
727 | of_node_put(node); | ||
728 | return -ENOMEM; | ||
729 | } | ||
730 | |||
731 | ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id, | ||
732 | vreg_data); | ||
733 | if (ret < 0) { | ||
734 | of_node_put(node); | ||
735 | return ret; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | static const struct of_device_id rpmh_regulator_match_table[] = { | ||
743 | { | ||
744 | .compatible = "qcom,pm8998-rpmh-regulators", | ||
745 | .data = pm8998_vreg_data, | ||
746 | }, | ||
747 | { | ||
748 | .compatible = "qcom,pmi8998-rpmh-regulators", | ||
749 | .data = pmi8998_vreg_data, | ||
750 | }, | ||
751 | { | ||
752 | .compatible = "qcom,pm8005-rpmh-regulators", | ||
753 | .data = pm8005_vreg_data, | ||
754 | }, | ||
755 | {} | ||
756 | }; | ||
757 | MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); | ||
758 | |||
759 | static struct platform_driver rpmh_regulator_driver = { | ||
760 | .driver = { | ||
761 | .name = "qcom-rpmh-regulator", | ||
762 | .of_match_table = of_match_ptr(rpmh_regulator_match_table), | ||
763 | }, | ||
764 | .probe = rpmh_regulator_probe, | ||
765 | }; | ||
766 | module_platform_driver(rpmh_regulator_driver); | ||
767 | |||
768 | MODULE_DESCRIPTION("Qualcomm RPMh regulator driver"); | ||
769 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 9817f1a75342..53a61fb65642 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c | |||
@@ -1060,7 +1060,7 @@ static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data) | |||
1060 | #define SAW3_AVS_CTL_TGGL_MASK 0x8000000 | 1060 | #define SAW3_AVS_CTL_TGGL_MASK 0x8000000 |
1061 | #define SAW3_AVS_CTL_CLEAR_MASK 0x7efc00 | 1061 | #define SAW3_AVS_CTL_CLEAR_MASK 0x7efc00 |
1062 | 1062 | ||
1063 | static struct regmap *saw_regmap = NULL; | 1063 | static struct regmap *saw_regmap; |
1064 | 1064 | ||
1065 | static void spmi_saw_set_vdd(void *data) | 1065 | static void spmi_saw_set_vdd(void *data) |
1066 | { | 1066 | { |
@@ -1728,7 +1728,7 @@ static const struct spmi_regulator_data pmi8994_regulators[] = { | |||
1728 | { "s2", 0x1700, "vdd_s2", }, | 1728 | { "s2", 0x1700, "vdd_s2", }, |
1729 | { "s3", 0x1a00, "vdd_s3", }, | 1729 | { "s3", 0x1a00, "vdd_s3", }, |
1730 | { "l1", 0x4000, "vdd_l1", }, | 1730 | { "l1", 0x4000, "vdd_l1", }, |
1731 | { } | 1731 | { } |
1732 | }; | 1732 | }; |
1733 | 1733 | ||
1734 | static const struct of_device_id qcom_spmi_regulator_match[] = { | 1734 | static const struct of_device_id qcom_spmi_regulator_match[] = { |
@@ -1752,7 +1752,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) | |||
1752 | const char *name; | 1752 | const char *name; |
1753 | struct device *dev = &pdev->dev; | 1753 | struct device *dev = &pdev->dev; |
1754 | struct device_node *node = pdev->dev.of_node; | 1754 | struct device_node *node = pdev->dev.of_node; |
1755 | struct device_node *syscon; | 1755 | struct device_node *syscon, *reg_node; |
1756 | struct property *reg_prop; | ||
1756 | int ret, lenp; | 1757 | int ret, lenp; |
1757 | struct list_head *vreg_list; | 1758 | struct list_head *vreg_list; |
1758 | 1759 | ||
@@ -1774,16 +1775,19 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) | |||
1774 | syscon = of_parse_phandle(node, "qcom,saw-reg", 0); | 1775 | syscon = of_parse_phandle(node, "qcom,saw-reg", 0); |
1775 | saw_regmap = syscon_node_to_regmap(syscon); | 1776 | saw_regmap = syscon_node_to_regmap(syscon); |
1776 | of_node_put(syscon); | 1777 | of_node_put(syscon); |
1777 | if (IS_ERR(regmap)) | 1778 | if (IS_ERR(saw_regmap)) |
1778 | dev_err(dev, "ERROR reading SAW regmap\n"); | 1779 | dev_err(dev, "ERROR reading SAW regmap\n"); |
1779 | } | 1780 | } |
1780 | 1781 | ||
1781 | for (reg = match->data; reg->name; reg++) { | 1782 | for (reg = match->data; reg->name; reg++) { |
1782 | 1783 | ||
1783 | if (saw_regmap && \ | 1784 | if (saw_regmap) { |
1784 | of_find_property(of_find_node_by_name(node, reg->name), \ | 1785 | reg_node = of_get_child_by_name(node, reg->name); |
1785 | "qcom,saw-slave", &lenp)) { | 1786 | reg_prop = of_find_property(reg_node, "qcom,saw-slave", |
1786 | continue; | 1787 | &lenp); |
1788 | of_node_put(reg_node); | ||
1789 | if (reg_prop) | ||
1790 | continue; | ||
1787 | } | 1791 | } |
1788 | 1792 | ||
1789 | vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); | 1793 | vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); |
@@ -1816,13 +1820,17 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) | |||
1816 | if (ret) | 1820 | if (ret) |
1817 | continue; | 1821 | continue; |
1818 | 1822 | ||
1819 | if (saw_regmap && \ | 1823 | if (saw_regmap) { |
1820 | of_find_property(of_find_node_by_name(node, reg->name), \ | 1824 | reg_node = of_get_child_by_name(node, reg->name); |
1821 | "qcom,saw-leader", &lenp)) { | 1825 | reg_prop = of_find_property(reg_node, "qcom,saw-leader", |
1822 | spmi_saw_ops = *(vreg->desc.ops); | 1826 | &lenp); |
1823 | spmi_saw_ops.set_voltage_sel = \ | 1827 | of_node_put(reg_node); |
1824 | spmi_regulator_saw_set_voltage; | 1828 | if (reg_prop) { |
1825 | vreg->desc.ops = &spmi_saw_ops; | 1829 | spmi_saw_ops = *(vreg->desc.ops); |
1830 | spmi_saw_ops.set_voltage_sel = | ||
1831 | spmi_regulator_saw_set_voltage; | ||
1832 | vreg->desc.ops = &spmi_saw_ops; | ||
1833 | } | ||
1826 | } | 1834 | } |
1827 | 1835 | ||
1828 | config.dev = dev; | 1836 | config.dev = dev; |
diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index 48f0ca90743c..095d25f3d2ea 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c | |||
@@ -1,13 +1,7 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * Copyright (c) 2013 Samsung Electronics Co., Ltd | 2 | // |
3 | * http://www.samsung.com | 3 | // Copyright (c) 2013 Samsung Electronics Co., Ltd |
4 | * | 4 | // http://www.samsung.com |
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | */ | ||
11 | 5 | ||
12 | #include <linux/bug.h> | 6 | #include <linux/bug.h> |
13 | #include <linux/err.h> | 7 | #include <linux/err.h> |
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index d1207ec683db..5bb6f4ca48db 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c | |||
@@ -1,20 +1,7 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * s2mps11.c | 2 | // |
3 | * | 3 | // Copyright (c) 2012-2014 Samsung Electronics Co., Ltd |
4 | * Copyright (c) 2012-2014 Samsung Electronics Co., Ltd | 4 | // http://www.samsung.com |
5 | * http://www.samsung.com | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | */ | ||
18 | 5 | ||
19 | #include <linux/bug.h> | 6 | #include <linux/bug.h> |
20 | #include <linux/err.h> | 7 | #include <linux/err.h> |
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 0cbc980753c2..667d16dc83ce 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c | |||
@@ -1,15 +1,7 @@ | |||
1 | /* | 1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | * s5m8767.c | 2 | // |
3 | * | 3 | // Copyright (c) 2011 Samsung Electronics Co., Ltd |
4 | * Copyright (c) 2011 Samsung Electronics Co., Ltd | 4 | // http://www.samsung.com |
5 | * http://www.samsung.com | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | 5 | ||
14 | #include <linux/err.h> | 6 | #include <linux/err.h> |
15 | #include <linux/of_gpio.h> | 7 | #include <linux/of_gpio.h> |
diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index fc12badf3805..d84fab616abf 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c | |||
@@ -232,6 +232,8 @@ static int tps65217_regulator_probe(struct platform_device *pdev) | |||
232 | tps->strobes = devm_kcalloc(&pdev->dev, | 232 | tps->strobes = devm_kcalloc(&pdev->dev, |
233 | TPS65217_NUM_REGULATOR, sizeof(u8), | 233 | TPS65217_NUM_REGULATOR, sizeof(u8), |
234 | GFP_KERNEL); | 234 | GFP_KERNEL); |
235 | if (!tps->strobes) | ||
236 | return -ENOMEM; | ||
235 | 237 | ||
236 | platform_set_drvdata(pdev, tps); | 238 | platform_set_drvdata(pdev, tps); |
237 | 239 | ||
diff --git a/drivers/regulator/uniphier-regulator.c b/drivers/regulator/uniphier-regulator.c new file mode 100644 index 000000000000..abf22acbd13e --- /dev/null +++ b/drivers/regulator/uniphier-regulator.c | |||
@@ -0,0 +1,213 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // | ||
3 | // Regulator controller driver for UniPhier SoC | ||
4 | // Copyright 2018 Socionext Inc. | ||
5 | // Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> | ||
6 | |||
7 | #include <linux/clk.h> | ||
8 | #include <linux/io.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/of_device.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/regmap.h> | ||
13 | #include <linux/regulator/driver.h> | ||
14 | #include <linux/regulator/of_regulator.h> | ||
15 | #include <linux/reset.h> | ||
16 | |||
17 | #define MAX_CLKS 2 | ||
18 | #define MAX_RSTS 2 | ||
19 | |||
20 | struct uniphier_regulator_soc_data { | ||
21 | int nclks; | ||
22 | const char * const *clock_names; | ||
23 | int nrsts; | ||
24 | const char * const *reset_names; | ||
25 | const struct regulator_desc *desc; | ||
26 | const struct regmap_config *regconf; | ||
27 | }; | ||
28 | |||
29 | struct uniphier_regulator_priv { | ||
30 | struct clk_bulk_data clk[MAX_CLKS]; | ||
31 | struct reset_control *rst[MAX_RSTS]; | ||
32 | const struct uniphier_regulator_soc_data *data; | ||
33 | }; | ||
34 | |||
35 | static struct regulator_ops uniphier_regulator_ops = { | ||
36 | .enable = regulator_enable_regmap, | ||
37 | .disable = regulator_disable_regmap, | ||
38 | .is_enabled = regulator_is_enabled_regmap, | ||
39 | }; | ||
40 | |||
41 | static int uniphier_regulator_probe(struct platform_device *pdev) | ||
42 | { | ||
43 | struct device *dev = &pdev->dev; | ||
44 | struct uniphier_regulator_priv *priv; | ||
45 | struct regulator_config config = { }; | ||
46 | struct regulator_dev *rdev; | ||
47 | struct regmap *regmap; | ||
48 | struct resource *res; | ||
49 | void __iomem *base; | ||
50 | const char *name; | ||
51 | int i, ret, nr; | ||
52 | |||
53 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
54 | if (!priv) | ||
55 | return -ENOMEM; | ||
56 | |||
57 | priv->data = of_device_get_match_data(dev); | ||
58 | if (WARN_ON(!priv->data)) | ||
59 | return -EINVAL; | ||
60 | |||
61 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
62 | base = devm_ioremap_resource(dev, res); | ||
63 | if (IS_ERR(base)) | ||
64 | return PTR_ERR(base); | ||
65 | |||
66 | for (i = 0; i < priv->data->nclks; i++) | ||
67 | priv->clk[i].id = priv->data->clock_names[i]; | ||
68 | ret = devm_clk_bulk_get(dev, priv->data->nclks, priv->clk); | ||
69 | if (ret) | ||
70 | return ret; | ||
71 | |||
72 | for (i = 0; i < priv->data->nrsts; i++) { | ||
73 | name = priv->data->reset_names[i]; | ||
74 | priv->rst[i] = devm_reset_control_get_shared(dev, name); | ||
75 | if (IS_ERR(priv->rst[i])) | ||
76 | return PTR_ERR(priv->rst[i]); | ||
77 | } | ||
78 | |||
79 | ret = clk_bulk_prepare_enable(priv->data->nclks, priv->clk); | ||
80 | if (ret) | ||
81 | return ret; | ||
82 | |||
83 | for (nr = 0; nr < priv->data->nrsts; nr++) { | ||
84 | ret = reset_control_deassert(priv->rst[nr]); | ||
85 | if (ret) | ||
86 | goto out_rst_assert; | ||
87 | } | ||
88 | |||
89 | regmap = devm_regmap_init_mmio(dev, base, priv->data->regconf); | ||
90 | if (IS_ERR(regmap)) | ||
91 | return PTR_ERR(regmap); | ||
92 | |||
93 | config.dev = dev; | ||
94 | config.driver_data = priv; | ||
95 | config.of_node = dev->of_node; | ||
96 | config.regmap = regmap; | ||
97 | config.init_data = of_get_regulator_init_data(dev, dev->of_node, | ||
98 | priv->data->desc); | ||
99 | rdev = devm_regulator_register(dev, priv->data->desc, &config); | ||
100 | if (IS_ERR(rdev)) { | ||
101 | ret = PTR_ERR(rdev); | ||
102 | goto out_rst_assert; | ||
103 | } | ||
104 | |||
105 | platform_set_drvdata(pdev, priv); | ||
106 | |||
107 | return 0; | ||
108 | |||
109 | out_rst_assert: | ||
110 | while (nr--) | ||
111 | reset_control_assert(priv->rst[nr]); | ||
112 | |||
113 | clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); | ||
114 | |||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | static int uniphier_regulator_remove(struct platform_device *pdev) | ||
119 | { | ||
120 | struct uniphier_regulator_priv *priv = platform_get_drvdata(pdev); | ||
121 | int i; | ||
122 | |||
123 | for (i = 0; i < priv->data->nrsts; i++) | ||
124 | reset_control_assert(priv->rst[i]); | ||
125 | |||
126 | clk_bulk_disable_unprepare(priv->data->nclks, priv->clk); | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /* USB3 controller data */ | ||
132 | #define USB3VBUS_OFFSET 0x0 | ||
133 | #define USB3VBUS_REG BIT(4) | ||
134 | #define USB3VBUS_REG_EN BIT(3) | ||
135 | static const struct regulator_desc uniphier_usb3_regulator_desc = { | ||
136 | .name = "vbus", | ||
137 | .of_match = of_match_ptr("vbus"), | ||
138 | .ops = &uniphier_regulator_ops, | ||
139 | .type = REGULATOR_VOLTAGE, | ||
140 | .owner = THIS_MODULE, | ||
141 | .enable_reg = USB3VBUS_OFFSET, | ||
142 | .enable_mask = USB3VBUS_REG_EN | USB3VBUS_REG, | ||
143 | .enable_val = USB3VBUS_REG_EN | USB3VBUS_REG, | ||
144 | .disable_val = USB3VBUS_REG_EN, | ||
145 | }; | ||
146 | |||
147 | static const struct regmap_config uniphier_usb3_regulator_regconf = { | ||
148 | .reg_bits = 32, | ||
149 | .val_bits = 32, | ||
150 | .reg_stride = 4, | ||
151 | .max_register = 1, | ||
152 | }; | ||
153 | |||
154 | static const char * const uniphier_pro4_clock_reset_names[] = { | ||
155 | "gio", "link", | ||
156 | }; | ||
157 | |||
158 | static const struct uniphier_regulator_soc_data uniphier_pro4_usb3_data = { | ||
159 | .nclks = ARRAY_SIZE(uniphier_pro4_clock_reset_names), | ||
160 | .clock_names = uniphier_pro4_clock_reset_names, | ||
161 | .nrsts = ARRAY_SIZE(uniphier_pro4_clock_reset_names), | ||
162 | .reset_names = uniphier_pro4_clock_reset_names, | ||
163 | .desc = &uniphier_usb3_regulator_desc, | ||
164 | .regconf = &uniphier_usb3_regulator_regconf, | ||
165 | }; | ||
166 | |||
167 | static const char * const uniphier_pxs2_clock_reset_names[] = { | ||
168 | "link", | ||
169 | }; | ||
170 | |||
171 | static const struct uniphier_regulator_soc_data uniphier_pxs2_usb3_data = { | ||
172 | .nclks = ARRAY_SIZE(uniphier_pxs2_clock_reset_names), | ||
173 | .clock_names = uniphier_pxs2_clock_reset_names, | ||
174 | .nrsts = ARRAY_SIZE(uniphier_pxs2_clock_reset_names), | ||
175 | .reset_names = uniphier_pxs2_clock_reset_names, | ||
176 | .desc = &uniphier_usb3_regulator_desc, | ||
177 | .regconf = &uniphier_usb3_regulator_regconf, | ||
178 | }; | ||
179 | |||
180 | static const struct of_device_id uniphier_regulator_match[] = { | ||
181 | /* USB VBUS */ | ||
182 | { | ||
183 | .compatible = "socionext,uniphier-pro4-usb3-regulator", | ||
184 | .data = &uniphier_pro4_usb3_data, | ||
185 | }, | ||
186 | { | ||
187 | .compatible = "socionext,uniphier-pxs2-usb3-regulator", | ||
188 | .data = &uniphier_pxs2_usb3_data, | ||
189 | }, | ||
190 | { | ||
191 | .compatible = "socionext,uniphier-ld20-usb3-regulator", | ||
192 | .data = &uniphier_pxs2_usb3_data, | ||
193 | }, | ||
194 | { | ||
195 | .compatible = "socionext,uniphier-pxs3-usb3-regulator", | ||
196 | .data = &uniphier_pxs2_usb3_data, | ||
197 | }, | ||
198 | { /* Sentinel */ }, | ||
199 | }; | ||
200 | |||
201 | static struct platform_driver uniphier_regulator_driver = { | ||
202 | .probe = uniphier_regulator_probe, | ||
203 | .remove = uniphier_regulator_remove, | ||
204 | .driver = { | ||
205 | .name = "uniphier-regulator", | ||
206 | .of_match_table = uniphier_regulator_match, | ||
207 | }, | ||
208 | }; | ||
209 | module_platform_driver(uniphier_regulator_driver); | ||
210 | |||
211 | MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); | ||
212 | MODULE_DESCRIPTION("UniPhier Regulator Controller Driver"); | ||
213 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 5856e792d09c..ba79b609aca2 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig | |||
@@ -40,6 +40,23 @@ config QCOM_GSBI | |||
40 | functions for connecting the underlying serial UART, SPI, and I2C | 40 | functions for connecting the underlying serial UART, SPI, and I2C |
41 | devices to the output pins. | 41 | devices to the output pins. |
42 | 42 | ||
43 | config QCOM_LLCC | ||
44 | tristate "Qualcomm Technologies, Inc. LLCC driver" | ||
45 | depends on ARCH_QCOM | ||
46 | help | ||
47 | Qualcomm Technologies, Inc. platform specific | ||
48 | Last Level Cache Controller(LLCC) driver. This provides interfaces | ||
49 | to clients that use the LLCC. Say yes here to enable LLCC slice | ||
50 | driver. | ||
51 | |||
52 | config QCOM_SDM845_LLCC | ||
53 | tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver" | ||
54 | depends on QCOM_LLCC | ||
55 | help | ||
56 | Say yes here to enable the LLCC driver for SDM845. This provides | ||
57 | data required to configure LLCC so that clients can start using the | ||
58 | LLCC slices. | ||
59 | |||
43 | config QCOM_MDT_LOADER | 60 | config QCOM_MDT_LOADER |
44 | tristate | 61 | tristate |
45 | select QCOM_SCM | 62 | select QCOM_SCM |
@@ -75,6 +92,16 @@ config QCOM_RMTFS_MEM | |||
75 | 92 | ||
76 | Say y here if you intend to boot the modem remoteproc. | 93 | Say y here if you intend to boot the modem remoteproc. |
77 | 94 | ||
95 | config QCOM_RPMH | ||
96 | bool "Qualcomm RPM-Hardened (RPMH) Communication" | ||
97 | depends on ARCH_QCOM && ARM64 && OF || COMPILE_TEST | ||
98 | help | ||
99 | Support for communication with the hardened-RPM blocks in | ||
100 | Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an | ||
101 | internal bus to transmit state requests for shared resources. A set | ||
102 | of hardware components aggregate requests for these resources and | ||
103 | help apply the aggregated state on the resource. | ||
104 | |||
78 | config QCOM_SMEM | 105 | config QCOM_SMEM |
79 | tristate "Qualcomm Shared Memory Manager (SMEM)" | 106 | tristate "Qualcomm Shared Memory Manager (SMEM)" |
80 | depends on ARCH_QCOM | 107 | depends on ARCH_QCOM |
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 19dcf957cb3a..f25b54cd6cf8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
2 | CFLAGS_rpmh-rsc.o := -I$(src) | ||
2 | obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o | 3 | obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o |
3 | obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o | 4 | obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o |
4 | obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o | 5 | obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o |
@@ -8,6 +9,9 @@ obj-$(CONFIG_QCOM_PM) += spm.o | |||
8 | obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o | 9 | obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o |
9 | qmi_helpers-y += qmi_encdec.o qmi_interface.o | 10 | qmi_helpers-y += qmi_encdec.o qmi_interface.o |
10 | obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o | 11 | obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o |
12 | obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o | ||
13 | qcom_rpmh-y += rpmh-rsc.o | ||
14 | qcom_rpmh-y += rpmh.o | ||
11 | obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o | 15 | obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o |
12 | obj-$(CONFIG_QCOM_SMEM) += smem.o | 16 | obj-$(CONFIG_QCOM_SMEM) += smem.o |
13 | obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o | 17 | obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o |
@@ -15,3 +19,5 @@ obj-$(CONFIG_QCOM_SMP2P) += smp2p.o | |||
15 | obj-$(CONFIG_QCOM_SMSM) += smsm.o | 19 | obj-$(CONFIG_QCOM_SMSM) += smsm.o |
16 | obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o | 20 | obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o |
17 | obj-$(CONFIG_QCOM_APR) += apr.o | 21 | obj-$(CONFIG_QCOM_APR) += apr.o |
22 | obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o | ||
23 | obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o | ||
diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c new file mode 100644 index 000000000000..2e1e4f0a5db8 --- /dev/null +++ b/drivers/soc/qcom/llcc-sdm845.c | |||
@@ -0,0 +1,94 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/module.h> | ||
9 | #include <linux/of.h> | ||
10 | #include <linux/of_device.h> | ||
11 | #include <linux/soc/qcom/llcc-qcom.h> | ||
12 | |||
13 | /* | ||
14 | * SCT(System Cache Table) entry contains of the following members: | ||
15 | * usecase_id: Unique id for the client's use case | ||
16 | * slice_id: llcc slice id for each client | ||
17 | * max_cap: The maximum capacity of the cache slice provided in KB | ||
18 | * priority: Priority of the client used to select victim line for replacement | ||
19 | * fixed_size: Boolean indicating if the slice has a fixed capacity | ||
20 | * bonus_ways: Bonus ways are additional ways to be used for any slice, | ||
21 | * if client ends up using more than reserved cache ways. Bonus | ||
22 | * ways are allocated only if they are not reserved for some | ||
23 | * other client. | ||
24 | * res_ways: Reserved ways for the cache slice, the reserved ways cannot | ||
25 | * be used by any other client than the one its assigned to. | ||
26 | * cache_mode: Each slice operates as a cache, this controls the mode of the | ||
27 | * slice: normal or TCM(Tightly Coupled Memory) | ||
28 | * probe_target_ways: Determines what ways to probe for access hit. When | ||
29 | * configured to 1 only bonus and reserved ways are probed. | ||
30 | * When configured to 0 all ways in llcc are probed. | ||
31 | * dis_cap_alloc: Disable capacity based allocation for a client | ||
32 | * retain_on_pc: If this bit is set and client has maintained active vote | ||
33 | * then the ways assigned to this client are not flushed on power | ||
34 | * collapse. | ||
35 | * activate_on_init: Activate the slice immediately after the SCT is programmed | ||
36 | */ | ||
37 | #define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \ | ||
38 | { \ | ||
39 | .usecase_id = uid, \ | ||
40 | .slice_id = sid, \ | ||
41 | .max_cap = mc, \ | ||
42 | .priority = p, \ | ||
43 | .fixed_size = fs, \ | ||
44 | .bonus_ways = bway, \ | ||
45 | .res_ways = rway, \ | ||
46 | .cache_mode = cmod, \ | ||
47 | .probe_target_ways = ptw, \ | ||
48 | .dis_cap_alloc = dca, \ | ||
49 | .retain_on_pc = rp, \ | ||
50 | .activate_on_init = a, \ | ||
51 | } | ||
52 | |||
53 | static struct llcc_slice_config sdm845_data[] = { | ||
54 | SCT_ENTRY(LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1), | ||
55 | SCT_ENTRY(LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), | ||
56 | SCT_ENTRY(LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), | ||
57 | SCT_ENTRY(LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0), | ||
58 | SCT_ENTRY(LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
59 | SCT_ENTRY(LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
60 | SCT_ENTRY(LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0), | ||
61 | SCT_ENTRY(LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
62 | SCT_ENTRY(LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
63 | SCT_ENTRY(LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0), | ||
64 | SCT_ENTRY(LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0), | ||
65 | SCT_ENTRY(LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1), | ||
66 | SCT_ENTRY(LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
67 | SCT_ENTRY(LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
68 | SCT_ENTRY(LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
69 | SCT_ENTRY(LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0), | ||
70 | SCT_ENTRY(LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0), | ||
71 | SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), | ||
72 | }; | ||
73 | |||
74 | static int sdm845_qcom_llcc_probe(struct platform_device *pdev) | ||
75 | { | ||
76 | return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); | ||
77 | } | ||
78 | |||
79 | static const struct of_device_id sdm845_qcom_llcc_of_match[] = { | ||
80 | { .compatible = "qcom,sdm845-llcc", }, | ||
81 | { } | ||
82 | }; | ||
83 | |||
84 | static struct platform_driver sdm845_qcom_llcc_driver = { | ||
85 | .driver = { | ||
86 | .name = "sdm845-llcc", | ||
87 | .of_match_table = sdm845_qcom_llcc_of_match, | ||
88 | }, | ||
89 | .probe = sdm845_qcom_llcc_probe, | ||
90 | }; | ||
91 | module_platform_driver(sdm845_qcom_llcc_driver); | ||
92 | |||
93 | MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); | ||
94 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c new file mode 100644 index 000000000000..54063a31132f --- /dev/null +++ b/drivers/soc/qcom/llcc-slice.c | |||
@@ -0,0 +1,338 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | #include <linux/bitmap.h> | ||
8 | #include <linux/bitops.h> | ||
9 | #include <linux/device.h> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/mutex.h> | ||
14 | #include <linux/of_device.h> | ||
15 | #include <linux/regmap.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/soc/qcom/llcc-qcom.h> | ||
18 | |||
19 | #define ACTIVATE BIT(0) | ||
20 | #define DEACTIVATE BIT(1) | ||
21 | #define ACT_CTRL_OPCODE_ACTIVATE BIT(0) | ||
22 | #define ACT_CTRL_OPCODE_DEACTIVATE BIT(1) | ||
23 | #define ACT_CTRL_ACT_TRIG BIT(0) | ||
24 | #define ACT_CTRL_OPCODE_SHIFT 0x01 | ||
25 | #define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x02 | ||
26 | #define ATTR1_FIXED_SIZE_SHIFT 0x03 | ||
27 | #define ATTR1_PRIORITY_SHIFT 0x04 | ||
28 | #define ATTR1_MAX_CAP_SHIFT 0x10 | ||
29 | #define ATTR0_RES_WAYS_MASK GENMASK(11, 0) | ||
30 | #define ATTR0_BONUS_WAYS_MASK GENMASK(27, 16) | ||
31 | #define ATTR0_BONUS_WAYS_SHIFT 0x10 | ||
32 | #define LLCC_STATUS_READ_DELAY 100 | ||
33 | |||
34 | #define CACHE_LINE_SIZE_SHIFT 6 | ||
35 | |||
36 | #define LLCC_COMMON_STATUS0 0x0003000c | ||
37 | #define LLCC_LB_CNT_MASK GENMASK(31, 28) | ||
38 | #define LLCC_LB_CNT_SHIFT 28 | ||
39 | |||
40 | #define MAX_CAP_TO_BYTES(n) (n * SZ_1K) | ||
41 | #define LLCC_TRP_ACT_CTRLn(n) (n * SZ_4K) | ||
42 | #define LLCC_TRP_STATUSn(n) (4 + n * SZ_4K) | ||
43 | #define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + SZ_8 * n) | ||
44 | #define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + SZ_8 * n) | ||
45 | |||
46 | #define BANK_OFFSET_STRIDE 0x80000 | ||
47 | |||
48 | static struct llcc_drv_data *drv_data; | ||
49 | |||
50 | static const struct regmap_config llcc_regmap_config = { | ||
51 | .reg_bits = 32, | ||
52 | .reg_stride = 4, | ||
53 | .val_bits = 32, | ||
54 | .fast_io = true, | ||
55 | }; | ||
56 | |||
57 | /** | ||
58 | * llcc_slice_getd - get llcc slice descriptor | ||
59 | * @uid: usecase_id for the client | ||
60 | * | ||
61 | * A pointer to llcc slice descriptor will be returned on success and | ||
62 | * and error pointer is returned on failure | ||
63 | */ | ||
64 | struct llcc_slice_desc *llcc_slice_getd(u32 uid) | ||
65 | { | ||
66 | const struct llcc_slice_config *cfg; | ||
67 | struct llcc_slice_desc *desc; | ||
68 | u32 sz, count; | ||
69 | |||
70 | cfg = drv_data->cfg; | ||
71 | sz = drv_data->cfg_size; | ||
72 | |||
73 | for (count = 0; cfg && count < sz; count++, cfg++) | ||
74 | if (cfg->usecase_id == uid) | ||
75 | break; | ||
76 | |||
77 | if (count == sz || !cfg) | ||
78 | return ERR_PTR(-ENODEV); | ||
79 | |||
80 | desc = kzalloc(sizeof(*desc), GFP_KERNEL); | ||
81 | if (!desc) | ||
82 | return ERR_PTR(-ENOMEM); | ||
83 | |||
84 | desc->slice_id = cfg->slice_id; | ||
85 | desc->slice_size = cfg->max_cap; | ||
86 | |||
87 | return desc; | ||
88 | } | ||
89 | EXPORT_SYMBOL_GPL(llcc_slice_getd); | ||
90 | |||
91 | /** | ||
92 | * llcc_slice_putd - llcc slice descritpor | ||
93 | * @desc: Pointer to llcc slice descriptor | ||
94 | */ | ||
95 | void llcc_slice_putd(struct llcc_slice_desc *desc) | ||
96 | { | ||
97 | kfree(desc); | ||
98 | } | ||
99 | EXPORT_SYMBOL_GPL(llcc_slice_putd); | ||
100 | |||
101 | static int llcc_update_act_ctrl(u32 sid, | ||
102 | u32 act_ctrl_reg_val, u32 status) | ||
103 | { | ||
104 | u32 act_ctrl_reg; | ||
105 | u32 status_reg; | ||
106 | u32 slice_status; | ||
107 | int ret; | ||
108 | |||
109 | act_ctrl_reg = drv_data->bcast_off + LLCC_TRP_ACT_CTRLn(sid); | ||
110 | status_reg = drv_data->bcast_off + LLCC_TRP_STATUSn(sid); | ||
111 | |||
112 | /* Set the ACTIVE trigger */ | ||
113 | act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG; | ||
114 | ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val); | ||
115 | if (ret) | ||
116 | return ret; | ||
117 | |||
118 | /* Clear the ACTIVE trigger */ | ||
119 | act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG; | ||
120 | ret = regmap_write(drv_data->regmap, act_ctrl_reg, act_ctrl_reg_val); | ||
121 | if (ret) | ||
122 | return ret; | ||
123 | |||
124 | ret = regmap_read_poll_timeout(drv_data->regmap, status_reg, | ||
125 | slice_status, !(slice_status & status), | ||
126 | 0, LLCC_STATUS_READ_DELAY); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * llcc_slice_activate - Activate the llcc slice | ||
132 | * @desc: Pointer to llcc slice descriptor | ||
133 | * | ||
134 | * A value of zero will be returned on success and a negative errno will | ||
135 | * be returned in error cases | ||
136 | */ | ||
137 | int llcc_slice_activate(struct llcc_slice_desc *desc) | ||
138 | { | ||
139 | int ret; | ||
140 | u32 act_ctrl_val; | ||
141 | |||
142 | mutex_lock(&drv_data->lock); | ||
143 | if (test_bit(desc->slice_id, drv_data->bitmap)) { | ||
144 | mutex_unlock(&drv_data->lock); | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT; | ||
149 | |||
150 | ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, | ||
151 | DEACTIVATE); | ||
152 | if (ret) { | ||
153 | mutex_unlock(&drv_data->lock); | ||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | __set_bit(desc->slice_id, drv_data->bitmap); | ||
158 | mutex_unlock(&drv_data->lock); | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | EXPORT_SYMBOL_GPL(llcc_slice_activate); | ||
163 | |||
164 | /** | ||
165 | * llcc_slice_deactivate - Deactivate the llcc slice | ||
166 | * @desc: Pointer to llcc slice descriptor | ||
167 | * | ||
168 | * A value of zero will be returned on success and a negative errno will | ||
169 | * be returned in error cases | ||
170 | */ | ||
171 | int llcc_slice_deactivate(struct llcc_slice_desc *desc) | ||
172 | { | ||
173 | u32 act_ctrl_val; | ||
174 | int ret; | ||
175 | |||
176 | mutex_lock(&drv_data->lock); | ||
177 | if (!test_bit(desc->slice_id, drv_data->bitmap)) { | ||
178 | mutex_unlock(&drv_data->lock); | ||
179 | return 0; | ||
180 | } | ||
181 | act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT; | ||
182 | |||
183 | ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val, | ||
184 | ACTIVATE); | ||
185 | if (ret) { | ||
186 | mutex_unlock(&drv_data->lock); | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | __clear_bit(desc->slice_id, drv_data->bitmap); | ||
191 | mutex_unlock(&drv_data->lock); | ||
192 | |||
193 | return ret; | ||
194 | } | ||
195 | EXPORT_SYMBOL_GPL(llcc_slice_deactivate); | ||
196 | |||
197 | /** | ||
198 | * llcc_get_slice_id - return the slice id | ||
199 | * @desc: Pointer to llcc slice descriptor | ||
200 | */ | ||
201 | int llcc_get_slice_id(struct llcc_slice_desc *desc) | ||
202 | { | ||
203 | return desc->slice_id; | ||
204 | } | ||
205 | EXPORT_SYMBOL_GPL(llcc_get_slice_id); | ||
206 | |||
207 | /** | ||
208 | * llcc_get_slice_size - return the slice id | ||
209 | * @desc: Pointer to llcc slice descriptor | ||
210 | */ | ||
211 | size_t llcc_get_slice_size(struct llcc_slice_desc *desc) | ||
212 | { | ||
213 | return desc->slice_size; | ||
214 | } | ||
215 | EXPORT_SYMBOL_GPL(llcc_get_slice_size); | ||
216 | |||
217 | static int qcom_llcc_cfg_program(struct platform_device *pdev) | ||
218 | { | ||
219 | int i; | ||
220 | u32 attr1_cfg; | ||
221 | u32 attr0_cfg; | ||
222 | u32 attr1_val; | ||
223 | u32 attr0_val; | ||
224 | u32 max_cap_cacheline; | ||
225 | u32 sz; | ||
226 | int ret; | ||
227 | const struct llcc_slice_config *llcc_table; | ||
228 | struct llcc_slice_desc desc; | ||
229 | u32 bcast_off = drv_data->bcast_off; | ||
230 | |||
231 | sz = drv_data->cfg_size; | ||
232 | llcc_table = drv_data->cfg; | ||
233 | |||
234 | for (i = 0; i < sz; i++) { | ||
235 | attr1_cfg = bcast_off + | ||
236 | LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id); | ||
237 | attr0_cfg = bcast_off + | ||
238 | LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id); | ||
239 | |||
240 | attr1_val = llcc_table[i].cache_mode; | ||
241 | attr1_val |= llcc_table[i].probe_target_ways << | ||
242 | ATTR1_PROBE_TARGET_WAYS_SHIFT; | ||
243 | attr1_val |= llcc_table[i].fixed_size << | ||
244 | ATTR1_FIXED_SIZE_SHIFT; | ||
245 | attr1_val |= llcc_table[i].priority << | ||
246 | ATTR1_PRIORITY_SHIFT; | ||
247 | |||
248 | max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap); | ||
249 | |||
250 | /* LLCC instances can vary for each target. | ||
251 | * The SW writes to broadcast register which gets propagated | ||
252 | * to each llcc instace (llcc0,.. llccN). | ||
253 | * Since the size of the memory is divided equally amongst the | ||
254 | * llcc instances, we need to configure the max cap accordingly. | ||
255 | */ | ||
256 | max_cap_cacheline = max_cap_cacheline / drv_data->num_banks; | ||
257 | max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT; | ||
258 | attr1_val |= max_cap_cacheline << ATTR1_MAX_CAP_SHIFT; | ||
259 | |||
260 | attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK; | ||
261 | attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT; | ||
262 | |||
263 | ret = regmap_write(drv_data->regmap, attr1_cfg, attr1_val); | ||
264 | if (ret) | ||
265 | return ret; | ||
266 | ret = regmap_write(drv_data->regmap, attr0_cfg, attr0_val); | ||
267 | if (ret) | ||
268 | return ret; | ||
269 | if (llcc_table[i].activate_on_init) { | ||
270 | desc.slice_id = llcc_table[i].slice_id; | ||
271 | ret = llcc_slice_activate(&desc); | ||
272 | } | ||
273 | } | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | int qcom_llcc_probe(struct platform_device *pdev, | ||
278 | const struct llcc_slice_config *llcc_cfg, u32 sz) | ||
279 | { | ||
280 | u32 num_banks; | ||
281 | struct device *dev = &pdev->dev; | ||
282 | struct resource *res; | ||
283 | void __iomem *base; | ||
284 | int ret, i; | ||
285 | |||
286 | drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); | ||
287 | if (!drv_data) | ||
288 | return -ENOMEM; | ||
289 | |||
290 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
291 | base = devm_ioremap_resource(&pdev->dev, res); | ||
292 | if (IS_ERR(base)) | ||
293 | return PTR_ERR(base); | ||
294 | |||
295 | drv_data->regmap = devm_regmap_init_mmio(dev, base, | ||
296 | &llcc_regmap_config); | ||
297 | if (IS_ERR(drv_data->regmap)) | ||
298 | return PTR_ERR(drv_data->regmap); | ||
299 | |||
300 | ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0, | ||
301 | &num_banks); | ||
302 | if (ret) | ||
303 | return ret; | ||
304 | |||
305 | num_banks &= LLCC_LB_CNT_MASK; | ||
306 | num_banks >>= LLCC_LB_CNT_SHIFT; | ||
307 | drv_data->num_banks = num_banks; | ||
308 | |||
309 | for (i = 0; i < sz; i++) | ||
310 | if (llcc_cfg[i].slice_id > drv_data->max_slices) | ||
311 | drv_data->max_slices = llcc_cfg[i].slice_id; | ||
312 | |||
313 | drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), | ||
314 | GFP_KERNEL); | ||
315 | if (!drv_data->offsets) | ||
316 | return -ENOMEM; | ||
317 | |||
318 | for (i = 0; i < num_banks; i++) | ||
319 | drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; | ||
320 | |||
321 | drv_data->bcast_off = num_banks * BANK_OFFSET_STRIDE; | ||
322 | |||
323 | drv_data->bitmap = devm_kcalloc(dev, | ||
324 | BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long), | ||
325 | GFP_KERNEL); | ||
326 | if (!drv_data->bitmap) | ||
327 | return -ENOMEM; | ||
328 | |||
329 | drv_data->cfg = llcc_cfg; | ||
330 | drv_data->cfg_size = sz; | ||
331 | mutex_init(&drv_data->lock); | ||
332 | platform_set_drvdata(pdev, drv_data); | ||
333 | |||
334 | return qcom_llcc_cfg_program(pdev); | ||
335 | } | ||
336 | EXPORT_SYMBOL_GPL(qcom_llcc_probe); | ||
337 | |||
338 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index c8999e38b005..8a3678c2e83c 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c | |||
@@ -184,6 +184,7 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) | |||
184 | device_initialize(&rmtfs_mem->dev); | 184 | device_initialize(&rmtfs_mem->dev); |
185 | rmtfs_mem->dev.parent = &pdev->dev; | 185 | rmtfs_mem->dev.parent = &pdev->dev; |
186 | rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups; | 186 | rmtfs_mem->dev.groups = qcom_rmtfs_mem_groups; |
187 | rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device; | ||
187 | 188 | ||
188 | rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr, | 189 | rmtfs_mem->base = devm_memremap(&rmtfs_mem->dev, rmtfs_mem->addr, |
189 | rmtfs_mem->size, MEMREMAP_WC); | 190 | rmtfs_mem->size, MEMREMAP_WC); |
@@ -206,8 +207,6 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) | |||
206 | goto put_device; | 207 | goto put_device; |
207 | } | 208 | } |
208 | 209 | ||
209 | rmtfs_mem->dev.release = qcom_rmtfs_mem_release_device; | ||
210 | |||
211 | ret = of_property_read_u32(node, "qcom,vmid", &vmid); | 210 | ret = of_property_read_u32(node, "qcom,vmid", &vmid); |
212 | if (ret < 0 && ret != -EINVAL) { | 211 | if (ret < 0 && ret != -EINVAL) { |
213 | dev_err(&pdev->dev, "failed to parse qcom,vmid\n"); | 212 | dev_err(&pdev->dev, "failed to parse qcom,vmid\n"); |
diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h new file mode 100644 index 000000000000..a7bbbb67991c --- /dev/null +++ b/drivers/soc/qcom/rpmh-internal.h | |||
@@ -0,0 +1,114 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | |||
7 | #ifndef __RPM_INTERNAL_H__ | ||
8 | #define __RPM_INTERNAL_H__ | ||
9 | |||
10 | #include <linux/bitmap.h> | ||
11 | #include <soc/qcom/tcs.h> | ||
12 | |||
13 | #define TCS_TYPE_NR 4 | ||
14 | #define MAX_CMDS_PER_TCS 16 | ||
15 | #define MAX_TCS_PER_TYPE 3 | ||
16 | #define MAX_TCS_NR (MAX_TCS_PER_TYPE * TCS_TYPE_NR) | ||
17 | #define MAX_TCS_SLOTS (MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE) | ||
18 | |||
19 | struct rsc_drv; | ||
20 | |||
21 | /** | ||
22 | * struct tcs_group: group of Trigger Command Sets (TCS) to send state requests | ||
23 | * to the controller | ||
24 | * | ||
25 | * @drv: the controller | ||
26 | * @type: type of the TCS in this group - active, sleep, wake | ||
27 | * @mask: mask of the TCSes relative to all the TCSes in the RSC | ||
28 | * @offset: start of the TCS group relative to the TCSes in the RSC | ||
29 | * @num_tcs: number of TCSes in this type | ||
30 | * @ncpt: number of commands in each TCS | ||
31 | * @lock: lock for synchronizing this TCS writes | ||
32 | * @req: requests that are sent from the TCS | ||
33 | * @cmd_cache: flattened cache of cmds in sleep/wake TCS | ||
34 | * @slots: indicates which of @cmd_addr are occupied | ||
35 | */ | ||
36 | struct tcs_group { | ||
37 | struct rsc_drv *drv; | ||
38 | int type; | ||
39 | u32 mask; | ||
40 | u32 offset; | ||
41 | int num_tcs; | ||
42 | int ncpt; | ||
43 | spinlock_t lock; | ||
44 | const struct tcs_request *req[MAX_TCS_PER_TYPE]; | ||
45 | u32 *cmd_cache; | ||
46 | DECLARE_BITMAP(slots, MAX_TCS_SLOTS); | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * struct rpmh_request: the message to be sent to rpmh-rsc | ||
51 | * | ||
52 | * @msg: the request | ||
53 | * @cmd: the payload that will be part of the @msg | ||
54 | * @completion: triggered when request is done | ||
55 | * @dev: the device making the request | ||
56 | * @err: err return from the controller | ||
57 | * @needs_free: check to free dynamically allocated request object | ||
58 | */ | ||
59 | struct rpmh_request { | ||
60 | struct tcs_request msg; | ||
61 | struct tcs_cmd cmd[MAX_RPMH_PAYLOAD]; | ||
62 | struct completion *completion; | ||
63 | const struct device *dev; | ||
64 | int err; | ||
65 | bool needs_free; | ||
66 | }; | ||
67 | |||
68 | /** | ||
69 | * struct rpmh_ctrlr: our representation of the controller | ||
70 | * | ||
71 | * @cache: the list of cached requests | ||
72 | * @cache_lock: synchronize access to the cache data | ||
73 | * @dirty: was the cache updated since flush | ||
74 | * @batch_cache: Cache sleep and wake requests sent as batch | ||
75 | */ | ||
76 | struct rpmh_ctrlr { | ||
77 | struct list_head cache; | ||
78 | spinlock_t cache_lock; | ||
79 | bool dirty; | ||
80 | struct list_head batch_cache; | ||
81 | }; | ||
82 | |||
83 | /** | ||
84 | * struct rsc_drv: the Direct Resource Voter (DRV) of the | ||
85 | * Resource State Coordinator controller (RSC) | ||
86 | * | ||
87 | * @name: controller identifier | ||
88 | * @tcs_base: start address of the TCS registers in this controller | ||
89 | * @id: instance id in the controller (Direct Resource Voter) | ||
90 | * @num_tcs: number of TCSes in this DRV | ||
91 | * @tcs: TCS groups | ||
92 | * @tcs_in_use: s/w state of the TCS | ||
93 | * @lock: synchronize state of the controller | ||
94 | * @client: handle to the DRV's client. | ||
95 | */ | ||
96 | struct rsc_drv { | ||
97 | const char *name; | ||
98 | void __iomem *tcs_base; | ||
99 | int id; | ||
100 | int num_tcs; | ||
101 | struct tcs_group tcs[TCS_TYPE_NR]; | ||
102 | DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); | ||
103 | spinlock_t lock; | ||
104 | struct rpmh_ctrlr client; | ||
105 | }; | ||
106 | |||
107 | int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg); | ||
108 | int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, | ||
109 | const struct tcs_request *msg); | ||
110 | int rpmh_rsc_invalidate(struct rsc_drv *drv); | ||
111 | |||
112 | void rpmh_tx_done(const struct tcs_request *msg, int r); | ||
113 | |||
114 | #endif /* __RPM_INTERNAL_H__ */ | ||
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c new file mode 100644 index 000000000000..ee75da66d64b --- /dev/null +++ b/drivers/soc/qcom/rpmh-rsc.c | |||
@@ -0,0 +1,693 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME | ||
7 | |||
8 | #include <linux/atomic.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/io.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/list.h> | ||
14 | #include <linux/of.h> | ||
15 | #include <linux/of_irq.h> | ||
16 | #include <linux/of_platform.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | |||
21 | #include <soc/qcom/cmd-db.h> | ||
22 | #include <soc/qcom/tcs.h> | ||
23 | #include <dt-bindings/soc/qcom,rpmh-rsc.h> | ||
24 | |||
25 | #include "rpmh-internal.h" | ||
26 | |||
27 | #define CREATE_TRACE_POINTS | ||
28 | #include "trace-rpmh.h" | ||
29 | |||
30 | #define RSC_DRV_TCS_OFFSET 672 | ||
31 | #define RSC_DRV_CMD_OFFSET 20 | ||
32 | |||
33 | /* DRV Configuration Information Register */ | ||
34 | #define DRV_PRNT_CHLD_CONFIG 0x0C | ||
35 | #define DRV_NUM_TCS_MASK 0x3F | ||
36 | #define DRV_NUM_TCS_SHIFT 6 | ||
37 | #define DRV_NCPT_MASK 0x1F | ||
38 | #define DRV_NCPT_SHIFT 27 | ||
39 | |||
40 | /* Register offsets */ | ||
41 | #define RSC_DRV_IRQ_ENABLE 0x00 | ||
42 | #define RSC_DRV_IRQ_STATUS 0x04 | ||
43 | #define RSC_DRV_IRQ_CLEAR 0x08 | ||
44 | #define RSC_DRV_CMD_WAIT_FOR_CMPL 0x10 | ||
45 | #define RSC_DRV_CONTROL 0x14 | ||
46 | #define RSC_DRV_STATUS 0x18 | ||
47 | #define RSC_DRV_CMD_ENABLE 0x1C | ||
48 | #define RSC_DRV_CMD_MSGID 0x30 | ||
49 | #define RSC_DRV_CMD_ADDR 0x34 | ||
50 | #define RSC_DRV_CMD_DATA 0x38 | ||
51 | #define RSC_DRV_CMD_STATUS 0x3C | ||
52 | #define RSC_DRV_CMD_RESP_DATA 0x40 | ||
53 | |||
54 | #define TCS_AMC_MODE_ENABLE BIT(16) | ||
55 | #define TCS_AMC_MODE_TRIGGER BIT(24) | ||
56 | |||
57 | /* TCS CMD register bit mask */ | ||
58 | #define CMD_MSGID_LEN 8 | ||
59 | #define CMD_MSGID_RESP_REQ BIT(8) | ||
60 | #define CMD_MSGID_WRITE BIT(16) | ||
61 | #define CMD_STATUS_ISSUED BIT(8) | ||
62 | #define CMD_STATUS_COMPL BIT(16) | ||
63 | |||
64 | static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) | ||
65 | { | ||
66 | return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + | ||
67 | RSC_DRV_CMD_OFFSET * cmd_id); | ||
68 | } | ||
69 | |||
70 | static void write_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id, | ||
71 | u32 data) | ||
72 | { | ||
73 | writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + | ||
74 | RSC_DRV_CMD_OFFSET * cmd_id); | ||
75 | } | ||
76 | |||
77 | static void write_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, u32 data) | ||
78 | { | ||
79 | writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id); | ||
80 | } | ||
81 | |||
82 | static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, | ||
83 | u32 data) | ||
84 | { | ||
85 | writel(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id); | ||
86 | for (;;) { | ||
87 | if (data == readl(drv->tcs_base + reg + | ||
88 | RSC_DRV_TCS_OFFSET * tcs_id)) | ||
89 | break; | ||
90 | udelay(1); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) | ||
95 | { | ||
96 | return !test_bit(tcs_id, drv->tcs_in_use) && | ||
97 | read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id, 0); | ||
98 | } | ||
99 | |||
100 | static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) | ||
101 | { | ||
102 | return &drv->tcs[type]; | ||
103 | } | ||
104 | |||
105 | static int tcs_invalidate(struct rsc_drv *drv, int type) | ||
106 | { | ||
107 | int m; | ||
108 | struct tcs_group *tcs; | ||
109 | |||
110 | tcs = get_tcs_of_type(drv, type); | ||
111 | |||
112 | spin_lock(&tcs->lock); | ||
113 | if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) { | ||
114 | spin_unlock(&tcs->lock); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) { | ||
119 | if (!tcs_is_free(drv, m)) { | ||
120 | spin_unlock(&tcs->lock); | ||
121 | return -EAGAIN; | ||
122 | } | ||
123 | write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0); | ||
124 | } | ||
125 | bitmap_zero(tcs->slots, MAX_TCS_SLOTS); | ||
126 | spin_unlock(&tcs->lock); | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * rpmh_rsc_invalidate - Invalidate sleep and wake TCSes | ||
133 | * | ||
134 | * @drv: the RSC controller | ||
135 | */ | ||
136 | int rpmh_rsc_invalidate(struct rsc_drv *drv) | ||
137 | { | ||
138 | int ret; | ||
139 | |||
140 | ret = tcs_invalidate(drv, SLEEP_TCS); | ||
141 | if (!ret) | ||
142 | ret = tcs_invalidate(drv, WAKE_TCS); | ||
143 | |||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, | ||
148 | const struct tcs_request *msg) | ||
149 | { | ||
150 | int type, ret; | ||
151 | struct tcs_group *tcs; | ||
152 | |||
153 | switch (msg->state) { | ||
154 | case RPMH_ACTIVE_ONLY_STATE: | ||
155 | type = ACTIVE_TCS; | ||
156 | break; | ||
157 | case RPMH_WAKE_ONLY_STATE: | ||
158 | type = WAKE_TCS; | ||
159 | break; | ||
160 | case RPMH_SLEEP_STATE: | ||
161 | type = SLEEP_TCS; | ||
162 | break; | ||
163 | default: | ||
164 | return ERR_PTR(-EINVAL); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * If we are making an active request on a RSC that does not have a | ||
169 | * dedicated TCS for active state use, then re-purpose a wake TCS to | ||
170 | * send active votes. | ||
171 | * NOTE: The driver must be aware that this RSC does not have a | ||
172 | * dedicated AMC, and therefore would invalidate the sleep and wake | ||
173 | * TCSes before making an active state request. | ||
174 | */ | ||
175 | tcs = get_tcs_of_type(drv, type); | ||
176 | if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) { | ||
177 | tcs = get_tcs_of_type(drv, WAKE_TCS); | ||
178 | if (tcs->num_tcs) { | ||
179 | ret = rpmh_rsc_invalidate(drv); | ||
180 | if (ret) | ||
181 | return ERR_PTR(ret); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | return tcs; | ||
186 | } | ||
187 | |||
188 | static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, | ||
189 | int tcs_id) | ||
190 | { | ||
191 | struct tcs_group *tcs; | ||
192 | int i; | ||
193 | |||
194 | for (i = 0; i < TCS_TYPE_NR; i++) { | ||
195 | tcs = &drv->tcs[i]; | ||
196 | if (tcs->mask & BIT(tcs_id)) | ||
197 | return tcs->req[tcs_id - tcs->offset]; | ||
198 | } | ||
199 | |||
200 | return NULL; | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * tcs_tx_done: TX Done interrupt handler | ||
205 | */ | ||
206 | static irqreturn_t tcs_tx_done(int irq, void *p) | ||
207 | { | ||
208 | struct rsc_drv *drv = p; | ||
209 | int i, j, err = 0; | ||
210 | unsigned long irq_status; | ||
211 | const struct tcs_request *req; | ||
212 | struct tcs_cmd *cmd; | ||
213 | |||
214 | irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0, 0); | ||
215 | |||
216 | for_each_set_bit(i, &irq_status, BITS_PER_LONG) { | ||
217 | req = get_req_from_tcs(drv, i); | ||
218 | if (!req) { | ||
219 | WARN_ON(1); | ||
220 | goto skip; | ||
221 | } | ||
222 | |||
223 | err = 0; | ||
224 | for (j = 0; j < req->num_cmds; j++) { | ||
225 | u32 sts; | ||
226 | |||
227 | cmd = &req->cmds[j]; | ||
228 | sts = read_tcs_reg(drv, RSC_DRV_CMD_STATUS, i, j); | ||
229 | if (!(sts & CMD_STATUS_ISSUED) || | ||
230 | ((req->wait_for_compl || cmd->wait) && | ||
231 | !(sts & CMD_STATUS_COMPL))) { | ||
232 | pr_err("Incomplete request: %s: addr=%#x data=%#x", | ||
233 | drv->name, cmd->addr, cmd->data); | ||
234 | err = -EIO; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | trace_rpmh_tx_done(drv, i, req, err); | ||
239 | skip: | ||
240 | /* Reclaim the TCS */ | ||
241 | write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0); | ||
242 | write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i)); | ||
243 | spin_lock(&drv->lock); | ||
244 | clear_bit(i, drv->tcs_in_use); | ||
245 | spin_unlock(&drv->lock); | ||
246 | if (req) | ||
247 | rpmh_tx_done(req, err); | ||
248 | } | ||
249 | |||
250 | return IRQ_HANDLED; | ||
251 | } | ||
252 | |||
253 | static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, | ||
254 | const struct tcs_request *msg) | ||
255 | { | ||
256 | u32 msgid, cmd_msgid; | ||
257 | u32 cmd_enable = 0; | ||
258 | u32 cmd_complete; | ||
259 | struct tcs_cmd *cmd; | ||
260 | int i, j; | ||
261 | |||
262 | cmd_msgid = CMD_MSGID_LEN; | ||
263 | cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0; | ||
264 | cmd_msgid |= CMD_MSGID_WRITE; | ||
265 | |||
266 | cmd_complete = read_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0); | ||
267 | |||
268 | for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) { | ||
269 | cmd = &msg->cmds[i]; | ||
270 | cmd_enable |= BIT(j); | ||
271 | cmd_complete |= cmd->wait << j; | ||
272 | msgid = cmd_msgid; | ||
273 | msgid |= cmd->wait ? CMD_MSGID_RESP_REQ : 0; | ||
274 | |||
275 | write_tcs_cmd(drv, RSC_DRV_CMD_MSGID, tcs_id, j, msgid); | ||
276 | write_tcs_cmd(drv, RSC_DRV_CMD_ADDR, tcs_id, j, cmd->addr); | ||
277 | write_tcs_cmd(drv, RSC_DRV_CMD_DATA, tcs_id, j, cmd->data); | ||
278 | trace_rpmh_send_msg(drv, tcs_id, j, msgid, cmd); | ||
279 | } | ||
280 | |||
281 | write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, cmd_complete); | ||
282 | cmd_enable |= read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); | ||
283 | write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, cmd_enable); | ||
284 | } | ||
285 | |||
286 | static void __tcs_trigger(struct rsc_drv *drv, int tcs_id) | ||
287 | { | ||
288 | u32 enable; | ||
289 | |||
290 | /* | ||
291 | * HW req: Clear the DRV_CONTROL and enable TCS again | ||
292 | * While clearing ensure that the AMC mode trigger is cleared | ||
293 | * and then the mode enable is cleared. | ||
294 | */ | ||
295 | enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id, 0); | ||
296 | enable &= ~TCS_AMC_MODE_TRIGGER; | ||
297 | write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); | ||
298 | enable &= ~TCS_AMC_MODE_ENABLE; | ||
299 | write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); | ||
300 | |||
301 | /* Enable the AMC mode on the TCS and then trigger the TCS */ | ||
302 | enable = TCS_AMC_MODE_ENABLE; | ||
303 | write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); | ||
304 | enable |= TCS_AMC_MODE_TRIGGER; | ||
305 | write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); | ||
306 | } | ||
307 | |||
308 | static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, | ||
309 | const struct tcs_request *msg) | ||
310 | { | ||
311 | unsigned long curr_enabled; | ||
312 | u32 addr; | ||
313 | int i, j, k; | ||
314 | int tcs_id = tcs->offset; | ||
315 | |||
316 | for (i = 0; i < tcs->num_tcs; i++, tcs_id++) { | ||
317 | if (tcs_is_free(drv, tcs_id)) | ||
318 | continue; | ||
319 | |||
320 | curr_enabled = read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); | ||
321 | |||
322 | for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) { | ||
323 | addr = read_tcs_reg(drv, RSC_DRV_CMD_ADDR, tcs_id, j); | ||
324 | for (k = 0; k < msg->num_cmds; k++) { | ||
325 | if (addr == msg->cmds[k].addr) | ||
326 | return -EBUSY; | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static int find_free_tcs(struct tcs_group *tcs) | ||
335 | { | ||
336 | int i; | ||
337 | |||
338 | for (i = 0; i < tcs->num_tcs; i++) { | ||
339 | if (tcs_is_free(tcs->drv, tcs->offset + i)) | ||
340 | return tcs->offset + i; | ||
341 | } | ||
342 | |||
343 | return -EBUSY; | ||
344 | } | ||
345 | |||
346 | static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) | ||
347 | { | ||
348 | struct tcs_group *tcs; | ||
349 | int tcs_id; | ||
350 | unsigned long flags; | ||
351 | int ret; | ||
352 | |||
353 | tcs = get_tcs_for_msg(drv, msg); | ||
354 | if (IS_ERR(tcs)) | ||
355 | return PTR_ERR(tcs); | ||
356 | |||
357 | spin_lock_irqsave(&tcs->lock, flags); | ||
358 | spin_lock(&drv->lock); | ||
359 | /* | ||
360 | * The h/w does not like if we send a request to the same address, | ||
361 | * when one is already in-flight or being processed. | ||
362 | */ | ||
363 | ret = check_for_req_inflight(drv, tcs, msg); | ||
364 | if (ret) { | ||
365 | spin_unlock(&drv->lock); | ||
366 | goto done_write; | ||
367 | } | ||
368 | |||
369 | tcs_id = find_free_tcs(tcs); | ||
370 | if (tcs_id < 0) { | ||
371 | ret = tcs_id; | ||
372 | spin_unlock(&drv->lock); | ||
373 | goto done_write; | ||
374 | } | ||
375 | |||
376 | tcs->req[tcs_id - tcs->offset] = msg; | ||
377 | set_bit(tcs_id, drv->tcs_in_use); | ||
378 | spin_unlock(&drv->lock); | ||
379 | |||
380 | __tcs_buffer_write(drv, tcs_id, 0, msg); | ||
381 | __tcs_trigger(drv, tcs_id); | ||
382 | |||
383 | done_write: | ||
384 | spin_unlock_irqrestore(&tcs->lock, flags); | ||
385 | return ret; | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * rpmh_rsc_send_data: Validate the incoming message and write to the | ||
390 | * appropriate TCS block. | ||
391 | * | ||
392 | * @drv: the controller | ||
393 | * @msg: the data to be sent | ||
394 | * | ||
395 | * Return: 0 on success, -EINVAL on error. | ||
396 | * Note: This call blocks until a valid data is written to the TCS. | ||
397 | */ | ||
398 | int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) | ||
399 | { | ||
400 | int ret; | ||
401 | |||
402 | if (!msg || !msg->cmds || !msg->num_cmds || | ||
403 | msg->num_cmds > MAX_RPMH_PAYLOAD) { | ||
404 | WARN_ON(1); | ||
405 | return -EINVAL; | ||
406 | } | ||
407 | |||
408 | do { | ||
409 | ret = tcs_write(drv, msg); | ||
410 | if (ret == -EBUSY) { | ||
411 | pr_info_ratelimited("TCS Busy, retrying RPMH message send: addr=%#x\n", | ||
412 | msg->cmds[0].addr); | ||
413 | udelay(10); | ||
414 | } | ||
415 | } while (ret == -EBUSY); | ||
416 | |||
417 | return ret; | ||
418 | } | ||
419 | |||
420 | static int find_match(const struct tcs_group *tcs, const struct tcs_cmd *cmd, | ||
421 | int len) | ||
422 | { | ||
423 | int i, j; | ||
424 | |||
425 | /* Check for already cached commands */ | ||
426 | for_each_set_bit(i, tcs->slots, MAX_TCS_SLOTS) { | ||
427 | if (tcs->cmd_cache[i] != cmd[0].addr) | ||
428 | continue; | ||
429 | if (i + len >= tcs->num_tcs * tcs->ncpt) | ||
430 | goto seq_err; | ||
431 | for (j = 0; j < len; j++) { | ||
432 | if (tcs->cmd_cache[i + j] != cmd[j].addr) | ||
433 | goto seq_err; | ||
434 | } | ||
435 | return i; | ||
436 | } | ||
437 | |||
438 | return -ENODATA; | ||
439 | |||
440 | seq_err: | ||
441 | WARN(1, "Message does not match previous sequence.\n"); | ||
442 | return -EINVAL; | ||
443 | } | ||
444 | |||
445 | static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, | ||
446 | int *tcs_id, int *cmd_id) | ||
447 | { | ||
448 | int slot, offset; | ||
449 | int i = 0; | ||
450 | |||
451 | /* Find if we already have the msg in our TCS */ | ||
452 | slot = find_match(tcs, msg->cmds, msg->num_cmds); | ||
453 | if (slot >= 0) | ||
454 | goto copy_data; | ||
455 | |||
456 | /* Do over, until we can fit the full payload in a TCS */ | ||
457 | do { | ||
458 | slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS, | ||
459 | i, msg->num_cmds, 0); | ||
460 | if (slot == tcs->num_tcs * tcs->ncpt) | ||
461 | return -ENOMEM; | ||
462 | i += tcs->ncpt; | ||
463 | } while (slot + msg->num_cmds - 1 >= i); | ||
464 | |||
465 | copy_data: | ||
466 | bitmap_set(tcs->slots, slot, msg->num_cmds); | ||
467 | /* Copy the addresses of the resources over to the slots */ | ||
468 | for (i = 0; i < msg->num_cmds; i++) | ||
469 | tcs->cmd_cache[slot + i] = msg->cmds[i].addr; | ||
470 | |||
471 | offset = slot / tcs->ncpt; | ||
472 | *tcs_id = offset + tcs->offset; | ||
473 | *cmd_id = slot % tcs->ncpt; | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg) | ||
479 | { | ||
480 | struct tcs_group *tcs; | ||
481 | int tcs_id = 0, cmd_id = 0; | ||
482 | unsigned long flags; | ||
483 | int ret; | ||
484 | |||
485 | tcs = get_tcs_for_msg(drv, msg); | ||
486 | if (IS_ERR(tcs)) | ||
487 | return PTR_ERR(tcs); | ||
488 | |||
489 | spin_lock_irqsave(&tcs->lock, flags); | ||
490 | /* find the TCS id and the command in the TCS to write to */ | ||
491 | ret = find_slots(tcs, msg, &tcs_id, &cmd_id); | ||
492 | if (!ret) | ||
493 | __tcs_buffer_write(drv, tcs_id, cmd_id, msg); | ||
494 | spin_unlock_irqrestore(&tcs->lock, flags); | ||
495 | |||
496 | return ret; | ||
497 | } | ||
498 | |||
499 | /** | ||
500 | * rpmh_rsc_write_ctrl_data: Write request to the controller | ||
501 | * | ||
502 | * @drv: the controller | ||
503 | * @msg: the data to be written to the controller | ||
504 | * | ||
505 | * There is no response returned for writing the request to the controller. | ||
506 | */ | ||
507 | int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) | ||
508 | { | ||
509 | if (!msg || !msg->cmds || !msg->num_cmds || | ||
510 | msg->num_cmds > MAX_RPMH_PAYLOAD) { | ||
511 | pr_err("Payload error\n"); | ||
512 | return -EINVAL; | ||
513 | } | ||
514 | |||
515 | /* Data sent to this API will not be sent immediately */ | ||
516 | if (msg->state == RPMH_ACTIVE_ONLY_STATE) | ||
517 | return -EINVAL; | ||
518 | |||
519 | return tcs_ctrl_write(drv, msg); | ||
520 | } | ||
521 | |||
522 | static int rpmh_probe_tcs_config(struct platform_device *pdev, | ||
523 | struct rsc_drv *drv) | ||
524 | { | ||
525 | struct tcs_type_config { | ||
526 | u32 type; | ||
527 | u32 n; | ||
528 | } tcs_cfg[TCS_TYPE_NR] = { { 0 } }; | ||
529 | struct device_node *dn = pdev->dev.of_node; | ||
530 | u32 config, max_tcs, ncpt, offset; | ||
531 | int i, ret, n, st = 0; | ||
532 | struct tcs_group *tcs; | ||
533 | struct resource *res; | ||
534 | void __iomem *base; | ||
535 | char drv_id[10] = {0}; | ||
536 | |||
537 | snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); | ||
538 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id); | ||
539 | base = devm_ioremap_resource(&pdev->dev, res); | ||
540 | if (IS_ERR(base)) | ||
541 | return PTR_ERR(base); | ||
542 | |||
543 | ret = of_property_read_u32(dn, "qcom,tcs-offset", &offset); | ||
544 | if (ret) | ||
545 | return ret; | ||
546 | drv->tcs_base = base + offset; | ||
547 | |||
548 | config = readl_relaxed(base + DRV_PRNT_CHLD_CONFIG); | ||
549 | |||
550 | max_tcs = config; | ||
551 | max_tcs &= DRV_NUM_TCS_MASK << (DRV_NUM_TCS_SHIFT * drv->id); | ||
552 | max_tcs = max_tcs >> (DRV_NUM_TCS_SHIFT * drv->id); | ||
553 | |||
554 | ncpt = config & (DRV_NCPT_MASK << DRV_NCPT_SHIFT); | ||
555 | ncpt = ncpt >> DRV_NCPT_SHIFT; | ||
556 | |||
557 | n = of_property_count_u32_elems(dn, "qcom,tcs-config"); | ||
558 | if (n != 2 * TCS_TYPE_NR) | ||
559 | return -EINVAL; | ||
560 | |||
561 | for (i = 0; i < TCS_TYPE_NR; i++) { | ||
562 | ret = of_property_read_u32_index(dn, "qcom,tcs-config", | ||
563 | i * 2, &tcs_cfg[i].type); | ||
564 | if (ret) | ||
565 | return ret; | ||
566 | if (tcs_cfg[i].type >= TCS_TYPE_NR) | ||
567 | return -EINVAL; | ||
568 | |||
569 | ret = of_property_read_u32_index(dn, "qcom,tcs-config", | ||
570 | i * 2 + 1, &tcs_cfg[i].n); | ||
571 | if (ret) | ||
572 | return ret; | ||
573 | if (tcs_cfg[i].n > MAX_TCS_PER_TYPE) | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | for (i = 0; i < TCS_TYPE_NR; i++) { | ||
578 | tcs = &drv->tcs[tcs_cfg[i].type]; | ||
579 | if (tcs->drv) | ||
580 | return -EINVAL; | ||
581 | tcs->drv = drv; | ||
582 | tcs->type = tcs_cfg[i].type; | ||
583 | tcs->num_tcs = tcs_cfg[i].n; | ||
584 | tcs->ncpt = ncpt; | ||
585 | spin_lock_init(&tcs->lock); | ||
586 | |||
587 | if (!tcs->num_tcs || tcs->type == CONTROL_TCS) | ||
588 | continue; | ||
589 | |||
590 | if (st + tcs->num_tcs > max_tcs || | ||
591 | st + tcs->num_tcs >= BITS_PER_BYTE * sizeof(tcs->mask)) | ||
592 | return -EINVAL; | ||
593 | |||
594 | tcs->mask = ((1 << tcs->num_tcs) - 1) << st; | ||
595 | tcs->offset = st; | ||
596 | st += tcs->num_tcs; | ||
597 | |||
598 | /* | ||
599 | * Allocate memory to cache sleep and wake requests to | ||
600 | * avoid reading TCS register memory. | ||
601 | */ | ||
602 | if (tcs->type == ACTIVE_TCS) | ||
603 | continue; | ||
604 | |||
605 | tcs->cmd_cache = devm_kcalloc(&pdev->dev, | ||
606 | tcs->num_tcs * ncpt, sizeof(u32), | ||
607 | GFP_KERNEL); | ||
608 | if (!tcs->cmd_cache) | ||
609 | return -ENOMEM; | ||
610 | } | ||
611 | |||
612 | drv->num_tcs = st; | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | static int rpmh_rsc_probe(struct platform_device *pdev) | ||
618 | { | ||
619 | struct device_node *dn = pdev->dev.of_node; | ||
620 | struct rsc_drv *drv; | ||
621 | int ret, irq; | ||
622 | |||
623 | /* | ||
624 | * Even though RPMh doesn't directly use cmd-db, all of its children | ||
625 | * do. To avoid adding this check to our children we'll do it now. | ||
626 | */ | ||
627 | ret = cmd_db_ready(); | ||
628 | if (ret) { | ||
629 | if (ret != -EPROBE_DEFER) | ||
630 | dev_err(&pdev->dev, "Command DB not available (%d)\n", | ||
631 | ret); | ||
632 | return ret; | ||
633 | } | ||
634 | |||
635 | drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); | ||
636 | if (!drv) | ||
637 | return -ENOMEM; | ||
638 | |||
639 | ret = of_property_read_u32(dn, "qcom,drv-id", &drv->id); | ||
640 | if (ret) | ||
641 | return ret; | ||
642 | |||
643 | drv->name = of_get_property(dn, "label", NULL); | ||
644 | if (!drv->name) | ||
645 | drv->name = dev_name(&pdev->dev); | ||
646 | |||
647 | ret = rpmh_probe_tcs_config(pdev, drv); | ||
648 | if (ret) | ||
649 | return ret; | ||
650 | |||
651 | spin_lock_init(&drv->lock); | ||
652 | bitmap_zero(drv->tcs_in_use, MAX_TCS_NR); | ||
653 | |||
654 | irq = platform_get_irq(pdev, drv->id); | ||
655 | if (irq < 0) | ||
656 | return irq; | ||
657 | |||
658 | ret = devm_request_irq(&pdev->dev, irq, tcs_tx_done, | ||
659 | IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, | ||
660 | drv->name, drv); | ||
661 | if (ret) | ||
662 | return ret; | ||
663 | |||
664 | /* Enable the active TCS to send requests immediately */ | ||
665 | write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, drv->tcs[ACTIVE_TCS].mask); | ||
666 | |||
667 | spin_lock_init(&drv->client.cache_lock); | ||
668 | INIT_LIST_HEAD(&drv->client.cache); | ||
669 | INIT_LIST_HEAD(&drv->client.batch_cache); | ||
670 | |||
671 | dev_set_drvdata(&pdev->dev, drv); | ||
672 | |||
673 | return devm_of_platform_populate(&pdev->dev); | ||
674 | } | ||
675 | |||
676 | static const struct of_device_id rpmh_drv_match[] = { | ||
677 | { .compatible = "qcom,rpmh-rsc", }, | ||
678 | { } | ||
679 | }; | ||
680 | |||
681 | static struct platform_driver rpmh_driver = { | ||
682 | .probe = rpmh_rsc_probe, | ||
683 | .driver = { | ||
684 | .name = "rpmh", | ||
685 | .of_match_table = rpmh_drv_match, | ||
686 | }, | ||
687 | }; | ||
688 | |||
689 | static int __init rpmh_driver_init(void) | ||
690 | { | ||
691 | return platform_driver_register(&rpmh_driver); | ||
692 | } | ||
693 | arch_initcall(rpmh_driver_init); | ||
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c new file mode 100644 index 000000000000..c7beb6841289 --- /dev/null +++ b/drivers/soc/qcom/rpmh.c | |||
@@ -0,0 +1,513 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #include <linux/atomic.h> | ||
7 | #include <linux/bug.h> | ||
8 | #include <linux/interrupt.h> | ||
9 | #include <linux/jiffies.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/list.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/wait.h> | ||
19 | |||
20 | #include <soc/qcom/rpmh.h> | ||
21 | |||
22 | #include "rpmh-internal.h" | ||
23 | |||
24 | #define RPMH_TIMEOUT_MS msecs_to_jiffies(10000) | ||
25 | |||
26 | #define DEFINE_RPMH_MSG_ONSTACK(dev, s, q, name) \ | ||
27 | struct rpmh_request name = { \ | ||
28 | .msg = { \ | ||
29 | .state = s, \ | ||
30 | .cmds = name.cmd, \ | ||
31 | .num_cmds = 0, \ | ||
32 | .wait_for_compl = true, \ | ||
33 | }, \ | ||
34 | .cmd = { { 0 } }, \ | ||
35 | .completion = q, \ | ||
36 | .dev = dev, \ | ||
37 | .needs_free = false, \ | ||
38 | } | ||
39 | |||
40 | #define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client) | ||
41 | |||
42 | /** | ||
43 | * struct cache_req: the request object for caching | ||
44 | * | ||
45 | * @addr: the address of the resource | ||
46 | * @sleep_val: the sleep vote | ||
47 | * @wake_val: the wake vote | ||
48 | * @list: linked list obj | ||
49 | */ | ||
50 | struct cache_req { | ||
51 | u32 addr; | ||
52 | u32 sleep_val; | ||
53 | u32 wake_val; | ||
54 | struct list_head list; | ||
55 | }; | ||
56 | |||
57 | /** | ||
58 | * struct batch_cache_req - An entry in our batch catch | ||
59 | * | ||
60 | * @list: linked list obj | ||
61 | * @count: number of messages | ||
62 | * @rpm_msgs: the messages | ||
63 | */ | ||
64 | |||
65 | struct batch_cache_req { | ||
66 | struct list_head list; | ||
67 | int count; | ||
68 | struct rpmh_request rpm_msgs[]; | ||
69 | }; | ||
70 | |||
71 | static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) | ||
72 | { | ||
73 | struct rsc_drv *drv = dev_get_drvdata(dev->parent); | ||
74 | |||
75 | return &drv->client; | ||
76 | } | ||
77 | |||
78 | void rpmh_tx_done(const struct tcs_request *msg, int r) | ||
79 | { | ||
80 | struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request, | ||
81 | msg); | ||
82 | struct completion *compl = rpm_msg->completion; | ||
83 | |||
84 | rpm_msg->err = r; | ||
85 | |||
86 | if (r) | ||
87 | dev_err(rpm_msg->dev, "RPMH TX fail in msg addr=%#x, err=%d\n", | ||
88 | rpm_msg->msg.cmds[0].addr, r); | ||
89 | |||
90 | if (!compl) | ||
91 | goto exit; | ||
92 | |||
93 | /* Signal the blocking thread we are done */ | ||
94 | complete(compl); | ||
95 | |||
96 | exit: | ||
97 | if (rpm_msg->needs_free) | ||
98 | kfree(rpm_msg); | ||
99 | } | ||
100 | |||
101 | static struct cache_req *__find_req(struct rpmh_ctrlr *ctrlr, u32 addr) | ||
102 | { | ||
103 | struct cache_req *p, *req = NULL; | ||
104 | |||
105 | list_for_each_entry(p, &ctrlr->cache, list) { | ||
106 | if (p->addr == addr) { | ||
107 | req = p; | ||
108 | break; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | return req; | ||
113 | } | ||
114 | |||
115 | static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, | ||
116 | enum rpmh_state state, | ||
117 | struct tcs_cmd *cmd) | ||
118 | { | ||
119 | struct cache_req *req; | ||
120 | unsigned long flags; | ||
121 | |||
122 | spin_lock_irqsave(&ctrlr->cache_lock, flags); | ||
123 | req = __find_req(ctrlr, cmd->addr); | ||
124 | if (req) | ||
125 | goto existing; | ||
126 | |||
127 | req = kzalloc(sizeof(*req), GFP_ATOMIC); | ||
128 | if (!req) { | ||
129 | req = ERR_PTR(-ENOMEM); | ||
130 | goto unlock; | ||
131 | } | ||
132 | |||
133 | req->addr = cmd->addr; | ||
134 | req->sleep_val = req->wake_val = UINT_MAX; | ||
135 | INIT_LIST_HEAD(&req->list); | ||
136 | list_add_tail(&req->list, &ctrlr->cache); | ||
137 | |||
138 | existing: | ||
139 | switch (state) { | ||
140 | case RPMH_ACTIVE_ONLY_STATE: | ||
141 | if (req->sleep_val != UINT_MAX) | ||
142 | req->wake_val = cmd->data; | ||
143 | break; | ||
144 | case RPMH_WAKE_ONLY_STATE: | ||
145 | req->wake_val = cmd->data; | ||
146 | break; | ||
147 | case RPMH_SLEEP_STATE: | ||
148 | req->sleep_val = cmd->data; | ||
149 | break; | ||
150 | default: | ||
151 | break; | ||
152 | } | ||
153 | |||
154 | ctrlr->dirty = true; | ||
155 | unlock: | ||
156 | spin_unlock_irqrestore(&ctrlr->cache_lock, flags); | ||
157 | |||
158 | return req; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * __rpmh_write: Cache and send the RPMH request | ||
163 | * | ||
164 | * @dev: The device making the request | ||
165 | * @state: Active/Sleep request type | ||
166 | * @rpm_msg: The data that needs to be sent (cmds). | ||
167 | * | ||
168 | * Cache the RPMH request and send if the state is ACTIVE_ONLY. | ||
169 | * SLEEP/WAKE_ONLY requests are not sent to the controller at | ||
170 | * this time. Use rpmh_flush() to send them to the controller. | ||
171 | */ | ||
172 | static int __rpmh_write(const struct device *dev, enum rpmh_state state, | ||
173 | struct rpmh_request *rpm_msg) | ||
174 | { | ||
175 | struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | ||
176 | int ret = -EINVAL; | ||
177 | struct cache_req *req; | ||
178 | int i; | ||
179 | |||
180 | rpm_msg->msg.state = state; | ||
181 | |||
182 | /* Cache the request in our store and link the payload */ | ||
183 | for (i = 0; i < rpm_msg->msg.num_cmds; i++) { | ||
184 | req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]); | ||
185 | if (IS_ERR(req)) | ||
186 | return PTR_ERR(req); | ||
187 | } | ||
188 | |||
189 | rpm_msg->msg.state = state; | ||
190 | |||
191 | if (state == RPMH_ACTIVE_ONLY_STATE) { | ||
192 | WARN_ON(irqs_disabled()); | ||
193 | ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); | ||
194 | } else { | ||
195 | ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), | ||
196 | &rpm_msg->msg); | ||
197 | /* Clean up our call by spoofing tx_done */ | ||
198 | rpmh_tx_done(&rpm_msg->msg, ret); | ||
199 | } | ||
200 | |||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state, | ||
205 | const struct tcs_cmd *cmd, u32 n) | ||
206 | { | ||
207 | if (!cmd || !n || n > MAX_RPMH_PAYLOAD) | ||
208 | return -EINVAL; | ||
209 | |||
210 | memcpy(req->cmd, cmd, n * sizeof(*cmd)); | ||
211 | |||
212 | req->msg.state = state; | ||
213 | req->msg.cmds = req->cmd; | ||
214 | req->msg.num_cmds = n; | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * rpmh_write_async: Write a set of RPMH commands | ||
221 | * | ||
222 | * @dev: The device making the request | ||
223 | * @state: Active/sleep set | ||
224 | * @cmd: The payload data | ||
225 | * @n: The number of elements in payload | ||
226 | * | ||
227 | * Write a set of RPMH commands, the order of commands is maintained | ||
228 | * and will be sent as a single shot. | ||
229 | */ | ||
230 | int rpmh_write_async(const struct device *dev, enum rpmh_state state, | ||
231 | const struct tcs_cmd *cmd, u32 n) | ||
232 | { | ||
233 | struct rpmh_request *rpm_msg; | ||
234 | int ret; | ||
235 | |||
236 | rpm_msg = kzalloc(sizeof(*rpm_msg), GFP_ATOMIC); | ||
237 | if (!rpm_msg) | ||
238 | return -ENOMEM; | ||
239 | rpm_msg->needs_free = true; | ||
240 | |||
241 | ret = __fill_rpmh_msg(rpm_msg, state, cmd, n); | ||
242 | if (ret) { | ||
243 | kfree(rpm_msg); | ||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | return __rpmh_write(dev, state, rpm_msg); | ||
248 | } | ||
249 | EXPORT_SYMBOL(rpmh_write_async); | ||
250 | |||
251 | /** | ||
252 | * rpmh_write: Write a set of RPMH commands and block until response | ||
253 | * | ||
254 | * @rc: The RPMH handle got from rpmh_get_client | ||
255 | * @state: Active/sleep set | ||
256 | * @cmd: The payload data | ||
257 | * @n: The number of elements in @cmd | ||
258 | * | ||
259 | * May sleep. Do not call from atomic contexts. | ||
260 | */ | ||
261 | int rpmh_write(const struct device *dev, enum rpmh_state state, | ||
262 | const struct tcs_cmd *cmd, u32 n) | ||
263 | { | ||
264 | DECLARE_COMPLETION_ONSTACK(compl); | ||
265 | DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg); | ||
266 | int ret; | ||
267 | |||
268 | if (!cmd || !n || n > MAX_RPMH_PAYLOAD) | ||
269 | return -EINVAL; | ||
270 | |||
271 | memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd)); | ||
272 | rpm_msg.msg.num_cmds = n; | ||
273 | |||
274 | ret = __rpmh_write(dev, state, &rpm_msg); | ||
275 | if (ret) | ||
276 | return ret; | ||
277 | |||
278 | ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS); | ||
279 | WARN_ON(!ret); | ||
280 | return (ret > 0) ? 0 : -ETIMEDOUT; | ||
281 | } | ||
282 | EXPORT_SYMBOL(rpmh_write); | ||
283 | |||
284 | static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req) | ||
285 | { | ||
286 | unsigned long flags; | ||
287 | |||
288 | spin_lock_irqsave(&ctrlr->cache_lock, flags); | ||
289 | list_add_tail(&req->list, &ctrlr->batch_cache); | ||
290 | spin_unlock_irqrestore(&ctrlr->cache_lock, flags); | ||
291 | } | ||
292 | |||
293 | static int flush_batch(struct rpmh_ctrlr *ctrlr) | ||
294 | { | ||
295 | struct batch_cache_req *req; | ||
296 | const struct rpmh_request *rpm_msg; | ||
297 | unsigned long flags; | ||
298 | int ret = 0; | ||
299 | int i; | ||
300 | |||
301 | /* Send Sleep/Wake requests to the controller, expect no response */ | ||
302 | spin_lock_irqsave(&ctrlr->cache_lock, flags); | ||
303 | list_for_each_entry(req, &ctrlr->batch_cache, list) { | ||
304 | for (i = 0; i < req->count; i++) { | ||
305 | rpm_msg = req->rpm_msgs + i; | ||
306 | ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), | ||
307 | &rpm_msg->msg); | ||
308 | if (ret) | ||
309 | break; | ||
310 | } | ||
311 | } | ||
312 | spin_unlock_irqrestore(&ctrlr->cache_lock, flags); | ||
313 | |||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | static void invalidate_batch(struct rpmh_ctrlr *ctrlr) | ||
318 | { | ||
319 | struct batch_cache_req *req, *tmp; | ||
320 | unsigned long flags; | ||
321 | |||
322 | spin_lock_irqsave(&ctrlr->cache_lock, flags); | ||
323 | list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) | ||
324 | kfree(req); | ||
325 | INIT_LIST_HEAD(&ctrlr->batch_cache); | ||
326 | spin_unlock_irqrestore(&ctrlr->cache_lock, flags); | ||
327 | } | ||
328 | |||
329 | /** | ||
330 | * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the | ||
331 | * batch to finish. | ||
332 | * | ||
333 | * @dev: the device making the request | ||
334 | * @state: Active/sleep set | ||
335 | * @cmd: The payload data | ||
336 | * @n: The array of count of elements in each batch, 0 terminated. | ||
337 | * | ||
338 | * Write a request to the RSC controller without caching. If the request | ||
339 | * state is ACTIVE, then the requests are treated as completion request | ||
340 | * and sent to the controller immediately. The function waits until all the | ||
341 | * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the | ||
342 | * request is sent as fire-n-forget and no ack is expected. | ||
343 | * | ||
344 | * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests. | ||
345 | */ | ||
346 | int rpmh_write_batch(const struct device *dev, enum rpmh_state state, | ||
347 | const struct tcs_cmd *cmd, u32 *n) | ||
348 | { | ||
349 | struct batch_cache_req *req; | ||
350 | struct rpmh_request *rpm_msgs; | ||
351 | DECLARE_COMPLETION_ONSTACK(compl); | ||
352 | struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | ||
353 | unsigned long time_left; | ||
354 | int count = 0; | ||
355 | int ret, i, j; | ||
356 | |||
357 | if (!cmd || !n) | ||
358 | return -EINVAL; | ||
359 | |||
360 | while (n[count] > 0) | ||
361 | count++; | ||
362 | if (!count) | ||
363 | return -EINVAL; | ||
364 | |||
365 | req = kzalloc(sizeof(*req) + count * sizeof(req->rpm_msgs[0]), | ||
366 | GFP_ATOMIC); | ||
367 | if (!req) | ||
368 | return -ENOMEM; | ||
369 | req->count = count; | ||
370 | rpm_msgs = req->rpm_msgs; | ||
371 | |||
372 | for (i = 0; i < count; i++) { | ||
373 | __fill_rpmh_msg(rpm_msgs + i, state, cmd, n[i]); | ||
374 | cmd += n[i]; | ||
375 | } | ||
376 | |||
377 | if (state != RPMH_ACTIVE_ONLY_STATE) { | ||
378 | cache_batch(ctrlr, req); | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | for (i = 0; i < count; i++) { | ||
383 | rpm_msgs[i].completion = &compl; | ||
384 | ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msgs[i].msg); | ||
385 | if (ret) { | ||
386 | pr_err("Error(%d) sending RPMH message addr=%#x\n", | ||
387 | ret, rpm_msgs[i].msg.cmds[0].addr); | ||
388 | for (j = i; j < count; j++) | ||
389 | rpmh_tx_done(&rpm_msgs[j].msg, ret); | ||
390 | break; | ||
391 | } | ||
392 | } | ||
393 | |||
394 | time_left = RPMH_TIMEOUT_MS; | ||
395 | for (i = 0; i < count; i++) { | ||
396 | time_left = wait_for_completion_timeout(&compl, time_left); | ||
397 | if (!time_left) { | ||
398 | /* | ||
399 | * Better hope they never finish because they'll signal | ||
400 | * the completion on our stack and that's bad once | ||
401 | * we've returned from the function. | ||
402 | */ | ||
403 | WARN_ON(1); | ||
404 | ret = -ETIMEDOUT; | ||
405 | goto exit; | ||
406 | } | ||
407 | } | ||
408 | |||
409 | exit: | ||
410 | kfree(req); | ||
411 | |||
412 | return ret; | ||
413 | } | ||
414 | EXPORT_SYMBOL(rpmh_write_batch); | ||
415 | |||
416 | static int is_req_valid(struct cache_req *req) | ||
417 | { | ||
418 | return (req->sleep_val != UINT_MAX && | ||
419 | req->wake_val != UINT_MAX && | ||
420 | req->sleep_val != req->wake_val); | ||
421 | } | ||
422 | |||
423 | static int send_single(const struct device *dev, enum rpmh_state state, | ||
424 | u32 addr, u32 data) | ||
425 | { | ||
426 | DEFINE_RPMH_MSG_ONSTACK(dev, state, NULL, rpm_msg); | ||
427 | struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | ||
428 | |||
429 | /* Wake sets are always complete and sleep sets are not */ | ||
430 | rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE); | ||
431 | rpm_msg.cmd[0].addr = addr; | ||
432 | rpm_msg.cmd[0].data = data; | ||
433 | rpm_msg.msg.num_cmds = 1; | ||
434 | |||
435 | return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg); | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * rpmh_flush: Flushes the buffered active and sleep sets to TCS | ||
440 | * | ||
441 | * @dev: The device making the request | ||
442 | * | ||
443 | * Return: -EBUSY if the controller is busy, probably waiting on a response | ||
444 | * to a RPMH request sent earlier. | ||
445 | * | ||
446 | * This function is always called from the sleep code from the last CPU | ||
447 | * that is powering down the entire system. Since no other RPMH API would be | ||
448 | * executing at this time, it is safe to run lockless. | ||
449 | */ | ||
450 | int rpmh_flush(const struct device *dev) | ||
451 | { | ||
452 | struct cache_req *p; | ||
453 | struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | ||
454 | int ret; | ||
455 | |||
456 | if (!ctrlr->dirty) { | ||
457 | pr_debug("Skipping flush, TCS has latest data.\n"); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /* First flush the cached batch requests */ | ||
462 | ret = flush_batch(ctrlr); | ||
463 | if (ret) | ||
464 | return ret; | ||
465 | |||
466 | /* | ||
467 | * Nobody else should be calling this function other than system PM, | ||
468 | * hence we can run without locks. | ||
469 | */ | ||
470 | list_for_each_entry(p, &ctrlr->cache, list) { | ||
471 | if (!is_req_valid(p)) { | ||
472 | pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x", | ||
473 | __func__, p->addr, p->sleep_val, p->wake_val); | ||
474 | continue; | ||
475 | } | ||
476 | ret = send_single(dev, RPMH_SLEEP_STATE, p->addr, p->sleep_val); | ||
477 | if (ret) | ||
478 | return ret; | ||
479 | ret = send_single(dev, RPMH_WAKE_ONLY_STATE, | ||
480 | p->addr, p->wake_val); | ||
481 | if (ret) | ||
482 | return ret; | ||
483 | } | ||
484 | |||
485 | ctrlr->dirty = false; | ||
486 | |||
487 | return 0; | ||
488 | } | ||
489 | EXPORT_SYMBOL(rpmh_flush); | ||
490 | |||
491 | /** | ||
492 | * rpmh_invalidate: Invalidate all sleep and active sets | ||
493 | * sets. | ||
494 | * | ||
495 | * @dev: The device making the request | ||
496 | * | ||
497 | * Invalidate the sleep and active values in the TCS blocks. | ||
498 | */ | ||
499 | int rpmh_invalidate(const struct device *dev) | ||
500 | { | ||
501 | struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | ||
502 | int ret; | ||
503 | |||
504 | invalidate_batch(ctrlr); | ||
505 | ctrlr->dirty = true; | ||
506 | |||
507 | do { | ||
508 | ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); | ||
509 | } while (ret == -EAGAIN); | ||
510 | |||
511 | return ret; | ||
512 | } | ||
513 | EXPORT_SYMBOL(rpmh_invalidate); | ||
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 70b2ee80d6bd..bf4bd71ab53f 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c | |||
@@ -364,11 +364,6 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, | |||
364 | end = phdr_to_last_uncached_entry(phdr); | 364 | end = phdr_to_last_uncached_entry(phdr); |
365 | cached = phdr_to_last_cached_entry(phdr); | 365 | cached = phdr_to_last_cached_entry(phdr); |
366 | 366 | ||
367 | if (smem->global_partition) { | ||
368 | dev_err(smem->dev, "Already found the global partition\n"); | ||
369 | return -EINVAL; | ||
370 | } | ||
371 | |||
372 | while (hdr < end) { | 367 | while (hdr < end) { |
373 | if (hdr->canary != SMEM_PRIVATE_CANARY) | 368 | if (hdr->canary != SMEM_PRIVATE_CANARY) |
374 | goto bad_canary; | 369 | goto bad_canary; |
@@ -736,6 +731,11 @@ static int qcom_smem_set_global_partition(struct qcom_smem *smem) | |||
736 | bool found = false; | 731 | bool found = false; |
737 | int i; | 732 | int i; |
738 | 733 | ||
734 | if (smem->global_partition) { | ||
735 | dev_err(smem->dev, "Already found the global partition\n"); | ||
736 | return -EINVAL; | ||
737 | } | ||
738 | |||
739 | ptable = qcom_smem_get_ptable(smem); | 739 | ptable = qcom_smem_get_ptable(smem); |
740 | if (IS_ERR(ptable)) | 740 | if (IS_ERR(ptable)) |
741 | return PTR_ERR(ptable); | 741 | return PTR_ERR(ptable); |
diff --git a/drivers/soc/qcom/trace-rpmh.h b/drivers/soc/qcom/trace-rpmh.h new file mode 100644 index 000000000000..feb0cb455e37 --- /dev/null +++ b/drivers/soc/qcom/trace-rpmh.h | |||
@@ -0,0 +1,82 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #if !defined(_TRACE_RPMH_H) || defined(TRACE_HEADER_MULTI_READ) | ||
7 | #define _TRACE_RPMH_H | ||
8 | |||
9 | #undef TRACE_SYSTEM | ||
10 | #define TRACE_SYSTEM rpmh | ||
11 | |||
12 | #include <linux/tracepoint.h> | ||
13 | #include "rpmh-internal.h" | ||
14 | |||
15 | TRACE_EVENT(rpmh_tx_done, | ||
16 | |||
17 | TP_PROTO(struct rsc_drv *d, int m, const struct tcs_request *r, int e), | ||
18 | |||
19 | TP_ARGS(d, m, r, e), | ||
20 | |||
21 | TP_STRUCT__entry( | ||
22 | __string(name, d->name) | ||
23 | __field(int, m) | ||
24 | __field(u32, addr) | ||
25 | __field(u32, data) | ||
26 | __field(int, err) | ||
27 | ), | ||
28 | |||
29 | TP_fast_assign( | ||
30 | __assign_str(name, d->name); | ||
31 | __entry->m = m; | ||
32 | __entry->addr = r->cmds[0].addr; | ||
33 | __entry->data = r->cmds[0].data; | ||
34 | __entry->err = e; | ||
35 | ), | ||
36 | |||
37 | TP_printk("%s: ack: tcs-m: %d addr: %#x data: %#x errno: %d", | ||
38 | __get_str(name), __entry->m, __entry->addr, __entry->data, | ||
39 | __entry->err) | ||
40 | ); | ||
41 | |||
42 | TRACE_EVENT(rpmh_send_msg, | ||
43 | |||
44 | TP_PROTO(struct rsc_drv *d, int m, int n, u32 h, | ||
45 | const struct tcs_cmd *c), | ||
46 | |||
47 | TP_ARGS(d, m, n, h, c), | ||
48 | |||
49 | TP_STRUCT__entry( | ||
50 | __string(name, d->name) | ||
51 | __field(int, m) | ||
52 | __field(int, n) | ||
53 | __field(u32, hdr) | ||
54 | __field(u32, addr) | ||
55 | __field(u32, data) | ||
56 | __field(bool, wait) | ||
57 | ), | ||
58 | |||
59 | TP_fast_assign( | ||
60 | __assign_str(name, d->name); | ||
61 | __entry->m = m; | ||
62 | __entry->n = n; | ||
63 | __entry->hdr = h; | ||
64 | __entry->addr = c->addr; | ||
65 | __entry->data = c->data; | ||
66 | __entry->wait = c->wait; | ||
67 | ), | ||
68 | |||
69 | TP_printk("%s: send-msg: tcs(m): %d cmd(n): %d msgid: %#x addr: %#x data: %#x complete: %d", | ||
70 | __get_str(name), __entry->m, __entry->n, __entry->hdr, | ||
71 | __entry->addr, __entry->data, __entry->wait) | ||
72 | ); | ||
73 | |||
74 | #endif /* _TRACE_RPMH_H */ | ||
75 | |||
76 | #undef TRACE_INCLUDE_PATH | ||
77 | #define TRACE_INCLUDE_PATH . | ||
78 | |||
79 | #undef TRACE_INCLUDE_FILE | ||
80 | #define TRACE_INCLUDE_FILE trace-rpmh | ||
81 | |||
82 | #include <trace/define_trace.h> | ||
diff --git a/include/dt-bindings/regulator/maxim,max77802.h b/include/dt-bindings/regulator/maxim,max77802.h index cf28631d7109..d0baba1973d4 100644 --- a/include/dt-bindings/regulator/maxim,max77802.h +++ b/include/dt-bindings/regulator/maxim,max77802.h | |||
@@ -1,10 +1,7 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
1 | /* | 2 | /* |
2 | * Copyright (C) 2014 Google, Inc | 3 | * Copyright (C) 2014 Google, Inc |
3 | * | 4 | * |
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * Device Tree binding constants for the Maxim 77802 PMIC regulators | 5 | * Device Tree binding constants for the Maxim 77802 PMIC regulators |
9 | */ | 6 | */ |
10 | 7 | ||
diff --git a/include/dt-bindings/regulator/qcom,rpmh-regulator.h b/include/dt-bindings/regulator/qcom,rpmh-regulator.h new file mode 100644 index 000000000000..86713dcf9e02 --- /dev/null +++ b/include/dt-bindings/regulator/qcom,rpmh-regulator.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* Copyright (c) 2018, The Linux Foundation. All rights reserved. */ | ||
3 | |||
4 | #ifndef __QCOM_RPMH_REGULATOR_H | ||
5 | #define __QCOM_RPMH_REGULATOR_H | ||
6 | |||
7 | /* | ||
8 | * These mode constants may be used to specify modes for various RPMh regulator | ||
9 | * device tree properties (e.g. regulator-initial-mode). Each type of regulator | ||
10 | * supports a subset of the possible modes. | ||
11 | * | ||
12 | * %RPMH_REGULATOR_MODE_RET: Retention mode in which only an extremely small | ||
13 | * load current is allowed. This mode is supported | ||
14 | * by LDO and SMPS type regulators. | ||
15 | * %RPMH_REGULATOR_MODE_LPM: Low power mode in which a small load current is | ||
16 | * allowed. This mode corresponds to PFM for SMPS | ||
17 | * and BOB type regulators. This mode is supported | ||
18 | * by LDO, HFSMPS, BOB, and PMIC4 FTSMPS type | ||
19 | * regulators. | ||
20 | * %RPMH_REGULATOR_MODE_AUTO: Auto mode in which the regulator hardware | ||
21 | * automatically switches between LPM and HPM based | ||
22 | * upon the real-time load current. This mode is | ||
23 | * supported by HFSMPS, BOB, and PMIC4 FTSMPS type | ||
24 | * regulators. | ||
25 | * %RPMH_REGULATOR_MODE_HPM: High power mode in which the full rated current | ||
26 | * of the regulator is allowed. This mode | ||
27 | * corresponds to PWM for SMPS and BOB type | ||
28 | * regulators. This mode is supported by all types | ||
29 | * of regulators. | ||
30 | */ | ||
31 | #define RPMH_REGULATOR_MODE_RET 0 | ||
32 | #define RPMH_REGULATOR_MODE_LPM 1 | ||
33 | #define RPMH_REGULATOR_MODE_AUTO 2 | ||
34 | #define RPMH_REGULATOR_MODE_HPM 3 | ||
35 | |||
36 | #endif | ||
diff --git a/include/dt-bindings/soc/qcom,rpmh-rsc.h b/include/dt-bindings/soc/qcom,rpmh-rsc.h new file mode 100644 index 000000000000..868f998ea998 --- /dev/null +++ b/include/dt-bindings/soc/qcom,rpmh-rsc.h | |||
@@ -0,0 +1,14 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #ifndef __DT_QCOM_RPMH_RSC_H__ | ||
7 | #define __DT_QCOM_RPMH_RSC_H__ | ||
8 | |||
9 | #define SLEEP_TCS 0 | ||
10 | #define WAKE_TCS 1 | ||
11 | #define ACTIVE_TCS 2 | ||
12 | #define CONTROL_TCS 3 | ||
13 | |||
14 | #endif /* __DT_QCOM_RPMH_RSC_H__ */ | ||
diff --git a/include/linux/device.h b/include/linux/device.h index 055a69dbcd18..9c1c3b1d5e11 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
@@ -1316,6 +1316,7 @@ extern const char *dev_driver_string(const struct device *dev); | |||
1316 | struct device_link *device_link_add(struct device *consumer, | 1316 | struct device_link *device_link_add(struct device *consumer, |
1317 | struct device *supplier, u32 flags); | 1317 | struct device *supplier, u32 flags); |
1318 | void device_link_del(struct device_link *link); | 1318 | void device_link_del(struct device_link *link); |
1319 | void device_link_remove(void *consumer, struct device *supplier); | ||
1319 | 1320 | ||
1320 | #ifdef CONFIG_PRINTK | 1321 | #ifdef CONFIG_PRINTK |
1321 | 1322 | ||
diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index b401b962afff..5d65521260b3 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h | |||
@@ -87,6 +87,10 @@ static inline int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, | |||
87 | static inline int | 87 | static inline int |
88 | qcom_scm_pas_auth_and_reset(u32 peripheral) { return -ENODEV; } | 88 | qcom_scm_pas_auth_and_reset(u32 peripheral) { return -ENODEV; } |
89 | static inline int qcom_scm_pas_shutdown(u32 peripheral) { return -ENODEV; } | 89 | static inline int qcom_scm_pas_shutdown(u32 peripheral) { return -ENODEV; } |
90 | static inline int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, | ||
91 | unsigned int *src, | ||
92 | struct qcom_scm_vmperm *newvm, | ||
93 | int dest_cnt) { return -ENODEV; } | ||
90 | static inline void qcom_scm_cpu_power_down(u32 flags) {} | 94 | static inline void qcom_scm_cpu_power_down(u32 flags) {} |
91 | static inline u32 qcom_scm_get_version(void) { return 0; } | 95 | static inline u32 qcom_scm_get_version(void) { return 0; } |
92 | static inline u32 | 96 | static inline u32 |
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index fc2dc8df476f..0fd8fbb74763 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h | |||
@@ -46,7 +46,7 @@ enum regulator_status { | |||
46 | /** | 46 | /** |
47 | * struct regulator_linear_range - specify linear voltage ranges | 47 | * struct regulator_linear_range - specify linear voltage ranges |
48 | * | 48 | * |
49 | * Specify a range of voltages for regulator_map_linar_range() and | 49 | * Specify a range of voltages for regulator_map_linear_range() and |
50 | * regulator_list_linear_range(). | 50 | * regulator_list_linear_range(). |
51 | * | 51 | * |
52 | * @min_uV: Lowest voltage in range | 52 | * @min_uV: Lowest voltage in range |
@@ -220,7 +220,7 @@ struct regulator_ops { | |||
220 | /* set regulator suspend operating mode (defined in consumer.h) */ | 220 | /* set regulator suspend operating mode (defined in consumer.h) */ |
221 | int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode); | 221 | int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode); |
222 | 222 | ||
223 | int (*resume_early)(struct regulator_dev *rdev); | 223 | int (*resume)(struct regulator_dev *rdev); |
224 | 224 | ||
225 | int (*set_pull_down) (struct regulator_dev *); | 225 | int (*set_pull_down) (struct regulator_dev *); |
226 | }; | 226 | }; |
diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h index e0ccf46f66cf..cb5aecd40f07 100644 --- a/include/linux/regulator/pfuze100.h +++ b/include/linux/regulator/pfuze100.h | |||
@@ -64,6 +64,17 @@ | |||
64 | #define PFUZE3000_VLDO3 11 | 64 | #define PFUZE3000_VLDO3 11 |
65 | #define PFUZE3000_VLDO4 12 | 65 | #define PFUZE3000_VLDO4 12 |
66 | 66 | ||
67 | #define PFUZE3001_SW1 0 | ||
68 | #define PFUZE3001_SW2 1 | ||
69 | #define PFUZE3001_SW3 2 | ||
70 | #define PFUZE3001_VSNVS 3 | ||
71 | #define PFUZE3001_VLDO1 4 | ||
72 | #define PFUZE3001_VLDO2 5 | ||
73 | #define PFUZE3001_VCCSD 6 | ||
74 | #define PFUZE3001_V33 7 | ||
75 | #define PFUZE3001_VLDO3 8 | ||
76 | #define PFUZE3001_VLDO4 9 | ||
77 | |||
67 | struct regulator_init_data; | 78 | struct regulator_init_data; |
68 | 79 | ||
69 | struct pfuze_regulator_platform_data { | 80 | struct pfuze_regulator_platform_data { |
diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h new file mode 100644 index 000000000000..7e3b9c605ab2 --- /dev/null +++ b/include/linux/soc/qcom/llcc-qcom.h | |||
@@ -0,0 +1,180 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | #include <linux/platform_device.h> | ||
8 | #ifndef __LLCC_QCOM__ | ||
9 | #define __LLCC_QCOM__ | ||
10 | |||
11 | #define LLCC_CPUSS 1 | ||
12 | #define LLCC_VIDSC0 2 | ||
13 | #define LLCC_VIDSC1 3 | ||
14 | #define LLCC_ROTATOR 4 | ||
15 | #define LLCC_VOICE 5 | ||
16 | #define LLCC_AUDIO 6 | ||
17 | #define LLCC_MDMHPGRW 7 | ||
18 | #define LLCC_MDM 8 | ||
19 | #define LLCC_CMPT 10 | ||
20 | #define LLCC_GPUHTW 11 | ||
21 | #define LLCC_GPU 12 | ||
22 | #define LLCC_MMUHWT 13 | ||
23 | #define LLCC_CMPTDMA 15 | ||
24 | #define LLCC_DISP 16 | ||
25 | #define LLCC_VIDFW 17 | ||
26 | #define LLCC_MDMHPFX 20 | ||
27 | #define LLCC_MDMPNG 21 | ||
28 | #define LLCC_AUDHW 22 | ||
29 | |||
30 | /** | ||
31 | * llcc_slice_desc - Cache slice descriptor | ||
32 | * @slice_id: llcc slice id | ||
33 | * @slice_size: Size allocated for the llcc slice | ||
34 | */ | ||
35 | struct llcc_slice_desc { | ||
36 | u32 slice_id; | ||
37 | size_t slice_size; | ||
38 | }; | ||
39 | |||
40 | /** | ||
41 | * llcc_slice_config - Data associated with the llcc slice | ||
42 | * @usecase_id: usecase id for which the llcc slice is used | ||
43 | * @slice_id: llcc slice id assigned to each slice | ||
44 | * @max_cap: maximum capacity of the llcc slice | ||
45 | * @priority: priority of the llcc slice | ||
46 | * @fixed_size: whether the llcc slice can grow beyond its size | ||
47 | * @bonus_ways: bonus ways associated with llcc slice | ||
48 | * @res_ways: reserved ways associated with llcc slice | ||
49 | * @cache_mode: mode of the llcc slice | ||
50 | * @probe_target_ways: Probe only reserved and bonus ways on a cache miss | ||
51 | * @dis_cap_alloc: Disable capacity based allocation | ||
52 | * @retain_on_pc: Retain through power collapse | ||
53 | * @activate_on_init: activate the slice on init | ||
54 | */ | ||
55 | struct llcc_slice_config { | ||
56 | u32 usecase_id; | ||
57 | u32 slice_id; | ||
58 | u32 max_cap; | ||
59 | u32 priority; | ||
60 | bool fixed_size; | ||
61 | u32 bonus_ways; | ||
62 | u32 res_ways; | ||
63 | u32 cache_mode; | ||
64 | u32 probe_target_ways; | ||
65 | bool dis_cap_alloc; | ||
66 | bool retain_on_pc; | ||
67 | bool activate_on_init; | ||
68 | }; | ||
69 | |||
70 | /** | ||
71 | * llcc_drv_data - Data associated with the llcc driver | ||
72 | * @regmap: regmap associated with the llcc device | ||
73 | * @cfg: pointer to the data structure for slice configuration | ||
74 | * @lock: mutex associated with each slice | ||
75 | * @cfg_size: size of the config data table | ||
76 | * @max_slices: max slices as read from device tree | ||
77 | * @bcast_off: Offset of the broadcast bank | ||
78 | * @num_banks: Number of llcc banks | ||
79 | * @bitmap: Bit map to track the active slice ids | ||
80 | * @offsets: Pointer to the bank offsets array | ||
81 | */ | ||
82 | struct llcc_drv_data { | ||
83 | struct regmap *regmap; | ||
84 | const struct llcc_slice_config *cfg; | ||
85 | struct mutex lock; | ||
86 | u32 cfg_size; | ||
87 | u32 max_slices; | ||
88 | u32 bcast_off; | ||
89 | u32 num_banks; | ||
90 | unsigned long *bitmap; | ||
91 | u32 *offsets; | ||
92 | }; | ||
93 | |||
94 | #if IS_ENABLED(CONFIG_QCOM_LLCC) | ||
95 | /** | ||
96 | * llcc_slice_getd - get llcc slice descriptor | ||
97 | * @uid: usecase_id of the client | ||
98 | */ | ||
99 | struct llcc_slice_desc *llcc_slice_getd(u32 uid); | ||
100 | |||
101 | /** | ||
102 | * llcc_slice_putd - llcc slice descritpor | ||
103 | * @desc: Pointer to llcc slice descriptor | ||
104 | */ | ||
105 | void llcc_slice_putd(struct llcc_slice_desc *desc); | ||
106 | |||
107 | /** | ||
108 | * llcc_get_slice_id - get slice id | ||
109 | * @desc: Pointer to llcc slice descriptor | ||
110 | */ | ||
111 | int llcc_get_slice_id(struct llcc_slice_desc *desc); | ||
112 | |||
113 | /** | ||
114 | * llcc_get_slice_size - llcc slice size | ||
115 | * @desc: Pointer to llcc slice descriptor | ||
116 | */ | ||
117 | size_t llcc_get_slice_size(struct llcc_slice_desc *desc); | ||
118 | |||
119 | /** | ||
120 | * llcc_slice_activate - Activate the llcc slice | ||
121 | * @desc: Pointer to llcc slice descriptor | ||
122 | */ | ||
123 | int llcc_slice_activate(struct llcc_slice_desc *desc); | ||
124 | |||
125 | /** | ||
126 | * llcc_slice_deactivate - Deactivate the llcc slice | ||
127 | * @desc: Pointer to llcc slice descriptor | ||
128 | */ | ||
129 | int llcc_slice_deactivate(struct llcc_slice_desc *desc); | ||
130 | |||
131 | /** | ||
132 | * qcom_llcc_probe - program the sct table | ||
133 | * @pdev: platform device pointer | ||
134 | * @table: soc sct table | ||
135 | * @sz: Size of the config table | ||
136 | */ | ||
137 | int qcom_llcc_probe(struct platform_device *pdev, | ||
138 | const struct llcc_slice_config *table, u32 sz); | ||
139 | #else | ||
140 | static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid) | ||
141 | { | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | static inline void llcc_slice_putd(struct llcc_slice_desc *desc) | ||
146 | { | ||
147 | |||
148 | }; | ||
149 | |||
150 | static inline int llcc_get_slice_id(struct llcc_slice_desc *desc) | ||
151 | { | ||
152 | return -EINVAL; | ||
153 | } | ||
154 | |||
155 | static inline size_t llcc_get_slice_size(struct llcc_slice_desc *desc) | ||
156 | { | ||
157 | return 0; | ||
158 | } | ||
159 | static inline int llcc_slice_activate(struct llcc_slice_desc *desc) | ||
160 | { | ||
161 | return -EINVAL; | ||
162 | } | ||
163 | |||
164 | static inline int llcc_slice_deactivate(struct llcc_slice_desc *desc) | ||
165 | { | ||
166 | return -EINVAL; | ||
167 | } | ||
168 | static inline int qcom_llcc_probe(struct platform_device *pdev, | ||
169 | const struct llcc_slice_config *table, u32 sz) | ||
170 | { | ||
171 | return -ENODEV; | ||
172 | } | ||
173 | |||
174 | static inline int qcom_llcc_remove(struct platform_device *pdev) | ||
175 | { | ||
176 | return -ENODEV; | ||
177 | } | ||
178 | #endif | ||
179 | |||
180 | #endif | ||
diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h new file mode 100644 index 000000000000..619e07c75da9 --- /dev/null +++ b/include/soc/qcom/rpmh.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #ifndef __SOC_QCOM_RPMH_H__ | ||
7 | #define __SOC_QCOM_RPMH_H__ | ||
8 | |||
9 | #include <soc/qcom/tcs.h> | ||
10 | #include <linux/platform_device.h> | ||
11 | |||
12 | |||
13 | #if IS_ENABLED(CONFIG_QCOM_RPMH) | ||
14 | int rpmh_write(const struct device *dev, enum rpmh_state state, | ||
15 | const struct tcs_cmd *cmd, u32 n); | ||
16 | |||
17 | int rpmh_write_async(const struct device *dev, enum rpmh_state state, | ||
18 | const struct tcs_cmd *cmd, u32 n); | ||
19 | |||
20 | int rpmh_write_batch(const struct device *dev, enum rpmh_state state, | ||
21 | const struct tcs_cmd *cmd, u32 *n); | ||
22 | |||
23 | int rpmh_flush(const struct device *dev); | ||
24 | |||
25 | int rpmh_invalidate(const struct device *dev); | ||
26 | |||
27 | #else | ||
28 | |||
29 | static inline int rpmh_write(const struct device *dev, enum rpmh_state state, | ||
30 | const struct tcs_cmd *cmd, u32 n) | ||
31 | { return -ENODEV; } | ||
32 | |||
33 | static inline int rpmh_write_async(const struct device *dev, | ||
34 | enum rpmh_state state, | ||
35 | const struct tcs_cmd *cmd, u32 n) | ||
36 | { return -ENODEV; } | ||
37 | |||
38 | static inline int rpmh_write_batch(const struct device *dev, | ||
39 | enum rpmh_state state, | ||
40 | const struct tcs_cmd *cmd, u32 *n) | ||
41 | { return -ENODEV; } | ||
42 | |||
43 | static inline int rpmh_flush(const struct device *dev) | ||
44 | { return -ENODEV; } | ||
45 | |||
46 | static inline int rpmh_invalidate(const struct device *dev) | ||
47 | { return -ENODEV; } | ||
48 | |||
49 | #endif /* CONFIG_QCOM_RPMH */ | ||
50 | |||
51 | #endif /* __SOC_QCOM_RPMH_H__ */ | ||
diff --git a/include/soc/qcom/tcs.h b/include/soc/qcom/tcs.h new file mode 100644 index 000000000000..262876a59e86 --- /dev/null +++ b/include/soc/qcom/tcs.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #ifndef __SOC_QCOM_TCS_H__ | ||
7 | #define __SOC_QCOM_TCS_H__ | ||
8 | |||
9 | #define MAX_RPMH_PAYLOAD 16 | ||
10 | |||
11 | /** | ||
12 | * rpmh_state: state for the request | ||
13 | * | ||
14 | * RPMH_SLEEP_STATE: State of the resource when the processor subsystem | ||
15 | * is powered down. There is no client using the | ||
16 | * resource actively. | ||
17 | * RPMH_WAKE_ONLY_STATE: Resume resource state to the value previously | ||
18 | * requested before the processor was powered down. | ||
19 | * RPMH_ACTIVE_ONLY_STATE: Active or AMC mode requests. Resource state | ||
20 | * is aggregated immediately. | ||
21 | */ | ||
22 | enum rpmh_state { | ||
23 | RPMH_SLEEP_STATE, | ||
24 | RPMH_WAKE_ONLY_STATE, | ||
25 | RPMH_ACTIVE_ONLY_STATE, | ||
26 | }; | ||
27 | |||
28 | /** | ||
29 | * struct tcs_cmd: an individual request to RPMH. | ||
30 | * | ||
31 | * @addr: the address of the resource slv_id:18:16 | offset:0:15 | ||
32 | * @data: the resource state request | ||
33 | * @wait: wait for this request to be complete before sending the next | ||
34 | */ | ||
35 | struct tcs_cmd { | ||
36 | u32 addr; | ||
37 | u32 data; | ||
38 | u32 wait; | ||
39 | }; | ||
40 | |||
41 | /** | ||
42 | * struct tcs_request: A set of tcs_cmds sent together in a TCS | ||
43 | * | ||
44 | * @state: state for the request. | ||
45 | * @wait_for_compl: wait until we get a response from the h/w accelerator | ||
46 | * @num_cmds: the number of @cmds in this request | ||
47 | * @cmds: an array of tcs_cmds | ||
48 | */ | ||
49 | struct tcs_request { | ||
50 | enum rpmh_state state; | ||
51 | u32 wait_for_compl; | ||
52 | u32 num_cmds; | ||
53 | struct tcs_cmd *cmds; | ||
54 | }; | ||
55 | |||
56 | #endif /* __SOC_QCOM_TCS_H__ */ | ||