diff options
126 files changed, 7122 insertions, 744 deletions
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt index cef5ee83fbc1..72d481c8dd48 100644 --- a/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt +++ b/Documentation/devicetree/bindings/arm/freescale/fsl,scu.txt | |||
@@ -58,7 +58,11 @@ This binding for the SCU power domain providers uses the generic power | |||
58 | domain binding[2]. | 58 | domain binding[2]. |
59 | 59 | ||
60 | Required properties: | 60 | Required properties: |
61 | - compatible: Should be "fsl,imx8qxp-scu-pd". | 61 | - compatible: Should be one of: |
62 | "fsl,imx8qm-scu-pd", | ||
63 | "fsl,imx8qxp-scu-pd" | ||
64 | followed by "fsl,scu-pd" | ||
65 | |||
62 | - #power-domain-cells: Must be 1. Contains the Resource ID used by | 66 | - #power-domain-cells: Must be 1. Contains the Resource ID used by |
63 | SCU commands. | 67 | SCU commands. |
64 | See detailed Resource ID list from: | 68 | See detailed Resource ID list from: |
@@ -157,7 +161,7 @@ firmware { | |||
157 | }; | 161 | }; |
158 | 162 | ||
159 | pd: imx8qx-pd { | 163 | pd: imx8qx-pd { |
160 | compatible = "fsl,imx8qxp-scu-pd"; | 164 | compatible = "fsl,imx8qxp-scu-pd", "fsl,scu-pd"; |
161 | #power-domain-cells = <1>; | 165 | #power-domain-cells = <1>; |
162 | }; | 166 | }; |
163 | 167 | ||
diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt index 683eaf3aed79..dda7d6d66479 100644 --- a/Documentation/devicetree/bindings/bus/imx-weim.txt +++ b/Documentation/devicetree/bindings/bus/imx-weim.txt | |||
@@ -47,9 +47,9 @@ Optional properties: | |||
47 | Timing property for child nodes. It is mandatory, not optional. | 47 | Timing property for child nodes. It is mandatory, not optional. |
48 | 48 | ||
49 | - fsl,weim-cs-timing: The timing array, contains timing values for the | 49 | - fsl,weim-cs-timing: The timing array, contains timing values for the |
50 | child node. We can get the CS index from the child | 50 | child node. We get the CS indexes from the address |
51 | node's "reg" property. The number of registers depends | 51 | ranges in the child node's "reg" property. |
52 | on the selected chip. | 52 | The number of registers depends on the selected chip: |
53 | For i.MX1, i.MX21 ("fsl,imx1-weim") there are two | 53 | For i.MX1, i.MX21 ("fsl,imx1-weim") there are two |
54 | registers: CSxU, CSxL. | 54 | registers: CSxU, CSxL. |
55 | For i.MX25, i.MX27, i.MX31 and i.MX35 ("fsl,imx27-weim") | 55 | For i.MX25, i.MX27, i.MX31 and i.MX35 ("fsl,imx27-weim") |
@@ -80,3 +80,29 @@ Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM: | |||
80 | 0x0000c000 0x1404a38e 0x00000000>; | 80 | 0x0000c000 0x1404a38e 0x00000000>; |
81 | }; | 81 | }; |
82 | }; | 82 | }; |
83 | |||
84 | Example for an imx6q-based board, a multi-chipselect device connected to WEIM: | ||
85 | |||
86 | In this case, both chip select 0 and 1 will be configured with the same timing | ||
87 | array values. | ||
88 | |||
89 | weim: weim@21b8000 { | ||
90 | compatible = "fsl,imx6q-weim"; | ||
91 | reg = <0x021b8000 0x4000>; | ||
92 | clocks = <&clks 196>; | ||
93 | #address-cells = <2>; | ||
94 | #size-cells = <1>; | ||
95 | ranges = <0 0 0x08000000 0x02000000 | ||
96 | 1 0 0x0a000000 0x02000000 | ||
97 | 2 0 0x0c000000 0x02000000 | ||
98 | 3 0 0x0e000000 0x02000000>; | ||
99 | fsl,weim-cs-gpr = <&gpr>; | ||
100 | |||
101 | acme@0 { | ||
102 | compatible = "acme,whatever"; | ||
103 | reg = <0 0 0x100>, <0 0x400000 0x800>, | ||
104 | <1 0x400000 0x800>; | ||
105 | fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100 | ||
106 | 0x00000000 0xa0000240 0x00000000>; | ||
107 | }; | ||
108 | }; | ||
diff --git a/Documentation/devicetree/bindings/nvmem/xlnx,zynqmp-nvmem.txt b/Documentation/devicetree/bindings/nvmem/xlnx,zynqmp-nvmem.txt new file mode 100644 index 000000000000..4881561b3a02 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/xlnx,zynqmp-nvmem.txt | |||
@@ -0,0 +1,46 @@ | |||
1 | -------------------------------------------------------------------------- | ||
2 | = Zynq UltraScale+ MPSoC nvmem firmware driver binding = | ||
3 | -------------------------------------------------------------------------- | ||
4 | The nvmem_firmware node provides access to the hardware related data | ||
5 | like soc revision, IDCODE... etc, By using the firmware interface. | ||
6 | |||
7 | Required properties: | ||
8 | - compatible: should be "xlnx,zynqmp-nvmem-fw" | ||
9 | |||
10 | = Data cells = | ||
11 | Are child nodes of silicon id, bindings of which as described in | ||
12 | bindings/nvmem/nvmem.txt | ||
13 | |||
14 | ------- | ||
15 | Example | ||
16 | ------- | ||
17 | firmware { | ||
18 | zynqmp_firmware: zynqmp-firmware { | ||
19 | compatible = "xlnx,zynqmp-firmware"; | ||
20 | method = "smc"; | ||
21 | |||
22 | nvmem_firmware { | ||
23 | compatible = "xlnx,zynqmp-nvmem-fw"; | ||
24 | #address-cells = <1>; | ||
25 | #size-cells = <1>; | ||
26 | |||
27 | /* Data cells */ | ||
28 | soc_revision: soc_revision { | ||
29 | reg = <0x0 0x4>; | ||
30 | }; | ||
31 | }; | ||
32 | }; | ||
33 | }; | ||
34 | |||
35 | = Data consumers = | ||
36 | Are device nodes which consume nvmem data cells. | ||
37 | |||
38 | For example: | ||
39 | pcap { | ||
40 | ... | ||
41 | |||
42 | nvmem-cells = <&soc_revision>; | ||
43 | nvmem-cell-names = "soc_revision"; | ||
44 | |||
45 | ... | ||
46 | }; | ||
diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt index c396c4c0af92..76b6c79604a5 100644 --- a/Documentation/devicetree/bindings/opp/opp.txt +++ b/Documentation/devicetree/bindings/opp/opp.txt | |||
@@ -129,6 +129,9 @@ Optional properties: | |||
129 | - opp-microamp-<name>: Named opp-microamp property. Similar to | 129 | - opp-microamp-<name>: Named opp-microamp property. Similar to |
130 | opp-microvolt-<name> property, but for microamp instead. | 130 | opp-microvolt-<name> property, but for microamp instead. |
131 | 131 | ||
132 | - opp-level: A value representing the performance level of the device, | ||
133 | expressed as a 32-bit integer. | ||
134 | |||
132 | - clock-latency-ns: Specifies the maximum possible transition latency (in | 135 | - clock-latency-ns: Specifies the maximum possible transition latency (in |
133 | nanoseconds) for switching to this OPP from any other OPP. | 136 | nanoseconds) for switching to this OPP from any other OPP. |
134 | 137 | ||
diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt index 7c947a996df1..7c7e972aaa42 100644 --- a/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt +++ b/Documentation/devicetree/bindings/power/fsl,imx-gpcv2.txt | |||
@@ -32,6 +32,9 @@ Required properties: | |||
32 | Optional properties: | 32 | Optional properties: |
33 | 33 | ||
34 | - power-supply: Power supply used to power the domain | 34 | - power-supply: Power supply used to power the domain |
35 | - clocks: a number of phandles to clocks that need to be enabled during | ||
36 | domain power-up sequencing to ensure reset propagation into devices | ||
37 | located inside this power domain | ||
35 | 38 | ||
36 | Example: | 39 | Example: |
37 | 40 | ||
diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.txt b/Documentation/devicetree/bindings/power/qcom,rpmpd.txt new file mode 100644 index 000000000000..980e5413d18f --- /dev/null +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.txt | |||
@@ -0,0 +1,145 @@ | |||
1 | Qualcomm RPM/RPMh Power domains | ||
2 | |||
3 | For RPM/RPMh Power domains, we communicate a performance state to RPM/RPMh | ||
4 | which then translates it into a corresponding voltage on a rail | ||
5 | |||
6 | Required Properties: | ||
7 | - compatible: Should be one of the following | ||
8 | * qcom,msm8996-rpmpd: RPM Power domain for the msm8996 family of SoC | ||
9 | * qcom,sdm845-rpmhpd: RPMh Power domain for the sdm845 family of SoC | ||
10 | - #power-domain-cells: number of cells in Power domain specifier | ||
11 | must be 1. | ||
12 | - operating-points-v2: Phandle to the OPP table for the Power domain. | ||
13 | Refer to Documentation/devicetree/bindings/power/power_domain.txt | ||
14 | and Documentation/devicetree/bindings/opp/opp.txt for more details | ||
15 | |||
16 | Refer to <dt-bindings/power/qcom-rpmpd.h> for the level values for | ||
17 | various OPPs for different platforms as well as Power domain indexes | ||
18 | |||
19 | Example: rpmh power domain controller and OPP table | ||
20 | |||
21 | #include <dt-bindings/power/qcom-rpmhpd.h> | ||
22 | |||
23 | opp-level values specified in the OPP tables for RPMh power domains | ||
24 | should use the RPMH_REGULATOR_LEVEL_* constants from | ||
25 | <dt-bindings/power/qcom-rpmhpd.h> | ||
26 | |||
27 | rpmhpd: power-controller { | ||
28 | compatible = "qcom,sdm845-rpmhpd"; | ||
29 | #power-domain-cells = <1>; | ||
30 | operating-points-v2 = <&rpmhpd_opp_table>; | ||
31 | |||
32 | rpmhpd_opp_table: opp-table { | ||
33 | compatible = "operating-points-v2"; | ||
34 | |||
35 | rpmhpd_opp_ret: opp1 { | ||
36 | opp-level = <RPMH_REGULATOR_LEVEL_RETENTION>; | ||
37 | }; | ||
38 | |||
39 | rpmhpd_opp_min_svs: opp2 { | ||
40 | opp-level = <RPMH_REGULATOR_LEVEL_MIN_SVS>; | ||
41 | }; | ||
42 | |||
43 | rpmhpd_opp_low_svs: opp3 { | ||
44 | opp-level = <RPMH_REGULATOR_LEVEL_LOW_SVS>; | ||
45 | }; | ||
46 | |||
47 | rpmhpd_opp_svs: opp4 { | ||
48 | opp-level = <RPMH_REGULATOR_LEVEL_SVS>; | ||
49 | }; | ||
50 | |||
51 | rpmhpd_opp_svs_l1: opp5 { | ||
52 | opp-level = <RPMH_REGULATOR_LEVEL_SVS_L1>; | ||
53 | }; | ||
54 | |||
55 | rpmhpd_opp_nom: opp6 { | ||
56 | opp-level = <RPMH_REGULATOR_LEVEL_NOM>; | ||
57 | }; | ||
58 | |||
59 | rpmhpd_opp_nom_l1: opp7 { | ||
60 | opp-level = <RPMH_REGULATOR_LEVEL_NOM_L1>; | ||
61 | }; | ||
62 | |||
63 | rpmhpd_opp_nom_l2: opp8 { | ||
64 | opp-level = <RPMH_REGULATOR_LEVEL_NOM_L2>; | ||
65 | }; | ||
66 | |||
67 | rpmhpd_opp_turbo: opp9 { | ||
68 | opp-level = <RPMH_REGULATOR_LEVEL_TURBO>; | ||
69 | }; | ||
70 | |||
71 | rpmhpd_opp_turbo_l1: opp10 { | ||
72 | opp-level = <RPMH_REGULATOR_LEVEL_TURBO_L1>; | ||
73 | }; | ||
74 | }; | ||
75 | }; | ||
76 | |||
77 | Example: rpm power domain controller and OPP table | ||
78 | |||
79 | rpmpd: power-controller { | ||
80 | compatible = "qcom,msm8996-rpmpd"; | ||
81 | #power-domain-cells = <1>; | ||
82 | operating-points-v2 = <&rpmpd_opp_table>; | ||
83 | |||
84 | rpmpd_opp_table: opp-table { | ||
85 | compatible = "operating-points-v2"; | ||
86 | |||
87 | rpmpd_opp_low: opp1 { | ||
88 | opp-level = <1>; | ||
89 | }; | ||
90 | |||
91 | rpmpd_opp_ret: opp2 { | ||
92 | opp-level = <2>; | ||
93 | }; | ||
94 | |||
95 | rpmpd_opp_svs: opp3 { | ||
96 | opp-level = <3>; | ||
97 | }; | ||
98 | |||
99 | rpmpd_opp_normal: opp4 { | ||
100 | opp-level = <4>; | ||
101 | }; | ||
102 | |||
103 | rpmpd_opp_high: opp5 { | ||
104 | opp-level = <5>; | ||
105 | }; | ||
106 | |||
107 | rpmpd_opp_turbo: opp6 { | ||
108 | opp-level = <6>; | ||
109 | }; | ||
110 | }; | ||
111 | }; | ||
112 | |||
113 | Example: Client/Consumer device using OPP table | ||
114 | |||
115 | leaky-device0@12350000 { | ||
116 | compatible = "foo,i-leak-current"; | ||
117 | reg = <0x12350000 0x1000>; | ||
118 | power-domains = <&rpmhpd SDM845_MX>; | ||
119 | operating-points-v2 = <&leaky_opp_table>; | ||
120 | }; | ||
121 | |||
122 | |||
123 | leaky_opp_table: opp-table { | ||
124 | compatible = "operating-points-v2"; | ||
125 | |||
126 | opp1 { | ||
127 | opp-hz = /bits/ 64 <144000>; | ||
128 | required-opps = <&rpmhpd_opp_low>; | ||
129 | }; | ||
130 | |||
131 | opp2 { | ||
132 | opp-hz = /bits/ 64 <400000>; | ||
133 | required-opps = <&rpmhpd_opp_ret>; | ||
134 | }; | ||
135 | |||
136 | opp3 { | ||
137 | opp-hz = /bits/ 64 <20000000>; | ||
138 | required-opps = <&rpmpd_opp_svs>; | ||
139 | }; | ||
140 | |||
141 | opp4 { | ||
142 | opp-hz = /bits/ 64 <25000000>; | ||
143 | required-opps = <&rpmpd_opp_normal>; | ||
144 | }; | ||
145 | }; | ||
diff --git a/Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.txt b/Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.txt new file mode 100644 index 000000000000..d366f1eb623a --- /dev/null +++ b/Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.txt | |||
@@ -0,0 +1,25 @@ | |||
1 | -------------------------------------------------------------------- | ||
2 | Device Tree Bindings for the Xilinx Zynq MPSoC Power Management | ||
3 | -------------------------------------------------------------------- | ||
4 | The zynqmp-power node describes the power management configurations. | ||
5 | It will control remote suspend/shutdown interfaces. | ||
6 | |||
7 | Required properties: | ||
8 | - compatible: Must contain: "xlnx,zynqmp-power" | ||
9 | - interrupts: Interrupt specifier | ||
10 | |||
11 | ------- | ||
12 | Example | ||
13 | ------- | ||
14 | |||
15 | firmware { | ||
16 | zynqmp_firmware: zynqmp-firmware { | ||
17 | compatible = "xlnx,zynqmp-firmware"; | ||
18 | method = "smc"; | ||
19 | |||
20 | zynqmp_power: zynqmp-power { | ||
21 | compatible = "xlnx,zynqmp-power"; | ||
22 | interrupts = <0 35 4>; | ||
23 | }; | ||
24 | }; | ||
25 | }; | ||
diff --git a/Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt b/Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt new file mode 100644 index 000000000000..8d1b8200ebd0 --- /dev/null +++ b/Documentation/devicetree/bindings/power/xlnx,zynqmp-genpd.txt | |||
@@ -0,0 +1,34 @@ | |||
1 | ----------------------------------------------------------- | ||
2 | Device Tree Bindings for the Xilinx Zynq MPSoC PM domains | ||
3 | ----------------------------------------------------------- | ||
4 | The binding for zynqmp-power-controller follow the common | ||
5 | generic PM domain binding[1]. | ||
6 | |||
7 | [1] Documentation/devicetree/bindings/power/power_domain.txt | ||
8 | |||
9 | == Zynq MPSoC Generic PM Domain Node == | ||
10 | |||
11 | Required property: | ||
12 | - Below property should be in zynqmp-firmware node. | ||
13 | - #power-domain-cells: Number of cells in a PM domain specifier. Must be 1. | ||
14 | |||
15 | Power domain ID indexes are mentioned in | ||
16 | include/dt-bindings/power/xlnx-zynqmp-power.h. | ||
17 | |||
18 | ------- | ||
19 | Example | ||
20 | ------- | ||
21 | |||
22 | firmware { | ||
23 | zynqmp_firmware: zynqmp-firmware { | ||
24 | ... | ||
25 | #power-domain-cells = <1>; | ||
26 | ... | ||
27 | }; | ||
28 | }; | ||
29 | |||
30 | sata { | ||
31 | ... | ||
32 | power-domains = <&zynqmp_firmware 28>; | ||
33 | ... | ||
34 | }; | ||
diff --git a/Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt b/Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt new file mode 100644 index 000000000000..6e5341b4f891 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/brcm,brcmstb-reset.txt | |||
@@ -0,0 +1,27 @@ | |||
1 | Broadcom STB SW_INIT-style reset controller | ||
2 | =========================================== | ||
3 | |||
4 | Broadcom STB SoCs have a SW_INIT-style reset controller with separate | ||
5 | SET/CLEAR/STATUS registers and possibly multiple banks, each of 32 bit | ||
6 | reset lines. | ||
7 | |||
8 | Please also refer to reset.txt in this directory for common reset | ||
9 | controller binding usage. | ||
10 | |||
11 | Required properties: | ||
12 | - compatible: should be brcm,brcmstb-reset | ||
13 | - reg: register base and length | ||
14 | - #reset-cells: must be set to 1 | ||
15 | |||
16 | Example: | ||
17 | |||
18 | reset: reset-controller@8404318 { | ||
19 | compatible = "brcm,brcmstb-reset"; | ||
20 | reg = <0x8404318 0x30>; | ||
21 | #reset-cells = <1>; | ||
22 | }; | ||
23 | |||
24 | ðernet_switch { | ||
25 | resets = <&reset>; | ||
26 | reset-names = "switch"; | ||
27 | }; | ||
diff --git a/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt b/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt index 1ab1d109318e..2ecf33815d18 100644 --- a/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt +++ b/Documentation/devicetree/bindings/reset/fsl,imx7-src.txt | |||
@@ -5,7 +5,9 @@ Please also refer to reset.txt in this directory for common reset | |||
5 | controller binding usage. | 5 | controller binding usage. |
6 | 6 | ||
7 | Required properties: | 7 | Required properties: |
8 | - compatible: Should be "fsl,imx7d-src", "syscon" | 8 | - compatible: |
9 | - For i.MX7 SoCs should be "fsl,imx7d-src", "syscon" | ||
10 | - For i.MX8MQ SoCs should be "fsl,imx8mq-src", "syscon" | ||
9 | - reg: should be register base and length as documented in the | 11 | - reg: should be register base and length as documented in the |
10 | datasheet | 12 | datasheet |
11 | - interrupts: Should contain SRC interrupt | 13 | - interrupts: Should contain SRC interrupt |
@@ -44,4 +46,5 @@ Example: | |||
44 | 46 | ||
45 | 47 | ||
46 | For list of all valid reset indicies see | 48 | For list of all valid reset indicies see |
47 | <dt-bindings/reset/imx7-reset.h> | 49 | <dt-bindings/reset/imx7-reset.h> for i.MX7 and |
50 | <dt-bindings/reset/imx8mq-reset.h> for i.MX8MQ | ||
diff --git a/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.txt b/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.txt new file mode 100644 index 000000000000..27a45fe5ecf1 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.txt | |||
@@ -0,0 +1,52 @@ | |||
1 | -------------------------------------------------------------------------- | ||
2 | = Zynq UltraScale+ MPSoC reset driver binding = | ||
3 | -------------------------------------------------------------------------- | ||
4 | The Zynq UltraScale+ MPSoC has several different resets. | ||
5 | |||
6 | See Chapter 36 of the Zynq UltraScale+ MPSoC TRM (UG) for more information | ||
7 | about zynqmp resets. | ||
8 | |||
9 | Please also refer to reset.txt in this directory for common reset | ||
10 | controller binding usage. | ||
11 | |||
12 | Required Properties: | ||
13 | - compatible: "xlnx,zynqmp-reset" | ||
14 | - #reset-cells: Specifies the number of cells needed to encode reset | ||
15 | line, should be 1 | ||
16 | |||
17 | ------- | ||
18 | Example | ||
19 | ------- | ||
20 | |||
21 | firmware { | ||
22 | zynqmp_firmware: zynqmp-firmware { | ||
23 | compatible = "xlnx,zynqmp-firmware"; | ||
24 | method = "smc"; | ||
25 | |||
26 | zynqmp_reset: reset-controller { | ||
27 | compatible = "xlnx,zynqmp-reset"; | ||
28 | #reset-cells = <1>; | ||
29 | }; | ||
30 | }; | ||
31 | }; | ||
32 | |||
33 | Specifying reset lines connected to IP modules | ||
34 | ============================================== | ||
35 | |||
36 | Device nodes that need access to reset lines should | ||
37 | specify them as a reset phandle in their corresponding node as | ||
38 | specified in reset.txt. | ||
39 | |||
40 | For list of all valid reset indicies see | ||
41 | <dt-bindings/reset/xlnx-zynqmp-resets.h> | ||
42 | |||
43 | Example: | ||
44 | |||
45 | serdes: zynqmp_phy@fd400000 { | ||
46 | ... | ||
47 | |||
48 | resets = <&zynqmp_reset ZYNQMP_RESET_SATA>; | ||
49 | reset-names = "sata_rst"; | ||
50 | |||
51 | ... | ||
52 | }; | ||
diff --git a/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt b/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt index 205a54bcd7c7..6bf6b43f8dd8 100644 --- a/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt +++ b/Documentation/devicetree/bindings/soc/amlogic/clk-measure.txt | |||
@@ -9,6 +9,8 @@ Required properties: | |||
9 | "amlogic,meson-gx-clk-measure" for GX SoCs | 9 | "amlogic,meson-gx-clk-measure" for GX SoCs |
10 | "amlogic,meson8-clk-measure" for Meson8 SoCs | 10 | "amlogic,meson8-clk-measure" for Meson8 SoCs |
11 | "amlogic,meson8b-clk-measure" for Meson8b SoCs | 11 | "amlogic,meson8b-clk-measure" for Meson8b SoCs |
12 | "amlogic,meson-axg-clk-measure" for AXG SoCs | ||
13 | "amlogic,meson-g12a-clk-measure" for G12a SoCs | ||
12 | - reg: base address and size of the Clock Measurer register space. | 14 | - reg: base address and size of the Clock Measurer register space. |
13 | 15 | ||
14 | Example: | 16 | Example: |
diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.txt b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.txt new file mode 100644 index 000000000000..3b7d32956391 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.txt | |||
@@ -0,0 +1,46 @@ | |||
1 | BCM2835 PM (Power domains, watchdog) | ||
2 | |||
3 | The PM block controls power domains and some reset lines, and includes | ||
4 | a watchdog timer. This binding supersedes the brcm,bcm2835-pm-wdt | ||
5 | binding which covered some of PM's register range and functionality. | ||
6 | |||
7 | Required properties: | ||
8 | |||
9 | - compatible: Should be "brcm,bcm2835-pm" | ||
10 | - reg: Specifies base physical address and size of the two | ||
11 | register ranges ("PM" and "ASYNC_BRIDGE" in that | ||
12 | order) | ||
13 | - clocks: a) v3d: The V3D clock from CPRMAN | ||
14 | b) peri_image: The PERI_IMAGE clock from CPRMAN | ||
15 | c) h264: The H264 clock from CPRMAN | ||
16 | d) isp: The ISP clock from CPRMAN | ||
17 | - #reset-cells: Should be 1. This property follows the reset controller | ||
18 | bindings[1]. | ||
19 | - #power-domain-cells: Should be 1. This property follows the power domain | ||
20 | bindings[2]. | ||
21 | |||
22 | Optional properties: | ||
23 | |||
24 | - timeout-sec: Contains the watchdog timeout in seconds | ||
25 | - system-power-controller: Whether the watchdog is controlling the | ||
26 | system power. This node follows the power controller bindings[3]. | ||
27 | |||
28 | [1] Documentation/devicetree/bindings/reset/reset.txt | ||
29 | [2] Documentation/devicetree/bindings/power/power_domain.txt | ||
30 | [3] Documentation/devicetree/bindings/power/power-controller.txt | ||
31 | |||
32 | Example: | ||
33 | |||
34 | pm { | ||
35 | compatible = "brcm,bcm2835-pm", "brcm,bcm2835-pm-wdt"; | ||
36 | #power-domain-cells = <1>; | ||
37 | #reset-cells = <1>; | ||
38 | reg = <0x7e100000 0x114>, | ||
39 | <0x7e00a000 0x24>; | ||
40 | clocks = <&clocks BCM2835_CLOCK_V3D>, | ||
41 | <&clocks BCM2835_CLOCK_PERI_IMAGE>, | ||
42 | <&clocks BCM2835_CLOCK_H264>, | ||
43 | <&clocks BCM2835_CLOCK_ISP>; | ||
44 | clock-names = "v3d", "peri_image", "h264", "isp"; | ||
45 | system-power-controller; | ||
46 | }; | ||
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt index ec95705ba692..f3fa313963d5 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.txt | |||
@@ -23,6 +23,7 @@ resources. | |||
23 | "qcom,rpm-msm8916" | 23 | "qcom,rpm-msm8916" |
24 | "qcom,rpm-msm8974" | 24 | "qcom,rpm-msm8974" |
25 | "qcom,rpm-msm8998" | 25 | "qcom,rpm-msm8998" |
26 | "qcom,rpm-sdm660" | ||
26 | "qcom,rpm-qcs404" | 27 | "qcom,rpm-qcs404" |
27 | 28 | ||
28 | - qcom,smd-channels: | 29 | - qcom,smd-channels: |
diff --git a/MAINTAINERS b/MAINTAINERS index 49f7d49c2d6e..d5a4b63d13ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -1940,19 +1940,37 @@ M: David Brown <david.brown@linaro.org> | |||
1940 | L: linux-arm-msm@vger.kernel.org | 1940 | L: linux-arm-msm@vger.kernel.org |
1941 | S: Maintained | 1941 | S: Maintained |
1942 | F: Documentation/devicetree/bindings/soc/qcom/ | 1942 | F: Documentation/devicetree/bindings/soc/qcom/ |
1943 | F: Documentation/devicetree/bindings/*/qcom* | ||
1943 | F: arch/arm/boot/dts/qcom-*.dts | 1944 | F: arch/arm/boot/dts/qcom-*.dts |
1944 | F: arch/arm/boot/dts/qcom-*.dtsi | 1945 | F: arch/arm/boot/dts/qcom-*.dtsi |
1945 | F: arch/arm/mach-qcom/ | 1946 | F: arch/arm/mach-qcom/ |
1946 | F: arch/arm64/boot/dts/qcom/* | 1947 | F: arch/arm64/boot/dts/qcom/ |
1948 | F: drivers/*/qcom/ | ||
1949 | F: drivers/*/qcom* | ||
1950 | F: drivers/*/*/qcom/ | ||
1951 | F: drivers/*/*/qcom* | ||
1952 | F: drivers/*/pm8???-* | ||
1953 | F: drivers/bluetooth/btqcomsmd.c | ||
1954 | F: drivers/clocksource/timer-qcom.c | ||
1955 | F: drivers/extcon/extcon-qcom* | ||
1956 | F: drivers/iommu/msm* | ||
1947 | F: drivers/i2c/busses/i2c-qup.c | 1957 | F: drivers/i2c/busses/i2c-qup.c |
1948 | F: drivers/clk/qcom/ | 1958 | F: drivers/i2c/busses/i2c-qcom-geni.c |
1949 | F: drivers/dma/qcom/ | 1959 | F: drivers/mfd/ssbi.c |
1950 | F: drivers/soc/qcom/ | 1960 | F: drivers/mmc/host/mmci_qcom* |
1961 | F: drivers/mmc/host/sdhci_msm.c | ||
1962 | F: drivers/pci/controller/dwc/pcie-qcom.c | ||
1963 | F: drivers/phy/qualcomm/ | ||
1964 | F: drivers/power/*/msm* | ||
1965 | F: drivers/reset/reset-qcom-* | ||
1966 | F: drivers/scsi/ufs/ufs-qcom.* | ||
1951 | F: drivers/spi/spi-qup.c | 1967 | F: drivers/spi/spi-qup.c |
1968 | F: drivers/spi/spi-geni-qcom.c | ||
1969 | F: drivers/spi/spi-qcom-qspi.c | ||
1952 | F: drivers/tty/serial/msm_serial.c | 1970 | F: drivers/tty/serial/msm_serial.c |
1953 | F: drivers/*/pm8???-* | 1971 | F: drivers/usb/dwc3/dwc3-qcom.c |
1954 | F: drivers/mfd/ssbi.c | 1972 | F: include/dt-bindings/*/qcom* |
1955 | F: drivers/firmware/qcom_scm* | 1973 | F: include/linux/*/qcom* |
1956 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux.git | 1974 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/agross/linux.git |
1957 | 1975 | ||
1958 | ARM/RADISYS ENP2611 MACHINE SUPPORT | 1976 | ARM/RADISYS ENP2611 MACHINE SUPPORT |
@@ -11314,6 +11332,11 @@ M: Jens Wiklander <jens.wiklander@linaro.org> | |||
11314 | S: Maintained | 11332 | S: Maintained |
11315 | F: drivers/tee/optee/ | 11333 | F: drivers/tee/optee/ |
11316 | 11334 | ||
11335 | OP-TEE RANDOM NUMBER GENERATOR (RNG) DRIVER | ||
11336 | M: Sumit Garg <sumit.garg@linaro.org> | ||
11337 | S: Maintained | ||
11338 | F: drivers/char/hw_random/optee-rng.c | ||
11339 | |||
11317 | OPA-VNIC DRIVER | 11340 | OPA-VNIC DRIVER |
11318 | M: Dennis Dalessandro <dennis.dalessandro@intel.com> | 11341 | M: Dennis Dalessandro <dennis.dalessandro@intel.com> |
11319 | M: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com> | 11342 | M: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com> |
@@ -13017,6 +13040,7 @@ F: drivers/reset/ | |||
13017 | F: Documentation/devicetree/bindings/reset/ | 13040 | F: Documentation/devicetree/bindings/reset/ |
13018 | F: include/dt-bindings/reset/ | 13041 | F: include/dt-bindings/reset/ |
13019 | F: include/linux/reset.h | 13042 | F: include/linux/reset.h |
13043 | F: include/linux/reset/ | ||
13020 | F: include/linux/reset-controller.h | 13044 | F: include/linux/reset-controller.h |
13021 | 13045 | ||
13022 | RESTARTABLE SEQUENCES SUPPORT | 13046 | RESTARTABLE SEQUENCES SUPPORT |
diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index 9d11cb759b5b..715d50c64529 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi | |||
@@ -85,10 +85,6 @@ | |||
85 | power-domains = <&power RPI_POWER_DOMAIN_USB>; | 85 | power-domains = <&power RPI_POWER_DOMAIN_USB>; |
86 | }; | 86 | }; |
87 | 87 | ||
88 | &v3d { | ||
89 | power-domains = <&power RPI_POWER_DOMAIN_V3D>; | ||
90 | }; | ||
91 | |||
92 | &hdmi { | 88 | &hdmi { |
93 | power-domains = <&power RPI_POWER_DOMAIN_HDMI>; | 89 | power-domains = <&power RPI_POWER_DOMAIN_HDMI>; |
94 | status = "okay"; | 90 | status = "okay"; |
diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi index 31b29646b14c..9777644c6c2b 100644 --- a/arch/arm/boot/dts/bcm283x.dtsi +++ b/arch/arm/boot/dts/bcm283x.dtsi | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <dt-bindings/clock/bcm2835-aux.h> | 3 | #include <dt-bindings/clock/bcm2835-aux.h> |
4 | #include <dt-bindings/gpio/gpio.h> | 4 | #include <dt-bindings/gpio/gpio.h> |
5 | #include <dt-bindings/interrupt-controller/irq.h> | 5 | #include <dt-bindings/interrupt-controller/irq.h> |
6 | #include <dt-bindings/soc/bcm2835-pm.h> | ||
6 | 7 | ||
7 | /* firmware-provided startup stubs live here, where the secondary CPUs are | 8 | /* firmware-provided startup stubs live here, where the secondary CPUs are |
8 | * spinning. | 9 | * spinning. |
@@ -120,9 +121,18 @@ | |||
120 | #interrupt-cells = <2>; | 121 | #interrupt-cells = <2>; |
121 | }; | 122 | }; |
122 | 123 | ||
123 | watchdog@7e100000 { | 124 | pm: watchdog@7e100000 { |
124 | compatible = "brcm,bcm2835-pm-wdt"; | 125 | compatible = "brcm,bcm2835-pm", "brcm,bcm2835-pm-wdt"; |
125 | reg = <0x7e100000 0x28>; | 126 | #power-domain-cells = <1>; |
127 | #reset-cells = <1>; | ||
128 | reg = <0x7e100000 0x114>, | ||
129 | <0x7e00a000 0x24>; | ||
130 | clocks = <&clocks BCM2835_CLOCK_V3D>, | ||
131 | <&clocks BCM2835_CLOCK_PERI_IMAGE>, | ||
132 | <&clocks BCM2835_CLOCK_H264>, | ||
133 | <&clocks BCM2835_CLOCK_ISP>; | ||
134 | clock-names = "v3d", "peri_image", "h264", "isp"; | ||
135 | system-power-controller; | ||
126 | }; | 136 | }; |
127 | 137 | ||
128 | clocks: cprman@7e101000 { | 138 | clocks: cprman@7e101000 { |
@@ -629,6 +639,7 @@ | |||
629 | compatible = "brcm,bcm2835-v3d"; | 639 | compatible = "brcm,bcm2835-v3d"; |
630 | reg = <0x7ec00000 0x1000>; | 640 | reg = <0x7ec00000 0x1000>; |
631 | interrupts = <1 10>; | 641 | interrupts = <1 10>; |
642 | power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>; | ||
632 | }; | 643 | }; |
633 | 644 | ||
634 | vc4: gpu { | 645 | vc4: gpu { |
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index a067adf9f1ee..4ef1e55f4a0b 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig | |||
@@ -167,6 +167,7 @@ config ARCH_BCM2835 | |||
167 | select BCM2835_TIMER | 167 | select BCM2835_TIMER |
168 | select PINCTRL | 168 | select PINCTRL |
169 | select PINCTRL_BCM2835 | 169 | select PINCTRL_BCM2835 |
170 | select MFD_CORE | ||
170 | help | 171 | help |
171 | This enables support for the Broadcom BCM2835 and BCM2836 SoCs. | 172 | This enables support for the Broadcom BCM2835 and BCM2836 SoCs. |
172 | This SoC is used in the Raspberry Pi and Roku 2 devices. | 173 | This SoC is used in the Raspberry Pi and Roku 2 devices. |
diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c index afd98971d903..816da0eb6616 100644 --- a/arch/arm/mach-socfpga/socfpga.c +++ b/arch/arm/mach-socfpga/socfpga.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/of_irq.h> | 19 | #include <linux/of_irq.h> |
20 | #include <linux/of_platform.h> | 20 | #include <linux/of_platform.h> |
21 | #include <linux/reboot.h> | 21 | #include <linux/reboot.h> |
22 | #include <linux/reset/socfpga.h> | ||
22 | 23 | ||
23 | #include <asm/hardware/cache-l2x0.h> | 24 | #include <asm/hardware/cache-l2x0.h> |
24 | #include <asm/mach/arch.h> | 25 | #include <asm/mach/arch.h> |
@@ -32,8 +33,6 @@ void __iomem *rst_manager_base_addr; | |||
32 | void __iomem *sdr_ctl_base_addr; | 33 | void __iomem *sdr_ctl_base_addr; |
33 | unsigned long socfpga_cpu1start_addr; | 34 | unsigned long socfpga_cpu1start_addr; |
34 | 35 | ||
35 | extern void __init socfpga_reset_init(void); | ||
36 | |||
37 | static void __init socfpga_sysmgr_init(void) | 36 | static void __init socfpga_sysmgr_init(void) |
38 | { | 37 | { |
39 | struct device_node *np; | 38 | struct device_node *np; |
diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c index 8a7f301839c2..933b6930f024 100644 --- a/arch/arm/mach-sunxi/sunxi.c +++ b/arch/arm/mach-sunxi/sunxi.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/clocksource.h> | 14 | #include <linux/clocksource.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/reset/sunxi.h> | ||
17 | 18 | ||
18 | #include <asm/mach/arch.h> | 19 | #include <asm/mach/arch.h> |
19 | #include <asm/secure_cntvoff.h> | 20 | #include <asm/secure_cntvoff.h> |
@@ -37,7 +38,6 @@ static const char * const sun6i_board_dt_compat[] = { | |||
37 | NULL, | 38 | NULL, |
38 | }; | 39 | }; |
39 | 40 | ||
40 | extern void __init sun6i_reset_init(void); | ||
41 | static void __init sun6i_timer_init(void) | 41 | static void __init sun6i_timer_init(void) |
42 | { | 42 | { |
43 | of_clk_init(NULL); | 43 | of_clk_init(NULL); |
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c index d5f85455fa62..19d7b6ff2f17 100644 --- a/drivers/bus/hisi_lpc.c +++ b/drivers/bus/hisi_lpc.c | |||
@@ -522,10 +522,9 @@ static int hisi_lpc_acpi_probe(struct device *hostdev) | |||
522 | 522 | ||
523 | if (!found) { | 523 | if (!found) { |
524 | dev_warn(hostdev, | 524 | dev_warn(hostdev, |
525 | "could not find cell for child device (%s)\n", | 525 | "could not find cell for child device (%s), discarding\n", |
526 | hid); | 526 | hid); |
527 | ret = -ENODEV; | 527 | continue; |
528 | goto fail; | ||
529 | } | 528 | } |
530 | 529 | ||
531 | pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO); | 530 | pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO); |
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index d84996a4528e..db74334ca5ef 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c | |||
@@ -46,6 +46,17 @@ static const struct imx_weim_devtype imx51_weim_devtype = { | |||
46 | }; | 46 | }; |
47 | 47 | ||
48 | #define MAX_CS_REGS_COUNT 6 | 48 | #define MAX_CS_REGS_COUNT 6 |
49 | #define MAX_CS_COUNT 6 | ||
50 | #define OF_REG_SIZE 3 | ||
51 | |||
52 | struct cs_timing { | ||
53 | bool is_applied; | ||
54 | u32 regs[MAX_CS_REGS_COUNT]; | ||
55 | }; | ||
56 | |||
57 | struct cs_timing_state { | ||
58 | struct cs_timing cs[MAX_CS_COUNT]; | ||
59 | }; | ||
49 | 60 | ||
50 | static const struct of_device_id weim_id_table[] = { | 61 | static const struct of_device_id weim_id_table[] = { |
51 | /* i.MX1/21 */ | 62 | /* i.MX1/21 */ |
@@ -111,21 +122,19 @@ err: | |||
111 | } | 122 | } |
112 | 123 | ||
113 | /* Parse and set the timing for this device. */ | 124 | /* Parse and set the timing for this device. */ |
114 | static int __init weim_timing_setup(struct device_node *np, void __iomem *base, | 125 | static int __init weim_timing_setup(struct device *dev, |
115 | const struct imx_weim_devtype *devtype) | 126 | struct device_node *np, void __iomem *base, |
127 | const struct imx_weim_devtype *devtype, | ||
128 | struct cs_timing_state *ts) | ||
116 | { | 129 | { |
117 | u32 cs_idx, value[MAX_CS_REGS_COUNT]; | 130 | u32 cs_idx, value[MAX_CS_REGS_COUNT]; |
118 | int i, ret; | 131 | int i, ret; |
132 | int reg_idx, num_regs; | ||
133 | struct cs_timing *cst; | ||
119 | 134 | ||
120 | if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) | 135 | if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) |
121 | return -EINVAL; | 136 | return -EINVAL; |
122 | 137 | if (WARN_ON(devtype->cs_count > MAX_CS_COUNT)) | |
123 | /* get the CS index from this child node's "reg" property. */ | ||
124 | ret = of_property_read_u32(np, "reg", &cs_idx); | ||
125 | if (ret) | ||
126 | return ret; | ||
127 | |||
128 | if (cs_idx >= devtype->cs_count) | ||
129 | return -EINVAL; | 138 | return -EINVAL; |
130 | 139 | ||
131 | ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", | 140 | ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", |
@@ -133,9 +142,43 @@ static int __init weim_timing_setup(struct device_node *np, void __iomem *base, | |||
133 | if (ret) | 142 | if (ret) |
134 | return ret; | 143 | return ret; |
135 | 144 | ||
136 | /* set the timing for WEIM */ | 145 | /* |
137 | for (i = 0; i < devtype->cs_regs_count; i++) | 146 | * the child node's "reg" property may contain multiple address ranges, |
138 | writel(value[i], base + cs_idx * devtype->cs_stride + i * 4); | 147 | * extract the chip select for each. |
148 | */ | ||
149 | num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE); | ||
150 | if (num_regs < 0) | ||
151 | return num_regs; | ||
152 | if (!num_regs) | ||
153 | return -EINVAL; | ||
154 | for (reg_idx = 0; reg_idx < num_regs; reg_idx++) { | ||
155 | /* get the CS index from this child node's "reg" property. */ | ||
156 | ret = of_property_read_u32_index(np, "reg", | ||
157 | reg_idx * OF_REG_SIZE, &cs_idx); | ||
158 | if (ret) | ||
159 | break; | ||
160 | |||
161 | if (cs_idx >= devtype->cs_count) | ||
162 | return -EINVAL; | ||
163 | |||
164 | /* prevent re-configuring a CS that's already been configured */ | ||
165 | cst = &ts->cs[cs_idx]; | ||
166 | if (cst->is_applied && memcmp(value, cst->regs, | ||
167 | devtype->cs_regs_count * sizeof(u32))) { | ||
168 | dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np); | ||
169 | return -EINVAL; | ||
170 | } | ||
171 | |||
172 | /* set the timing for WEIM */ | ||
173 | for (i = 0; i < devtype->cs_regs_count; i++) | ||
174 | writel(value[i], | ||
175 | base + cs_idx * devtype->cs_stride + i * 4); | ||
176 | if (!cst->is_applied) { | ||
177 | cst->is_applied = true; | ||
178 | memcpy(cst->regs, value, | ||
179 | devtype->cs_regs_count * sizeof(u32)); | ||
180 | } | ||
181 | } | ||
139 | 182 | ||
140 | return 0; | 183 | return 0; |
141 | } | 184 | } |
@@ -148,6 +191,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, | |||
148 | const struct imx_weim_devtype *devtype = of_id->data; | 191 | const struct imx_weim_devtype *devtype = of_id->data; |
149 | struct device_node *child; | 192 | struct device_node *child; |
150 | int ret, have_child = 0; | 193 | int ret, have_child = 0; |
194 | struct cs_timing_state ts = {}; | ||
151 | 195 | ||
152 | if (devtype == &imx50_weim_devtype) { | 196 | if (devtype == &imx50_weim_devtype) { |
153 | ret = imx_weim_gpr_setup(pdev); | 197 | ret = imx_weim_gpr_setup(pdev); |
@@ -156,7 +200,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, | |||
156 | } | 200 | } |
157 | 201 | ||
158 | for_each_available_child_of_node(pdev->dev.of_node, child) { | 202 | for_each_available_child_of_node(pdev->dev.of_node, child) { |
159 | ret = weim_timing_setup(child, base, devtype); | 203 | ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts); |
160 | if (ret) | 204 | if (ret) |
161 | dev_warn(&pdev->dev, "%pOF set timing failed.\n", | 205 | dev_warn(&pdev->dev, "%pOF set timing failed.\n", |
162 | child); | 206 | child); |
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index dac895dc01b9..25a7d8ffdb5d 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig | |||
@@ -424,6 +424,21 @@ config HW_RANDOM_EXYNOS | |||
424 | will be called exynos-trng. | 424 | will be called exynos-trng. |
425 | 425 | ||
426 | If unsure, say Y. | 426 | If unsure, say Y. |
427 | |||
428 | config HW_RANDOM_OPTEE | ||
429 | tristate "OP-TEE based Random Number Generator support" | ||
430 | depends on OPTEE | ||
431 | default HW_RANDOM | ||
432 | help | ||
433 | This driver provides support for OP-TEE based Random Number | ||
434 | Generator on ARM SoCs where hardware entropy sources are not | ||
435 | accessible to normal world (Linux). | ||
436 | |||
437 | To compile this driver as a module, choose M here: the module | ||
438 | will be called optee-rng. | ||
439 | |||
440 | If unsure, say Y. | ||
441 | |||
427 | endif # HW_RANDOM | 442 | endif # HW_RANDOM |
428 | 443 | ||
429 | config UML_RANDOM | 444 | config UML_RANDOM |
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index e35ec3ce3a20..7c9ef4a7667f 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile | |||
@@ -38,3 +38,4 @@ obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o | |||
38 | obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o | 38 | obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o |
39 | obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o | 39 | obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o |
40 | obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o | 40 | obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o |
41 | obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o | ||
diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c new file mode 100644 index 000000000000..ddfbabaa5f8f --- /dev/null +++ b/drivers/char/hw_random/optee-rng.c | |||
@@ -0,0 +1,306 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2018-2019 Linaro Ltd. | ||
4 | */ | ||
5 | |||
6 | #include <linux/delay.h> | ||
7 | #include <linux/of.h> | ||
8 | #include <linux/hw_random.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/tee_drv.h> | ||
13 | #include <linux/uuid.h> | ||
14 | |||
15 | #define DRIVER_NAME "optee-rng" | ||
16 | |||
17 | #define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001 | ||
18 | |||
19 | /* | ||
20 | * TA_CMD_GET_ENTROPY - Get Entropy from RNG | ||
21 | * | ||
22 | * param[0] (inout memref) - Entropy buffer memory reference | ||
23 | * param[1] unused | ||
24 | * param[2] unused | ||
25 | * param[3] unused | ||
26 | * | ||
27 | * Result: | ||
28 | * TEE_SUCCESS - Invoke command success | ||
29 | * TEE_ERROR_BAD_PARAMETERS - Incorrect input param | ||
30 | * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool | ||
31 | * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed | ||
32 | */ | ||
33 | #define TA_CMD_GET_ENTROPY 0x0 | ||
34 | |||
35 | /* | ||
36 | * TA_CMD_GET_RNG_INFO - Get RNG information | ||
37 | * | ||
38 | * param[0] (out value) - value.a: RNG data-rate in bytes per second | ||
39 | * value.b: Quality/Entropy per 1024 bit of data | ||
40 | * param[1] unused | ||
41 | * param[2] unused | ||
42 | * param[3] unused | ||
43 | * | ||
44 | * Result: | ||
45 | * TEE_SUCCESS - Invoke command success | ||
46 | * TEE_ERROR_BAD_PARAMETERS - Incorrect input param | ||
47 | */ | ||
48 | #define TA_CMD_GET_RNG_INFO 0x1 | ||
49 | |||
50 | #define MAX_ENTROPY_REQ_SZ (4 * 1024) | ||
51 | |||
52 | /** | ||
53 | * struct optee_rng_private - OP-TEE Random Number Generator private data | ||
54 | * @dev: OP-TEE based RNG device. | ||
55 | * @ctx: OP-TEE context handler. | ||
56 | * @session_id: RNG TA session identifier. | ||
57 | * @data_rate: RNG data rate. | ||
58 | * @entropy_shm_pool: Memory pool shared with RNG device. | ||
59 | * @optee_rng: OP-TEE RNG driver structure. | ||
60 | */ | ||
61 | struct optee_rng_private { | ||
62 | struct device *dev; | ||
63 | struct tee_context *ctx; | ||
64 | u32 session_id; | ||
65 | u32 data_rate; | ||
66 | struct tee_shm *entropy_shm_pool; | ||
67 | struct hwrng optee_rng; | ||
68 | }; | ||
69 | |||
70 | #define to_optee_rng_private(r) \ | ||
71 | container_of(r, struct optee_rng_private, optee_rng) | ||
72 | |||
73 | static size_t get_optee_rng_data(struct optee_rng_private *pvt_data, | ||
74 | void *buf, size_t req_size) | ||
75 | { | ||
76 | int ret = 0; | ||
77 | u8 *rng_data = NULL; | ||
78 | size_t rng_size = 0; | ||
79 | struct tee_ioctl_invoke_arg inv_arg; | ||
80 | struct tee_param param[4]; | ||
81 | |||
82 | memset(&inv_arg, 0, sizeof(inv_arg)); | ||
83 | memset(¶m, 0, sizeof(param)); | ||
84 | |||
85 | /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */ | ||
86 | inv_arg.func = TA_CMD_GET_ENTROPY; | ||
87 | inv_arg.session = pvt_data->session_id; | ||
88 | inv_arg.num_params = 4; | ||
89 | |||
90 | /* Fill invoke cmd params */ | ||
91 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; | ||
92 | param[0].u.memref.shm = pvt_data->entropy_shm_pool; | ||
93 | param[0].u.memref.size = req_size; | ||
94 | param[0].u.memref.shm_offs = 0; | ||
95 | |||
96 | ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param); | ||
97 | if ((ret < 0) || (inv_arg.ret != 0)) { | ||
98 | dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n", | ||
99 | inv_arg.ret); | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0); | ||
104 | if (IS_ERR(rng_data)) { | ||
105 | dev_err(pvt_data->dev, "tee_shm_get_va failed\n"); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | rng_size = param[0].u.memref.size; | ||
110 | memcpy(buf, rng_data, rng_size); | ||
111 | |||
112 | return rng_size; | ||
113 | } | ||
114 | |||
115 | static int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) | ||
116 | { | ||
117 | struct optee_rng_private *pvt_data = to_optee_rng_private(rng); | ||
118 | size_t read = 0, rng_size = 0; | ||
119 | int timeout = 1; | ||
120 | u8 *data = buf; | ||
121 | |||
122 | if (max > MAX_ENTROPY_REQ_SZ) | ||
123 | max = MAX_ENTROPY_REQ_SZ; | ||
124 | |||
125 | while (read == 0) { | ||
126 | rng_size = get_optee_rng_data(pvt_data, data, (max - read)); | ||
127 | |||
128 | data += rng_size; | ||
129 | read += rng_size; | ||
130 | |||
131 | if (wait) { | ||
132 | if (timeout-- == 0) | ||
133 | return read; | ||
134 | msleep((1000 * (max - read)) / pvt_data->data_rate); | ||
135 | } else { | ||
136 | return read; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | return read; | ||
141 | } | ||
142 | |||
143 | static int optee_rng_init(struct hwrng *rng) | ||
144 | { | ||
145 | struct optee_rng_private *pvt_data = to_optee_rng_private(rng); | ||
146 | struct tee_shm *entropy_shm_pool = NULL; | ||
147 | |||
148 | entropy_shm_pool = tee_shm_alloc(pvt_data->ctx, MAX_ENTROPY_REQ_SZ, | ||
149 | TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); | ||
150 | if (IS_ERR(entropy_shm_pool)) { | ||
151 | dev_err(pvt_data->dev, "tee_shm_alloc failed\n"); | ||
152 | return PTR_ERR(entropy_shm_pool); | ||
153 | } | ||
154 | |||
155 | pvt_data->entropy_shm_pool = entropy_shm_pool; | ||
156 | |||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static void optee_rng_cleanup(struct hwrng *rng) | ||
161 | { | ||
162 | struct optee_rng_private *pvt_data = to_optee_rng_private(rng); | ||
163 | |||
164 | tee_shm_free(pvt_data->entropy_shm_pool); | ||
165 | } | ||
166 | |||
167 | static struct optee_rng_private pvt_data = { | ||
168 | .optee_rng = { | ||
169 | .name = DRIVER_NAME, | ||
170 | .init = optee_rng_init, | ||
171 | .cleanup = optee_rng_cleanup, | ||
172 | .read = optee_rng_read, | ||
173 | } | ||
174 | }; | ||
175 | |||
176 | static int get_optee_rng_info(struct device *dev) | ||
177 | { | ||
178 | int ret = 0; | ||
179 | struct tee_ioctl_invoke_arg inv_arg; | ||
180 | struct tee_param param[4]; | ||
181 | |||
182 | memset(&inv_arg, 0, sizeof(inv_arg)); | ||
183 | memset(¶m, 0, sizeof(param)); | ||
184 | |||
185 | /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */ | ||
186 | inv_arg.func = TA_CMD_GET_RNG_INFO; | ||
187 | inv_arg.session = pvt_data.session_id; | ||
188 | inv_arg.num_params = 4; | ||
189 | |||
190 | /* Fill invoke cmd params */ | ||
191 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; | ||
192 | |||
193 | ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param); | ||
194 | if ((ret < 0) || (inv_arg.ret != 0)) { | ||
195 | dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n", | ||
196 | inv_arg.ret); | ||
197 | return -EINVAL; | ||
198 | } | ||
199 | |||
200 | pvt_data.data_rate = param[0].u.value.a; | ||
201 | pvt_data.optee_rng.quality = param[0].u.value.b; | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) | ||
207 | { | ||
208 | if (ver->impl_id == TEE_IMPL_ID_OPTEE) | ||
209 | return 1; | ||
210 | else | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int optee_rng_probe(struct device *dev) | ||
215 | { | ||
216 | struct tee_client_device *rng_device = to_tee_client_device(dev); | ||
217 | int ret = 0, err = -ENODEV; | ||
218 | struct tee_ioctl_open_session_arg sess_arg; | ||
219 | |||
220 | memset(&sess_arg, 0, sizeof(sess_arg)); | ||
221 | |||
222 | /* Open context with TEE driver */ | ||
223 | pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, | ||
224 | NULL); | ||
225 | if (IS_ERR(pvt_data.ctx)) | ||
226 | return -ENODEV; | ||
227 | |||
228 | /* Open session with hwrng Trusted App */ | ||
229 | memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN); | ||
230 | sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; | ||
231 | sess_arg.num_params = 0; | ||
232 | |||
233 | ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); | ||
234 | if ((ret < 0) || (sess_arg.ret != 0)) { | ||
235 | dev_err(dev, "tee_client_open_session failed, err: %x\n", | ||
236 | sess_arg.ret); | ||
237 | err = -EINVAL; | ||
238 | goto out_ctx; | ||
239 | } | ||
240 | pvt_data.session_id = sess_arg.session; | ||
241 | |||
242 | err = get_optee_rng_info(dev); | ||
243 | if (err) | ||
244 | goto out_sess; | ||
245 | |||
246 | err = hwrng_register(&pvt_data.optee_rng); | ||
247 | if (err) { | ||
248 | dev_err(dev, "hwrng registration failed (%d)\n", err); | ||
249 | goto out_sess; | ||
250 | } | ||
251 | |||
252 | pvt_data.dev = dev; | ||
253 | |||
254 | return 0; | ||
255 | |||
256 | out_sess: | ||
257 | tee_client_close_session(pvt_data.ctx, pvt_data.session_id); | ||
258 | out_ctx: | ||
259 | tee_client_close_context(pvt_data.ctx); | ||
260 | |||
261 | return err; | ||
262 | } | ||
263 | |||
264 | static int optee_rng_remove(struct device *dev) | ||
265 | { | ||
266 | hwrng_unregister(&pvt_data.optee_rng); | ||
267 | tee_client_close_session(pvt_data.ctx, pvt_data.session_id); | ||
268 | tee_client_close_context(pvt_data.ctx); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static const struct tee_client_device_id optee_rng_id_table[] = { | ||
274 | {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f, | ||
275 | 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)}, | ||
276 | {} | ||
277 | }; | ||
278 | |||
279 | MODULE_DEVICE_TABLE(tee, optee_rng_id_table); | ||
280 | |||
281 | static struct tee_client_driver optee_rng_driver = { | ||
282 | .id_table = optee_rng_id_table, | ||
283 | .driver = { | ||
284 | .name = DRIVER_NAME, | ||
285 | .bus = &tee_bus_type, | ||
286 | .probe = optee_rng_probe, | ||
287 | .remove = optee_rng_remove, | ||
288 | }, | ||
289 | }; | ||
290 | |||
291 | static int __init optee_rng_mod_init(void) | ||
292 | { | ||
293 | return driver_register(&optee_rng_driver.driver); | ||
294 | } | ||
295 | |||
296 | static void __exit optee_rng_mod_exit(void) | ||
297 | { | ||
298 | driver_unregister(&optee_rng_driver.driver); | ||
299 | } | ||
300 | |||
301 | module_init(optee_rng_mod_init); | ||
302 | module_exit(optee_rng_mod_exit); | ||
303 | |||
304 | MODULE_LICENSE("GPL v2"); | ||
305 | MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>"); | ||
306 | MODULE_DESCRIPTION("OP-TEE based random number generator driver"); | ||
diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig index 7ddacae5d0b1..1adcccfa7829 100644 --- a/drivers/clk/tegra/Kconfig +++ b/drivers/clk/tegra/Kconfig | |||
@@ -5,3 +5,8 @@ config TEGRA_CLK_EMC | |||
5 | config CLK_TEGRA_BPMP | 5 | config CLK_TEGRA_BPMP |
6 | def_bool y | 6 | def_bool y |
7 | depends on TEGRA_BPMP | 7 | depends on TEGRA_BPMP |
8 | |||
9 | config TEGRA_CLK_DFLL | ||
10 | depends on ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC | ||
11 | select PM_OPP | ||
12 | def_bool y | ||
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 6507acc843c7..4812e45c2214 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile | |||
@@ -20,7 +20,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o | |||
20 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o | 20 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o |
21 | obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o | 21 | obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o |
22 | obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o | 22 | obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o |
23 | obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124-dfll-fcpu.o | 23 | obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o |
24 | obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o | 24 | obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o |
25 | obj-y += cvb.o | 25 | obj-y += cvb.o |
26 | obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o | 26 | obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o |
diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index 609e363dabf8..0400e5b1d627 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * clk-dfll.c - Tegra DFLL clock source common code | 2 | * clk-dfll.c - Tegra DFLL clock source common code |
3 | * | 3 | * |
4 | * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved. | 4 | * Copyright (C) 2012-2019 NVIDIA Corporation. All rights reserved. |
5 | * | 5 | * |
6 | * Aleksandr Frid <afrid@nvidia.com> | 6 | * Aleksandr Frid <afrid@nvidia.com> |
7 | * Paul Walmsley <pwalmsley@nvidia.com> | 7 | * Paul Walmsley <pwalmsley@nvidia.com> |
@@ -47,6 +47,7 @@ | |||
47 | #include <linux/kernel.h> | 47 | #include <linux/kernel.h> |
48 | #include <linux/module.h> | 48 | #include <linux/module.h> |
49 | #include <linux/of.h> | 49 | #include <linux/of.h> |
50 | #include <linux/pinctrl/consumer.h> | ||
50 | #include <linux/pm_opp.h> | 51 | #include <linux/pm_opp.h> |
51 | #include <linux/pm_runtime.h> | 52 | #include <linux/pm_runtime.h> |
52 | #include <linux/regmap.h> | 53 | #include <linux/regmap.h> |
@@ -243,6 +244,12 @@ enum dfll_tune_range { | |||
243 | DFLL_TUNE_LOW = 1, | 244 | DFLL_TUNE_LOW = 1, |
244 | }; | 245 | }; |
245 | 246 | ||
247 | |||
248 | enum tegra_dfll_pmu_if { | ||
249 | TEGRA_DFLL_PMU_I2C = 0, | ||
250 | TEGRA_DFLL_PMU_PWM = 1, | ||
251 | }; | ||
252 | |||
246 | /** | 253 | /** |
247 | * struct dfll_rate_req - target DFLL rate request data | 254 | * struct dfll_rate_req - target DFLL rate request data |
248 | * @rate: target frequency, after the postscaling | 255 | * @rate: target frequency, after the postscaling |
@@ -300,10 +307,19 @@ struct tegra_dfll { | |||
300 | u32 i2c_reg; | 307 | u32 i2c_reg; |
301 | u32 i2c_slave_addr; | 308 | u32 i2c_slave_addr; |
302 | 309 | ||
303 | /* i2c_lut array entries are regulator framework selectors */ | 310 | /* lut array entries are regulator framework selectors or PWM values*/ |
304 | unsigned i2c_lut[MAX_DFLL_VOLTAGES]; | 311 | unsigned lut[MAX_DFLL_VOLTAGES]; |
305 | int i2c_lut_size; | 312 | unsigned long lut_uv[MAX_DFLL_VOLTAGES]; |
306 | u8 lut_min, lut_max, lut_safe; | 313 | int lut_size; |
314 | u8 lut_bottom, lut_min, lut_max, lut_safe; | ||
315 | |||
316 | /* PWM interface */ | ||
317 | enum tegra_dfll_pmu_if pmu_if; | ||
318 | unsigned long pwm_rate; | ||
319 | struct pinctrl *pwm_pin; | ||
320 | struct pinctrl_state *pwm_enable_state; | ||
321 | struct pinctrl_state *pwm_disable_state; | ||
322 | u32 reg_init_uV; | ||
307 | }; | 323 | }; |
308 | 324 | ||
309 | #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw) | 325 | #define clk_hw_to_dfll(_hw) container_of(_hw, struct tegra_dfll, dfll_clk_hw) |
@@ -490,6 +506,34 @@ static void dfll_set_mode(struct tegra_dfll *td, | |||
490 | } | 506 | } |
491 | 507 | ||
492 | /* | 508 | /* |
509 | * DVCO rate control | ||
510 | */ | ||
511 | |||
512 | static unsigned long get_dvco_rate_below(struct tegra_dfll *td, u8 out_min) | ||
513 | { | ||
514 | struct dev_pm_opp *opp; | ||
515 | unsigned long rate, prev_rate; | ||
516 | unsigned long uv, min_uv; | ||
517 | |||
518 | min_uv = td->lut_uv[out_min]; | ||
519 | for (rate = 0, prev_rate = 0; ; rate++) { | ||
520 | opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); | ||
521 | if (IS_ERR(opp)) | ||
522 | break; | ||
523 | |||
524 | uv = dev_pm_opp_get_voltage(opp); | ||
525 | dev_pm_opp_put(opp); | ||
526 | |||
527 | if (uv && uv > min_uv) | ||
528 | return prev_rate; | ||
529 | |||
530 | prev_rate = rate; | ||
531 | } | ||
532 | |||
533 | return prev_rate; | ||
534 | } | ||
535 | |||
536 | /* | ||
493 | * DFLL-to-I2C controller interface | 537 | * DFLL-to-I2C controller interface |
494 | */ | 538 | */ |
495 | 539 | ||
@@ -518,6 +562,118 @@ static int dfll_i2c_set_output_enabled(struct tegra_dfll *td, bool enable) | |||
518 | return 0; | 562 | return 0; |
519 | } | 563 | } |
520 | 564 | ||
565 | |||
566 | /* | ||
567 | * DFLL-to-PWM controller interface | ||
568 | */ | ||
569 | |||
570 | /** | ||
571 | * dfll_pwm_set_output_enabled - enable/disable PWM voltage requests | ||
572 | * @td: DFLL instance | ||
573 | * @enable: whether to enable or disable the PWM voltage requests | ||
574 | * | ||
575 | * Set the master enable control for PWM control value updates. If disabled, | ||
576 | * then the PWM signal is not driven. Also configure the PWM output pad | ||
577 | * to the appropriate state. | ||
578 | */ | ||
579 | static int dfll_pwm_set_output_enabled(struct tegra_dfll *td, bool enable) | ||
580 | { | ||
581 | int ret; | ||
582 | u32 val, div; | ||
583 | |||
584 | if (enable) { | ||
585 | ret = pinctrl_select_state(td->pwm_pin, td->pwm_enable_state); | ||
586 | if (ret < 0) { | ||
587 | dev_err(td->dev, "setting enable state failed\n"); | ||
588 | return -EINVAL; | ||
589 | } | ||
590 | val = dfll_readl(td, DFLL_OUTPUT_CFG); | ||
591 | val &= ~DFLL_OUTPUT_CFG_PWM_DIV_MASK; | ||
592 | div = DIV_ROUND_UP(td->ref_rate, td->pwm_rate); | ||
593 | val |= (div << DFLL_OUTPUT_CFG_PWM_DIV_SHIFT) | ||
594 | & DFLL_OUTPUT_CFG_PWM_DIV_MASK; | ||
595 | dfll_writel(td, val, DFLL_OUTPUT_CFG); | ||
596 | dfll_wmb(td); | ||
597 | |||
598 | val |= DFLL_OUTPUT_CFG_PWM_ENABLE; | ||
599 | dfll_writel(td, val, DFLL_OUTPUT_CFG); | ||
600 | dfll_wmb(td); | ||
601 | } else { | ||
602 | ret = pinctrl_select_state(td->pwm_pin, td->pwm_disable_state); | ||
603 | if (ret < 0) | ||
604 | dev_warn(td->dev, "setting disable state failed\n"); | ||
605 | |||
606 | val = dfll_readl(td, DFLL_OUTPUT_CFG); | ||
607 | val &= ~DFLL_OUTPUT_CFG_PWM_ENABLE; | ||
608 | dfll_writel(td, val, DFLL_OUTPUT_CFG); | ||
609 | dfll_wmb(td); | ||
610 | } | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | /** | ||
616 | * dfll_set_force_output_value - set fixed value for force output | ||
617 | * @td: DFLL instance | ||
618 | * @out_val: value to force output | ||
619 | * | ||
620 | * Set the fixed value for force output, DFLL will output this value when | ||
621 | * force output is enabled. | ||
622 | */ | ||
623 | static u32 dfll_set_force_output_value(struct tegra_dfll *td, u8 out_val) | ||
624 | { | ||
625 | u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE); | ||
626 | |||
627 | val = (val & DFLL_OUTPUT_FORCE_ENABLE) | (out_val & OUT_MASK); | ||
628 | dfll_writel(td, val, DFLL_OUTPUT_FORCE); | ||
629 | dfll_wmb(td); | ||
630 | |||
631 | return dfll_readl(td, DFLL_OUTPUT_FORCE); | ||
632 | } | ||
633 | |||
634 | /** | ||
635 | * dfll_set_force_output_enabled - enable/disable force output | ||
636 | * @td: DFLL instance | ||
637 | * @enable: whether to enable or disable the force output | ||
638 | * | ||
639 | * Set the enable control for fouce output with fixed value. | ||
640 | */ | ||
641 | static void dfll_set_force_output_enabled(struct tegra_dfll *td, bool enable) | ||
642 | { | ||
643 | u32 val = dfll_readl(td, DFLL_OUTPUT_FORCE); | ||
644 | |||
645 | if (enable) | ||
646 | val |= DFLL_OUTPUT_FORCE_ENABLE; | ||
647 | else | ||
648 | val &= ~DFLL_OUTPUT_FORCE_ENABLE; | ||
649 | |||
650 | dfll_writel(td, val, DFLL_OUTPUT_FORCE); | ||
651 | dfll_wmb(td); | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * dfll_force_output - force output a fixed value | ||
656 | * @td: DFLL instance | ||
657 | * @out_sel: value to force output | ||
658 | * | ||
659 | * Set the fixed value for force output, DFLL will output this value. | ||
660 | */ | ||
661 | static int dfll_force_output(struct tegra_dfll *td, unsigned int out_sel) | ||
662 | { | ||
663 | u32 val; | ||
664 | |||
665 | if (out_sel > OUT_MASK) | ||
666 | return -EINVAL; | ||
667 | |||
668 | val = dfll_set_force_output_value(td, out_sel); | ||
669 | if ((td->mode < DFLL_CLOSED_LOOP) && | ||
670 | !(val & DFLL_OUTPUT_FORCE_ENABLE)) { | ||
671 | dfll_set_force_output_enabled(td, true); | ||
672 | } | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
521 | /** | 677 | /** |
522 | * dfll_load_lut - load the voltage lookup table | 678 | * dfll_load_lut - load the voltage lookup table |
523 | * @td: struct tegra_dfll * | 679 | * @td: struct tegra_dfll * |
@@ -539,7 +695,7 @@ static void dfll_load_i2c_lut(struct tegra_dfll *td) | |||
539 | lut_index = i; | 695 | lut_index = i; |
540 | 696 | ||
541 | val = regulator_list_hardware_vsel(td->vdd_reg, | 697 | val = regulator_list_hardware_vsel(td->vdd_reg, |
542 | td->i2c_lut[lut_index]); | 698 | td->lut[lut_index]); |
543 | __raw_writel(val, td->lut_base + i * 4); | 699 | __raw_writel(val, td->lut_base + i * 4); |
544 | } | 700 | } |
545 | 701 | ||
@@ -594,24 +750,41 @@ static void dfll_init_out_if(struct tegra_dfll *td) | |||
594 | { | 750 | { |
595 | u32 val; | 751 | u32 val; |
596 | 752 | ||
597 | td->lut_min = 0; | 753 | td->lut_min = td->lut_bottom; |
598 | td->lut_max = td->i2c_lut_size - 1; | 754 | td->lut_max = td->lut_size - 1; |
599 | td->lut_safe = td->lut_min + 1; | 755 | td->lut_safe = td->lut_min + (td->lut_min < td->lut_max ? 1 : 0); |
756 | |||
757 | /* clear DFLL_OUTPUT_CFG before setting new value */ | ||
758 | dfll_writel(td, 0, DFLL_OUTPUT_CFG); | ||
759 | dfll_wmb(td); | ||
600 | 760 | ||
601 | dfll_i2c_writel(td, 0, DFLL_OUTPUT_CFG); | ||
602 | val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) | | 761 | val = (td->lut_safe << DFLL_OUTPUT_CFG_SAFE_SHIFT) | |
603 | (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) | | 762 | (td->lut_max << DFLL_OUTPUT_CFG_MAX_SHIFT) | |
604 | (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT); | 763 | (td->lut_min << DFLL_OUTPUT_CFG_MIN_SHIFT); |
605 | dfll_i2c_writel(td, val, DFLL_OUTPUT_CFG); | 764 | dfll_writel(td, val, DFLL_OUTPUT_CFG); |
606 | dfll_i2c_wmb(td); | 765 | dfll_wmb(td); |
607 | 766 | ||
608 | dfll_writel(td, 0, DFLL_OUTPUT_FORCE); | 767 | dfll_writel(td, 0, DFLL_OUTPUT_FORCE); |
609 | dfll_i2c_writel(td, 0, DFLL_INTR_EN); | 768 | dfll_i2c_writel(td, 0, DFLL_INTR_EN); |
610 | dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK, | 769 | dfll_i2c_writel(td, DFLL_INTR_MAX_MASK | DFLL_INTR_MIN_MASK, |
611 | DFLL_INTR_STS); | 770 | DFLL_INTR_STS); |
612 | 771 | ||
613 | dfll_load_i2c_lut(td); | 772 | if (td->pmu_if == TEGRA_DFLL_PMU_PWM) { |
614 | dfll_init_i2c_if(td); | 773 | u32 vinit = td->reg_init_uV; |
774 | int vstep = td->soc->alignment.step_uv; | ||
775 | unsigned long vmin = td->lut_uv[0]; | ||
776 | |||
777 | /* set initial voltage */ | ||
778 | if ((vinit >= vmin) && vstep) { | ||
779 | unsigned int vsel; | ||
780 | |||
781 | vsel = DIV_ROUND_UP((vinit - vmin), vstep); | ||
782 | dfll_force_output(td, vsel); | ||
783 | } | ||
784 | } else { | ||
785 | dfll_load_i2c_lut(td); | ||
786 | dfll_init_i2c_if(td); | ||
787 | } | ||
615 | } | 788 | } |
616 | 789 | ||
617 | /* | 790 | /* |
@@ -631,17 +804,17 @@ static void dfll_init_out_if(struct tegra_dfll *td) | |||
631 | static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate) | 804 | static int find_lut_index_for_rate(struct tegra_dfll *td, unsigned long rate) |
632 | { | 805 | { |
633 | struct dev_pm_opp *opp; | 806 | struct dev_pm_opp *opp; |
634 | int i, uv; | 807 | int i, align_step; |
635 | 808 | ||
636 | opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); | 809 | opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); |
637 | if (IS_ERR(opp)) | 810 | if (IS_ERR(opp)) |
638 | return PTR_ERR(opp); | 811 | return PTR_ERR(opp); |
639 | 812 | ||
640 | uv = dev_pm_opp_get_voltage(opp); | 813 | align_step = dev_pm_opp_get_voltage(opp) / td->soc->alignment.step_uv; |
641 | dev_pm_opp_put(opp); | 814 | dev_pm_opp_put(opp); |
642 | 815 | ||
643 | for (i = 0; i < td->i2c_lut_size; i++) { | 816 | for (i = td->lut_bottom; i < td->lut_size; i++) { |
644 | if (regulator_list_voltage(td->vdd_reg, td->i2c_lut[i]) == uv) | 817 | if ((td->lut_uv[i] / td->soc->alignment.step_uv) >= align_step) |
645 | return i; | 818 | return i; |
646 | } | 819 | } |
647 | 820 | ||
@@ -863,9 +1036,14 @@ static int dfll_lock(struct tegra_dfll *td) | |||
863 | return -EINVAL; | 1036 | return -EINVAL; |
864 | } | 1037 | } |
865 | 1038 | ||
866 | dfll_i2c_set_output_enabled(td, true); | 1039 | if (td->pmu_if == TEGRA_DFLL_PMU_PWM) |
1040 | dfll_pwm_set_output_enabled(td, true); | ||
1041 | else | ||
1042 | dfll_i2c_set_output_enabled(td, true); | ||
1043 | |||
867 | dfll_set_mode(td, DFLL_CLOSED_LOOP); | 1044 | dfll_set_mode(td, DFLL_CLOSED_LOOP); |
868 | dfll_set_frequency_request(td, req); | 1045 | dfll_set_frequency_request(td, req); |
1046 | dfll_set_force_output_enabled(td, false); | ||
869 | return 0; | 1047 | return 0; |
870 | 1048 | ||
871 | default: | 1049 | default: |
@@ -889,7 +1067,10 @@ static int dfll_unlock(struct tegra_dfll *td) | |||
889 | case DFLL_CLOSED_LOOP: | 1067 | case DFLL_CLOSED_LOOP: |
890 | dfll_set_open_loop_config(td); | 1068 | dfll_set_open_loop_config(td); |
891 | dfll_set_mode(td, DFLL_OPEN_LOOP); | 1069 | dfll_set_mode(td, DFLL_OPEN_LOOP); |
892 | dfll_i2c_set_output_enabled(td, false); | 1070 | if (td->pmu_if == TEGRA_DFLL_PMU_PWM) |
1071 | dfll_pwm_set_output_enabled(td, false); | ||
1072 | else | ||
1073 | dfll_i2c_set_output_enabled(td, false); | ||
893 | return 0; | 1074 | return 0; |
894 | 1075 | ||
895 | case DFLL_OPEN_LOOP: | 1076 | case DFLL_OPEN_LOOP: |
@@ -1171,15 +1352,17 @@ static int attr_registers_show(struct seq_file *s, void *data) | |||
1171 | seq_printf(s, "[0x%02x] = 0x%08x\n", offs, | 1352 | seq_printf(s, "[0x%02x] = 0x%08x\n", offs, |
1172 | dfll_i2c_readl(td, offs)); | 1353 | dfll_i2c_readl(td, offs)); |
1173 | 1354 | ||
1174 | seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n"); | 1355 | if (td->pmu_if == TEGRA_DFLL_PMU_I2C) { |
1175 | offs = DFLL_I2C_CLK_DIVISOR; | 1356 | seq_puts(s, "\nINTEGRATED I2C CONTROLLER REGISTERS:\n"); |
1176 | seq_printf(s, "[0x%02x] = 0x%08x\n", offs, | 1357 | offs = DFLL_I2C_CLK_DIVISOR; |
1177 | __raw_readl(td->i2c_controller_base + offs)); | ||
1178 | |||
1179 | seq_puts(s, "\nLUT:\n"); | ||
1180 | for (offs = 0; offs < 4 * MAX_DFLL_VOLTAGES; offs += 4) | ||
1181 | seq_printf(s, "[0x%02x] = 0x%08x\n", offs, | 1358 | seq_printf(s, "[0x%02x] = 0x%08x\n", offs, |
1182 | __raw_readl(td->lut_base + offs)); | 1359 | __raw_readl(td->i2c_controller_base + offs)); |
1360 | |||
1361 | seq_puts(s, "\nLUT:\n"); | ||
1362 | for (offs = 0; offs < 4 * MAX_DFLL_VOLTAGES; offs += 4) | ||
1363 | seq_printf(s, "[0x%02x] = 0x%08x\n", offs, | ||
1364 | __raw_readl(td->lut_base + offs)); | ||
1365 | } | ||
1183 | 1366 | ||
1184 | return 0; | 1367 | return 0; |
1185 | } | 1368 | } |
@@ -1349,15 +1532,21 @@ di_err1: | |||
1349 | */ | 1532 | */ |
1350 | static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV) | 1533 | static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV) |
1351 | { | 1534 | { |
1352 | int i, n_voltages, reg_uV; | 1535 | int i, n_voltages, reg_uV,reg_volt_id, align_step; |
1536 | |||
1537 | if (WARN_ON(td->pmu_if == TEGRA_DFLL_PMU_PWM)) | ||
1538 | return -EINVAL; | ||
1353 | 1539 | ||
1540 | align_step = uV / td->soc->alignment.step_uv; | ||
1354 | n_voltages = regulator_count_voltages(td->vdd_reg); | 1541 | n_voltages = regulator_count_voltages(td->vdd_reg); |
1355 | for (i = 0; i < n_voltages; i++) { | 1542 | for (i = 0; i < n_voltages; i++) { |
1356 | reg_uV = regulator_list_voltage(td->vdd_reg, i); | 1543 | reg_uV = regulator_list_voltage(td->vdd_reg, i); |
1357 | if (reg_uV < 0) | 1544 | if (reg_uV < 0) |
1358 | break; | 1545 | break; |
1359 | 1546 | ||
1360 | if (uV == reg_uV) | 1547 | reg_volt_id = reg_uV / td->soc->alignment.step_uv; |
1548 | |||
1549 | if (align_step == reg_volt_id) | ||
1361 | return i; | 1550 | return i; |
1362 | } | 1551 | } |
1363 | 1552 | ||
@@ -1371,15 +1560,21 @@ static int find_vdd_map_entry_exact(struct tegra_dfll *td, int uV) | |||
1371 | * */ | 1560 | * */ |
1372 | static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV) | 1561 | static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV) |
1373 | { | 1562 | { |
1374 | int i, n_voltages, reg_uV; | 1563 | int i, n_voltages, reg_uV, reg_volt_id, align_step; |
1375 | 1564 | ||
1565 | if (WARN_ON(td->pmu_if == TEGRA_DFLL_PMU_PWM)) | ||
1566 | return -EINVAL; | ||
1567 | |||
1568 | align_step = uV / td->soc->alignment.step_uv; | ||
1376 | n_voltages = regulator_count_voltages(td->vdd_reg); | 1569 | n_voltages = regulator_count_voltages(td->vdd_reg); |
1377 | for (i = 0; i < n_voltages; i++) { | 1570 | for (i = 0; i < n_voltages; i++) { |
1378 | reg_uV = regulator_list_voltage(td->vdd_reg, i); | 1571 | reg_uV = regulator_list_voltage(td->vdd_reg, i); |
1379 | if (reg_uV < 0) | 1572 | if (reg_uV < 0) |
1380 | break; | 1573 | break; |
1381 | 1574 | ||
1382 | if (uV <= reg_uV) | 1575 | reg_volt_id = reg_uV / td->soc->alignment.step_uv; |
1576 | |||
1577 | if (align_step <= reg_volt_id) | ||
1383 | return i; | 1578 | return i; |
1384 | } | 1579 | } |
1385 | 1580 | ||
@@ -1387,9 +1582,61 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV) | |||
1387 | return -EINVAL; | 1582 | return -EINVAL; |
1388 | } | 1583 | } |
1389 | 1584 | ||
1585 | /* | ||
1586 | * dfll_build_pwm_lut - build the PWM regulator lookup table | ||
1587 | * @td: DFLL instance | ||
1588 | * @v_max: Vmax from OPP table | ||
1589 | * | ||
1590 | * Look-up table in h/w is ignored when PWM is used as DFLL interface to PMIC. | ||
1591 | * In this case closed loop output is controlling duty cycle directly. The s/w | ||
1592 | * look-up that maps PWM duty cycle to voltage is still built by this function. | ||
1593 | */ | ||
1594 | static int dfll_build_pwm_lut(struct tegra_dfll *td, unsigned long v_max) | ||
1595 | { | ||
1596 | int i; | ||
1597 | unsigned long rate, reg_volt; | ||
1598 | u8 lut_bottom = MAX_DFLL_VOLTAGES; | ||
1599 | int v_min = td->soc->cvb->min_millivolts * 1000; | ||
1600 | |||
1601 | for (i = 0; i < MAX_DFLL_VOLTAGES; i++) { | ||
1602 | reg_volt = td->lut_uv[i]; | ||
1603 | |||
1604 | /* since opp voltage is exact mv */ | ||
1605 | reg_volt = (reg_volt / 1000) * 1000; | ||
1606 | if (reg_volt > v_max) | ||
1607 | break; | ||
1608 | |||
1609 | td->lut[i] = i; | ||
1610 | if ((lut_bottom == MAX_DFLL_VOLTAGES) && (reg_volt >= v_min)) | ||
1611 | lut_bottom = i; | ||
1612 | } | ||
1613 | |||
1614 | /* determine voltage boundaries */ | ||
1615 | td->lut_size = i; | ||
1616 | if ((lut_bottom == MAX_DFLL_VOLTAGES) || | ||
1617 | (lut_bottom + 1 >= td->lut_size)) { | ||
1618 | dev_err(td->dev, "no voltage above DFLL minimum %d mV\n", | ||
1619 | td->soc->cvb->min_millivolts); | ||
1620 | return -EINVAL; | ||
1621 | } | ||
1622 | td->lut_bottom = lut_bottom; | ||
1623 | |||
1624 | /* determine rate boundaries */ | ||
1625 | rate = get_dvco_rate_below(td, td->lut_bottom); | ||
1626 | if (!rate) { | ||
1627 | dev_err(td->dev, "no opp below DFLL minimum voltage %d mV\n", | ||
1628 | td->soc->cvb->min_millivolts); | ||
1629 | return -EINVAL; | ||
1630 | } | ||
1631 | td->dvco_rate_min = rate; | ||
1632 | |||
1633 | return 0; | ||
1634 | } | ||
1635 | |||
1390 | /** | 1636 | /** |
1391 | * dfll_build_i2c_lut - build the I2C voltage register lookup table | 1637 | * dfll_build_i2c_lut - build the I2C voltage register lookup table |
1392 | * @td: DFLL instance | 1638 | * @td: DFLL instance |
1639 | * @v_max: Vmax from OPP table | ||
1393 | * | 1640 | * |
1394 | * The DFLL hardware has 33 bytes of look-up table RAM that must be filled with | 1641 | * The DFLL hardware has 33 bytes of look-up table RAM that must be filled with |
1395 | * PMIC voltage register values that span the entire DFLL operating range. | 1642 | * PMIC voltage register values that span the entire DFLL operating range. |
@@ -1397,33 +1644,24 @@ static int find_vdd_map_entry_min(struct tegra_dfll *td, int uV) | |||
1397 | * the soc-specific platform driver (td->soc->opp_dev) and the PMIC | 1644 | * the soc-specific platform driver (td->soc->opp_dev) and the PMIC |
1398 | * register-to-voltage mapping queried from the regulator framework. | 1645 | * register-to-voltage mapping queried from the regulator framework. |
1399 | * | 1646 | * |
1400 | * On success, fills in td->i2c_lut and returns 0, or -err on failure. | 1647 | * On success, fills in td->lut and returns 0, or -err on failure. |
1401 | */ | 1648 | */ |
1402 | static int dfll_build_i2c_lut(struct tegra_dfll *td) | 1649 | static int dfll_build_i2c_lut(struct tegra_dfll *td, unsigned long v_max) |
1403 | { | 1650 | { |
1651 | unsigned long rate, v, v_opp; | ||
1404 | int ret = -EINVAL; | 1652 | int ret = -EINVAL; |
1405 | int j, v, v_max, v_opp; | 1653 | int j, selector, lut; |
1406 | int selector; | ||
1407 | unsigned long rate; | ||
1408 | struct dev_pm_opp *opp; | ||
1409 | int lut; | ||
1410 | |||
1411 | rate = ULONG_MAX; | ||
1412 | opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate); | ||
1413 | if (IS_ERR(opp)) { | ||
1414 | dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n"); | ||
1415 | goto out; | ||
1416 | } | ||
1417 | v_max = dev_pm_opp_get_voltage(opp); | ||
1418 | dev_pm_opp_put(opp); | ||
1419 | 1654 | ||
1420 | v = td->soc->cvb->min_millivolts * 1000; | 1655 | v = td->soc->cvb->min_millivolts * 1000; |
1421 | lut = find_vdd_map_entry_exact(td, v); | 1656 | lut = find_vdd_map_entry_exact(td, v); |
1422 | if (lut < 0) | 1657 | if (lut < 0) |
1423 | goto out; | 1658 | goto out; |
1424 | td->i2c_lut[0] = lut; | 1659 | td->lut[0] = lut; |
1660 | td->lut_bottom = 0; | ||
1425 | 1661 | ||
1426 | for (j = 1, rate = 0; ; rate++) { | 1662 | for (j = 1, rate = 0; ; rate++) { |
1663 | struct dev_pm_opp *opp; | ||
1664 | |||
1427 | opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); | 1665 | opp = dev_pm_opp_find_freq_ceil(td->soc->dev, &rate); |
1428 | if (IS_ERR(opp)) | 1666 | if (IS_ERR(opp)) |
1429 | break; | 1667 | break; |
@@ -1435,39 +1673,64 @@ static int dfll_build_i2c_lut(struct tegra_dfll *td) | |||
1435 | dev_pm_opp_put(opp); | 1673 | dev_pm_opp_put(opp); |
1436 | 1674 | ||
1437 | for (;;) { | 1675 | for (;;) { |
1438 | v += max(1, (v_max - v) / (MAX_DFLL_VOLTAGES - j)); | 1676 | v += max(1UL, (v_max - v) / (MAX_DFLL_VOLTAGES - j)); |
1439 | if (v >= v_opp) | 1677 | if (v >= v_opp) |
1440 | break; | 1678 | break; |
1441 | 1679 | ||
1442 | selector = find_vdd_map_entry_min(td, v); | 1680 | selector = find_vdd_map_entry_min(td, v); |
1443 | if (selector < 0) | 1681 | if (selector < 0) |
1444 | goto out; | 1682 | goto out; |
1445 | if (selector != td->i2c_lut[j - 1]) | 1683 | if (selector != td->lut[j - 1]) |
1446 | td->i2c_lut[j++] = selector; | 1684 | td->lut[j++] = selector; |
1447 | } | 1685 | } |
1448 | 1686 | ||
1449 | v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp; | 1687 | v = (j == MAX_DFLL_VOLTAGES - 1) ? v_max : v_opp; |
1450 | selector = find_vdd_map_entry_exact(td, v); | 1688 | selector = find_vdd_map_entry_exact(td, v); |
1451 | if (selector < 0) | 1689 | if (selector < 0) |
1452 | goto out; | 1690 | goto out; |
1453 | if (selector != td->i2c_lut[j - 1]) | 1691 | if (selector != td->lut[j - 1]) |
1454 | td->i2c_lut[j++] = selector; | 1692 | td->lut[j++] = selector; |
1455 | 1693 | ||
1456 | if (v >= v_max) | 1694 | if (v >= v_max) |
1457 | break; | 1695 | break; |
1458 | } | 1696 | } |
1459 | td->i2c_lut_size = j; | 1697 | td->lut_size = j; |
1460 | 1698 | ||
1461 | if (!td->dvco_rate_min) | 1699 | if (!td->dvco_rate_min) |
1462 | dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n", | 1700 | dev_err(td->dev, "no opp above DFLL minimum voltage %d mV\n", |
1463 | td->soc->cvb->min_millivolts); | 1701 | td->soc->cvb->min_millivolts); |
1464 | else | 1702 | else { |
1465 | ret = 0; | 1703 | ret = 0; |
1704 | for (j = 0; j < td->lut_size; j++) | ||
1705 | td->lut_uv[j] = | ||
1706 | regulator_list_voltage(td->vdd_reg, | ||
1707 | td->lut[j]); | ||
1708 | } | ||
1466 | 1709 | ||
1467 | out: | 1710 | out: |
1468 | return ret; | 1711 | return ret; |
1469 | } | 1712 | } |
1470 | 1713 | ||
1714 | static int dfll_build_lut(struct tegra_dfll *td) | ||
1715 | { | ||
1716 | unsigned long rate, v_max; | ||
1717 | struct dev_pm_opp *opp; | ||
1718 | |||
1719 | rate = ULONG_MAX; | ||
1720 | opp = dev_pm_opp_find_freq_floor(td->soc->dev, &rate); | ||
1721 | if (IS_ERR(opp)) { | ||
1722 | dev_err(td->dev, "couldn't get vmax opp, empty opp table?\n"); | ||
1723 | return -EINVAL; | ||
1724 | } | ||
1725 | v_max = dev_pm_opp_get_voltage(opp); | ||
1726 | dev_pm_opp_put(opp); | ||
1727 | |||
1728 | if (td->pmu_if == TEGRA_DFLL_PMU_PWM) | ||
1729 | return dfll_build_pwm_lut(td, v_max); | ||
1730 | else | ||
1731 | return dfll_build_i2c_lut(td, v_max); | ||
1732 | } | ||
1733 | |||
1471 | /** | 1734 | /** |
1472 | * read_dt_param - helper function for reading required parameters from the DT | 1735 | * read_dt_param - helper function for reading required parameters from the DT |
1473 | * @td: DFLL instance | 1736 | * @td: DFLL instance |
@@ -1526,11 +1789,56 @@ static int dfll_fetch_i2c_params(struct tegra_dfll *td) | |||
1526 | } | 1789 | } |
1527 | td->i2c_reg = vsel_reg; | 1790 | td->i2c_reg = vsel_reg; |
1528 | 1791 | ||
1529 | ret = dfll_build_i2c_lut(td); | 1792 | return 0; |
1530 | if (ret) { | 1793 | } |
1531 | dev_err(td->dev, "couldn't build I2C LUT\n"); | 1794 | |
1795 | static int dfll_fetch_pwm_params(struct tegra_dfll *td) | ||
1796 | { | ||
1797 | int ret, i; | ||
1798 | u32 pwm_period; | ||
1799 | |||
1800 | if (!td->soc->alignment.step_uv || !td->soc->alignment.offset_uv) { | ||
1801 | dev_err(td->dev, | ||
1802 | "Missing step or alignment info for PWM regulator"); | ||
1803 | return -EINVAL; | ||
1804 | } | ||
1805 | for (i = 0; i < MAX_DFLL_VOLTAGES; i++) | ||
1806 | td->lut_uv[i] = td->soc->alignment.offset_uv + | ||
1807 | i * td->soc->alignment.step_uv; | ||
1808 | |||
1809 | ret = read_dt_param(td, "nvidia,pwm-tristate-microvolts", | ||
1810 | &td->reg_init_uV); | ||
1811 | if (!ret) { | ||
1812 | dev_err(td->dev, "couldn't get initialized voltage\n"); | ||
1813 | return ret; | ||
1814 | } | ||
1815 | |||
1816 | ret = read_dt_param(td, "nvidia,pwm-period-nanoseconds", &pwm_period); | ||
1817 | if (!ret) { | ||
1818 | dev_err(td->dev, "couldn't get PWM period\n"); | ||
1532 | return ret; | 1819 | return ret; |
1533 | } | 1820 | } |
1821 | td->pwm_rate = (NSEC_PER_SEC / pwm_period) * (MAX_DFLL_VOLTAGES - 1); | ||
1822 | |||
1823 | td->pwm_pin = devm_pinctrl_get(td->dev); | ||
1824 | if (IS_ERR(td->pwm_pin)) { | ||
1825 | dev_err(td->dev, "DT: missing pinctrl device\n"); | ||
1826 | return PTR_ERR(td->pwm_pin); | ||
1827 | } | ||
1828 | |||
1829 | td->pwm_enable_state = pinctrl_lookup_state(td->pwm_pin, | ||
1830 | "dvfs_pwm_enable"); | ||
1831 | if (IS_ERR(td->pwm_enable_state)) { | ||
1832 | dev_err(td->dev, "DT: missing pwm enabled state\n"); | ||
1833 | return PTR_ERR(td->pwm_enable_state); | ||
1834 | } | ||
1835 | |||
1836 | td->pwm_disable_state = pinctrl_lookup_state(td->pwm_pin, | ||
1837 | "dvfs_pwm_disable"); | ||
1838 | if (IS_ERR(td->pwm_disable_state)) { | ||
1839 | dev_err(td->dev, "DT: missing pwm disabled state\n"); | ||
1840 | return PTR_ERR(td->pwm_disable_state); | ||
1841 | } | ||
1534 | 1842 | ||
1535 | return 0; | 1843 | return 0; |
1536 | } | 1844 | } |
@@ -1597,16 +1905,6 @@ int tegra_dfll_register(struct platform_device *pdev, | |||
1597 | 1905 | ||
1598 | td->soc = soc; | 1906 | td->soc = soc; |
1599 | 1907 | ||
1600 | td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu"); | ||
1601 | if (IS_ERR(td->vdd_reg)) { | ||
1602 | ret = PTR_ERR(td->vdd_reg); | ||
1603 | if (ret != -EPROBE_DEFER) | ||
1604 | dev_err(td->dev, "couldn't get vdd_cpu regulator: %d\n", | ||
1605 | ret); | ||
1606 | |||
1607 | return ret; | ||
1608 | } | ||
1609 | |||
1610 | td->dvco_rst = devm_reset_control_get(td->dev, "dvco"); | 1908 | td->dvco_rst = devm_reset_control_get(td->dev, "dvco"); |
1611 | if (IS_ERR(td->dvco_rst)) { | 1909 | if (IS_ERR(td->dvco_rst)) { |
1612 | dev_err(td->dev, "couldn't get dvco reset\n"); | 1910 | dev_err(td->dev, "couldn't get dvco reset\n"); |
@@ -1619,10 +1917,27 @@ int tegra_dfll_register(struct platform_device *pdev, | |||
1619 | return ret; | 1917 | return ret; |
1620 | } | 1918 | } |
1621 | 1919 | ||
1622 | ret = dfll_fetch_i2c_params(td); | 1920 | if (of_property_read_bool(td->dev->of_node, "nvidia,pwm-to-pmic")) { |
1921 | td->pmu_if = TEGRA_DFLL_PMU_PWM; | ||
1922 | ret = dfll_fetch_pwm_params(td); | ||
1923 | } else { | ||
1924 | td->vdd_reg = devm_regulator_get(td->dev, "vdd-cpu"); | ||
1925 | if (IS_ERR(td->vdd_reg)) { | ||
1926 | dev_err(td->dev, "couldn't get vdd_cpu regulator\n"); | ||
1927 | return PTR_ERR(td->vdd_reg); | ||
1928 | } | ||
1929 | td->pmu_if = TEGRA_DFLL_PMU_I2C; | ||
1930 | ret = dfll_fetch_i2c_params(td); | ||
1931 | } | ||
1623 | if (ret) | 1932 | if (ret) |
1624 | return ret; | 1933 | return ret; |
1625 | 1934 | ||
1935 | ret = dfll_build_lut(td); | ||
1936 | if (ret) { | ||
1937 | dev_err(td->dev, "couldn't build LUT\n"); | ||
1938 | return ret; | ||
1939 | } | ||
1940 | |||
1626 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1941 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1627 | if (!mem) { | 1942 | if (!mem) { |
1628 | dev_err(td->dev, "no control register resource\n"); | 1943 | dev_err(td->dev, "no control register resource\n"); |
diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h index 83352c8078f2..85d0d95223f3 100644 --- a/drivers/clk/tegra/clk-dfll.h +++ b/drivers/clk/tegra/clk-dfll.h | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * clk-dfll.h - prototypes and macros for the Tegra DFLL clocksource driver | 2 | * clk-dfll.h - prototypes and macros for the Tegra DFLL clocksource driver |
3 | * Copyright (C) 2013 NVIDIA Corporation. All rights reserved. | 3 | * Copyright (C) 2013-2019 NVIDIA Corporation. All rights reserved. |
4 | * | 4 | * |
5 | * Aleksandr Frid <afrid@nvidia.com> | 5 | * Aleksandr Frid <afrid@nvidia.com> |
6 | * Paul Walmsley <pwalmsley@nvidia.com> | 6 | * Paul Walmsley <pwalmsley@nvidia.com> |
@@ -22,11 +22,14 @@ | |||
22 | #include <linux/reset.h> | 22 | #include <linux/reset.h> |
23 | #include <linux/types.h> | 23 | #include <linux/types.h> |
24 | 24 | ||
25 | #include "cvb.h" | ||
26 | |||
25 | /** | 27 | /** |
26 | * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver | 28 | * struct tegra_dfll_soc_data - SoC-specific hooks/integration for the DFLL driver |
27 | * @dev: struct device * that holds the OPP table for the DFLL | 29 | * @dev: struct device * that holds the OPP table for the DFLL |
28 | * @max_freq: maximum frequency supported on this SoC | 30 | * @max_freq: maximum frequency supported on this SoC |
29 | * @cvb: CPU frequency table for this SoC | 31 | * @cvb: CPU frequency table for this SoC |
32 | * @alignment: parameters of the regulator step and offset | ||
30 | * @init_clock_trimmers: callback to initialize clock trimmers | 33 | * @init_clock_trimmers: callback to initialize clock trimmers |
31 | * @set_clock_trimmers_high: callback to tune clock trimmers for high voltage | 34 | * @set_clock_trimmers_high: callback to tune clock trimmers for high voltage |
32 | * @set_clock_trimmers_low: callback to tune clock trimmers for low voltage | 35 | * @set_clock_trimmers_low: callback to tune clock trimmers for low voltage |
@@ -35,6 +38,7 @@ struct tegra_dfll_soc_data { | |||
35 | struct device *dev; | 38 | struct device *dev; |
36 | unsigned long max_freq; | 39 | unsigned long max_freq; |
37 | const struct cvb_table *cvb; | 40 | const struct cvb_table *cvb; |
41 | struct rail_alignment alignment; | ||
38 | 42 | ||
39 | void (*init_clock_trimmers)(void); | 43 | void (*init_clock_trimmers)(void); |
40 | void (*set_clock_trimmers_high)(void); | 44 | void (*set_clock_trimmers_high)(void); |
diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c index edc31bb56674..e8ec42bf8638 100644 --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Tegra124 DFLL FCPU clock source driver | 2 | * Tegra124 DFLL FCPU clock source driver |
3 | * | 3 | * |
4 | * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved. | 4 | * Copyright (C) 2012-2019 NVIDIA Corporation. All rights reserved. |
5 | * | 5 | * |
6 | * Aleksandr Frid <afrid@nvidia.com> | 6 | * Aleksandr Frid <afrid@nvidia.com> |
7 | * Paul Walmsley <pwalmsley@nvidia.com> | 7 | * Paul Walmsley <pwalmsley@nvidia.com> |
@@ -21,15 +21,24 @@ | |||
21 | #include <linux/err.h> | 21 | #include <linux/err.h> |
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
24 | #include <linux/of_device.h> | ||
24 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
26 | #include <linux/regulator/consumer.h> | ||
25 | #include <soc/tegra/fuse.h> | 27 | #include <soc/tegra/fuse.h> |
26 | 28 | ||
27 | #include "clk.h" | 29 | #include "clk.h" |
28 | #include "clk-dfll.h" | 30 | #include "clk-dfll.h" |
29 | #include "cvb.h" | 31 | #include "cvb.h" |
30 | 32 | ||
33 | struct dfll_fcpu_data { | ||
34 | const unsigned long *cpu_max_freq_table; | ||
35 | unsigned int cpu_max_freq_table_size; | ||
36 | const struct cvb_table *cpu_cvb_tables; | ||
37 | unsigned int cpu_cvb_tables_size; | ||
38 | }; | ||
39 | |||
31 | /* Maximum CPU frequency, indexed by CPU speedo id */ | 40 | /* Maximum CPU frequency, indexed by CPU speedo id */ |
32 | static const unsigned long cpu_max_freq_table[] = { | 41 | static const unsigned long tegra124_cpu_max_freq_table[] = { |
33 | [0] = 2014500000UL, | 42 | [0] = 2014500000UL, |
34 | [1] = 2320500000UL, | 43 | [1] = 2320500000UL, |
35 | [2] = 2116500000UL, | 44 | [2] = 2116500000UL, |
@@ -42,9 +51,6 @@ static const struct cvb_table tegra124_cpu_cvb_tables[] = { | |||
42 | .process_id = -1, | 51 | .process_id = -1, |
43 | .min_millivolts = 900, | 52 | .min_millivolts = 900, |
44 | .max_millivolts = 1260, | 53 | .max_millivolts = 1260, |
45 | .alignment = { | ||
46 | .step_uv = 10000, /* 10mV */ | ||
47 | }, | ||
48 | .speedo_scale = 100, | 54 | .speedo_scale = 100, |
49 | .voltage_scale = 1000, | 55 | .voltage_scale = 1000, |
50 | .entries = { | 56 | .entries = { |
@@ -82,16 +88,493 @@ static const struct cvb_table tegra124_cpu_cvb_tables[] = { | |||
82 | }, | 88 | }, |
83 | }; | 89 | }; |
84 | 90 | ||
91 | static const unsigned long tegra210_cpu_max_freq_table[] = { | ||
92 | [0] = 1912500000UL, | ||
93 | [1] = 1912500000UL, | ||
94 | [2] = 2218500000UL, | ||
95 | [3] = 1785000000UL, | ||
96 | [4] = 1632000000UL, | ||
97 | [5] = 1912500000UL, | ||
98 | [6] = 2014500000UL, | ||
99 | [7] = 1734000000UL, | ||
100 | [8] = 1683000000UL, | ||
101 | [9] = 1555500000UL, | ||
102 | [10] = 1504500000UL, | ||
103 | }; | ||
104 | |||
105 | #define CPU_CVB_TABLE \ | ||
106 | .speedo_scale = 100, \ | ||
107 | .voltage_scale = 1000, \ | ||
108 | .entries = { \ | ||
109 | { 204000000UL, { 1007452, -23865, 370 } }, \ | ||
110 | { 306000000UL, { 1052709, -24875, 370 } }, \ | ||
111 | { 408000000UL, { 1099069, -25895, 370 } }, \ | ||
112 | { 510000000UL, { 1146534, -26905, 370 } }, \ | ||
113 | { 612000000UL, { 1195102, -27915, 370 } }, \ | ||
114 | { 714000000UL, { 1244773, -28925, 370 } }, \ | ||
115 | { 816000000UL, { 1295549, -29935, 370 } }, \ | ||
116 | { 918000000UL, { 1347428, -30955, 370 } }, \ | ||
117 | { 1020000000UL, { 1400411, -31965, 370 } }, \ | ||
118 | { 1122000000UL, { 1454497, -32975, 370 } }, \ | ||
119 | { 1224000000UL, { 1509687, -33985, 370 } }, \ | ||
120 | { 1326000000UL, { 1565981, -35005, 370 } }, \ | ||
121 | { 1428000000UL, { 1623379, -36015, 370 } }, \ | ||
122 | { 1530000000UL, { 1681880, -37025, 370 } }, \ | ||
123 | { 1632000000UL, { 1741485, -38035, 370 } }, \ | ||
124 | { 1734000000UL, { 1802194, -39055, 370 } }, \ | ||
125 | { 1836000000UL, { 1864006, -40065, 370 } }, \ | ||
126 | { 1912500000UL, { 1910780, -40815, 370 } }, \ | ||
127 | { 2014500000UL, { 1227000, 0, 0 } }, \ | ||
128 | { 2218500000UL, { 1227000, 0, 0 } }, \ | ||
129 | { 0UL, { 0, 0, 0 } }, \ | ||
130 | } | ||
131 | |||
132 | #define CPU_CVB_TABLE_XA \ | ||
133 | .speedo_scale = 100, \ | ||
134 | .voltage_scale = 1000, \ | ||
135 | .entries = { \ | ||
136 | { 204000000UL, { 1250024, -39785, 565 } }, \ | ||
137 | { 306000000UL, { 1297556, -41145, 565 } }, \ | ||
138 | { 408000000UL, { 1346718, -42505, 565 } }, \ | ||
139 | { 510000000UL, { 1397511, -43855, 565 } }, \ | ||
140 | { 612000000UL, { 1449933, -45215, 565 } }, \ | ||
141 | { 714000000UL, { 1503986, -46575, 565 } }, \ | ||
142 | { 816000000UL, { 1559669, -47935, 565 } }, \ | ||
143 | { 918000000UL, { 1616982, -49295, 565 } }, \ | ||
144 | { 1020000000UL, { 1675926, -50645, 565 } }, \ | ||
145 | { 1122000000UL, { 1736500, -52005, 565 } }, \ | ||
146 | { 1224000000UL, { 1798704, -53365, 565 } }, \ | ||
147 | { 1326000000UL, { 1862538, -54725, 565 } }, \ | ||
148 | { 1428000000UL, { 1928003, -56085, 565 } }, \ | ||
149 | { 1530000000UL, { 1995097, -57435, 565 } }, \ | ||
150 | { 1606500000UL, { 2046149, -58445, 565 } }, \ | ||
151 | { 1632000000UL, { 2063822, -58795, 565 } }, \ | ||
152 | { 0UL, { 0, 0, 0 } }, \ | ||
153 | } | ||
154 | |||
155 | #define CPU_CVB_TABLE_EUCM1 \ | ||
156 | .speedo_scale = 100, \ | ||
157 | .voltage_scale = 1000, \ | ||
158 | .entries = { \ | ||
159 | { 204000000UL, { 734429, 0, 0 } }, \ | ||
160 | { 306000000UL, { 768191, 0, 0 } }, \ | ||
161 | { 408000000UL, { 801953, 0, 0 } }, \ | ||
162 | { 510000000UL, { 835715, 0, 0 } }, \ | ||
163 | { 612000000UL, { 869477, 0, 0 } }, \ | ||
164 | { 714000000UL, { 903239, 0, 0 } }, \ | ||
165 | { 816000000UL, { 937001, 0, 0 } }, \ | ||
166 | { 918000000UL, { 970763, 0, 0 } }, \ | ||
167 | { 1020000000UL, { 1004525, 0, 0 } }, \ | ||
168 | { 1122000000UL, { 1038287, 0, 0 } }, \ | ||
169 | { 1224000000UL, { 1072049, 0, 0 } }, \ | ||
170 | { 1326000000UL, { 1105811, 0, 0 } }, \ | ||
171 | { 1428000000UL, { 1130000, 0, 0 } }, \ | ||
172 | { 1555500000UL, { 1130000, 0, 0 } }, \ | ||
173 | { 1632000000UL, { 1170000, 0, 0 } }, \ | ||
174 | { 1734000000UL, { 1227500, 0, 0 } }, \ | ||
175 | { 0UL, { 0, 0, 0 } }, \ | ||
176 | } | ||
177 | |||
178 | #define CPU_CVB_TABLE_EUCM2 \ | ||
179 | .speedo_scale = 100, \ | ||
180 | .voltage_scale = 1000, \ | ||
181 | .entries = { \ | ||
182 | { 204000000UL, { 742283, 0, 0 } }, \ | ||
183 | { 306000000UL, { 776249, 0, 0 } }, \ | ||
184 | { 408000000UL, { 810215, 0, 0 } }, \ | ||
185 | { 510000000UL, { 844181, 0, 0 } }, \ | ||
186 | { 612000000UL, { 878147, 0, 0 } }, \ | ||
187 | { 714000000UL, { 912113, 0, 0 } }, \ | ||
188 | { 816000000UL, { 946079, 0, 0 } }, \ | ||
189 | { 918000000UL, { 980045, 0, 0 } }, \ | ||
190 | { 1020000000UL, { 1014011, 0, 0 } }, \ | ||
191 | { 1122000000UL, { 1047977, 0, 0 } }, \ | ||
192 | { 1224000000UL, { 1081943, 0, 0 } }, \ | ||
193 | { 1326000000UL, { 1090000, 0, 0 } }, \ | ||
194 | { 1479000000UL, { 1090000, 0, 0 } }, \ | ||
195 | { 1555500000UL, { 1162000, 0, 0 } }, \ | ||
196 | { 1683000000UL, { 1195000, 0, 0 } }, \ | ||
197 | { 0UL, { 0, 0, 0 } }, \ | ||
198 | } | ||
199 | |||
200 | #define CPU_CVB_TABLE_EUCM2_JOINT_RAIL \ | ||
201 | .speedo_scale = 100, \ | ||
202 | .voltage_scale = 1000, \ | ||
203 | .entries = { \ | ||
204 | { 204000000UL, { 742283, 0, 0 } }, \ | ||
205 | { 306000000UL, { 776249, 0, 0 } }, \ | ||
206 | { 408000000UL, { 810215, 0, 0 } }, \ | ||
207 | { 510000000UL, { 844181, 0, 0 } }, \ | ||
208 | { 612000000UL, { 878147, 0, 0 } }, \ | ||
209 | { 714000000UL, { 912113, 0, 0 } }, \ | ||
210 | { 816000000UL, { 946079, 0, 0 } }, \ | ||
211 | { 918000000UL, { 980045, 0, 0 } }, \ | ||
212 | { 1020000000UL, { 1014011, 0, 0 } }, \ | ||
213 | { 1122000000UL, { 1047977, 0, 0 } }, \ | ||
214 | { 1224000000UL, { 1081943, 0, 0 } }, \ | ||
215 | { 1326000000UL, { 1090000, 0, 0 } }, \ | ||
216 | { 1479000000UL, { 1090000, 0, 0 } }, \ | ||
217 | { 1504500000UL, { 1120000, 0, 0 } }, \ | ||
218 | { 0UL, { 0, 0, 0 } }, \ | ||
219 | } | ||
220 | |||
221 | #define CPU_CVB_TABLE_ODN \ | ||
222 | .speedo_scale = 100, \ | ||
223 | .voltage_scale = 1000, \ | ||
224 | .entries = { \ | ||
225 | { 204000000UL, { 721094, 0, 0 } }, \ | ||
226 | { 306000000UL, { 754040, 0, 0 } }, \ | ||
227 | { 408000000UL, { 786986, 0, 0 } }, \ | ||
228 | { 510000000UL, { 819932, 0, 0 } }, \ | ||
229 | { 612000000UL, { 852878, 0, 0 } }, \ | ||
230 | { 714000000UL, { 885824, 0, 0 } }, \ | ||
231 | { 816000000UL, { 918770, 0, 0 } }, \ | ||
232 | { 918000000UL, { 915716, 0, 0 } }, \ | ||
233 | { 1020000000UL, { 984662, 0, 0 } }, \ | ||
234 | { 1122000000UL, { 1017608, 0, 0 } }, \ | ||
235 | { 1224000000UL, { 1050554, 0, 0 } }, \ | ||
236 | { 1326000000UL, { 1083500, 0, 0 } }, \ | ||
237 | { 1428000000UL, { 1116446, 0, 0 } }, \ | ||
238 | { 1581000000UL, { 1130000, 0, 0 } }, \ | ||
239 | { 1683000000UL, { 1168000, 0, 0 } }, \ | ||
240 | { 1785000000UL, { 1227500, 0, 0 } }, \ | ||
241 | { 0UL, { 0, 0, 0 } }, \ | ||
242 | } | ||
243 | |||
244 | static struct cvb_table tegra210_cpu_cvb_tables[] = { | ||
245 | { | ||
246 | .speedo_id = 10, | ||
247 | .process_id = 0, | ||
248 | .min_millivolts = 840, | ||
249 | .max_millivolts = 1120, | ||
250 | CPU_CVB_TABLE_EUCM2_JOINT_RAIL, | ||
251 | .cpu_dfll_data = { | ||
252 | .tune0_low = 0xffead0ff, | ||
253 | .tune0_high = 0xffead0ff, | ||
254 | .tune1 = 0x20091d9, | ||
255 | .tune_high_min_millivolts = 864, | ||
256 | } | ||
257 | }, | ||
258 | { | ||
259 | .speedo_id = 10, | ||
260 | .process_id = 1, | ||
261 | .min_millivolts = 840, | ||
262 | .max_millivolts = 1120, | ||
263 | CPU_CVB_TABLE_EUCM2_JOINT_RAIL, | ||
264 | .cpu_dfll_data = { | ||
265 | .tune0_low = 0xffead0ff, | ||
266 | .tune0_high = 0xffead0ff, | ||
267 | .tune1 = 0x20091d9, | ||
268 | .tune_high_min_millivolts = 864, | ||
269 | } | ||
270 | }, | ||
271 | { | ||
272 | .speedo_id = 9, | ||
273 | .process_id = 0, | ||
274 | .min_millivolts = 900, | ||
275 | .max_millivolts = 1162, | ||
276 | CPU_CVB_TABLE_EUCM2, | ||
277 | .cpu_dfll_data = { | ||
278 | .tune0_low = 0xffead0ff, | ||
279 | .tune0_high = 0xffead0ff, | ||
280 | .tune1 = 0x20091d9, | ||
281 | } | ||
282 | }, | ||
283 | { | ||
284 | .speedo_id = 9, | ||
285 | .process_id = 1, | ||
286 | .min_millivolts = 900, | ||
287 | .max_millivolts = 1162, | ||
288 | CPU_CVB_TABLE_EUCM2, | ||
289 | .cpu_dfll_data = { | ||
290 | .tune0_low = 0xffead0ff, | ||
291 | .tune0_high = 0xffead0ff, | ||
292 | .tune1 = 0x20091d9, | ||
293 | } | ||
294 | }, | ||
295 | { | ||
296 | .speedo_id = 8, | ||
297 | .process_id = 0, | ||
298 | .min_millivolts = 900, | ||
299 | .max_millivolts = 1195, | ||
300 | CPU_CVB_TABLE_EUCM2, | ||
301 | .cpu_dfll_data = { | ||
302 | .tune0_low = 0xffead0ff, | ||
303 | .tune0_high = 0xffead0ff, | ||
304 | .tune1 = 0x20091d9, | ||
305 | } | ||
306 | }, | ||
307 | { | ||
308 | .speedo_id = 8, | ||
309 | .process_id = 1, | ||
310 | .min_millivolts = 900, | ||
311 | .max_millivolts = 1195, | ||
312 | CPU_CVB_TABLE_EUCM2, | ||
313 | .cpu_dfll_data = { | ||
314 | .tune0_low = 0xffead0ff, | ||
315 | .tune0_high = 0xffead0ff, | ||
316 | .tune1 = 0x20091d9, | ||
317 | } | ||
318 | }, | ||
319 | { | ||
320 | .speedo_id = 7, | ||
321 | .process_id = 0, | ||
322 | .min_millivolts = 841, | ||
323 | .max_millivolts = 1227, | ||
324 | CPU_CVB_TABLE_EUCM1, | ||
325 | .cpu_dfll_data = { | ||
326 | .tune0_low = 0xffead0ff, | ||
327 | .tune0_high = 0xffead0ff, | ||
328 | .tune1 = 0x20091d9, | ||
329 | .tune_high_min_millivolts = 864, | ||
330 | } | ||
331 | }, | ||
332 | { | ||
333 | .speedo_id = 7, | ||
334 | .process_id = 1, | ||
335 | .min_millivolts = 841, | ||
336 | .max_millivolts = 1227, | ||
337 | CPU_CVB_TABLE_EUCM1, | ||
338 | .cpu_dfll_data = { | ||
339 | .tune0_low = 0xffead0ff, | ||
340 | .tune0_high = 0xffead0ff, | ||
341 | .tune1 = 0x20091d9, | ||
342 | .tune_high_min_millivolts = 864, | ||
343 | } | ||
344 | }, | ||
345 | { | ||
346 | .speedo_id = 6, | ||
347 | .process_id = 0, | ||
348 | .min_millivolts = 870, | ||
349 | .max_millivolts = 1150, | ||
350 | CPU_CVB_TABLE, | ||
351 | .cpu_dfll_data = { | ||
352 | .tune0_low = 0xffead0ff, | ||
353 | .tune1 = 0x20091d9, | ||
354 | } | ||
355 | }, | ||
356 | { | ||
357 | .speedo_id = 6, | ||
358 | .process_id = 1, | ||
359 | .min_millivolts = 870, | ||
360 | .max_millivolts = 1150, | ||
361 | CPU_CVB_TABLE, | ||
362 | .cpu_dfll_data = { | ||
363 | .tune0_low = 0xffead0ff, | ||
364 | .tune1 = 0x25501d0, | ||
365 | } | ||
366 | }, | ||
367 | { | ||
368 | .speedo_id = 5, | ||
369 | .process_id = 0, | ||
370 | .min_millivolts = 818, | ||
371 | .max_millivolts = 1227, | ||
372 | CPU_CVB_TABLE, | ||
373 | .cpu_dfll_data = { | ||
374 | .tune0_low = 0xffead0ff, | ||
375 | .tune0_high = 0xffead0ff, | ||
376 | .tune1 = 0x20091d9, | ||
377 | .tune_high_min_millivolts = 864, | ||
378 | } | ||
379 | }, | ||
380 | { | ||
381 | .speedo_id = 5, | ||
382 | .process_id = 1, | ||
383 | .min_millivolts = 818, | ||
384 | .max_millivolts = 1227, | ||
385 | CPU_CVB_TABLE, | ||
386 | .cpu_dfll_data = { | ||
387 | .tune0_low = 0xffead0ff, | ||
388 | .tune0_high = 0xffead0ff, | ||
389 | .tune1 = 0x25501d0, | ||
390 | .tune_high_min_millivolts = 864, | ||
391 | } | ||
392 | }, | ||
393 | { | ||
394 | .speedo_id = 4, | ||
395 | .process_id = -1, | ||
396 | .min_millivolts = 918, | ||
397 | .max_millivolts = 1113, | ||
398 | CPU_CVB_TABLE_XA, | ||
399 | .cpu_dfll_data = { | ||
400 | .tune0_low = 0xffead0ff, | ||
401 | .tune1 = 0x17711BD, | ||
402 | } | ||
403 | }, | ||
404 | { | ||
405 | .speedo_id = 3, | ||
406 | .process_id = 0, | ||
407 | .min_millivolts = 825, | ||
408 | .max_millivolts = 1227, | ||
409 | CPU_CVB_TABLE_ODN, | ||
410 | .cpu_dfll_data = { | ||
411 | .tune0_low = 0xffead0ff, | ||
412 | .tune0_high = 0xffead0ff, | ||
413 | .tune1 = 0x20091d9, | ||
414 | .tune_high_min_millivolts = 864, | ||
415 | } | ||
416 | }, | ||
417 | { | ||
418 | .speedo_id = 3, | ||
419 | .process_id = 1, | ||
420 | .min_millivolts = 825, | ||
421 | .max_millivolts = 1227, | ||
422 | CPU_CVB_TABLE_ODN, | ||
423 | .cpu_dfll_data = { | ||
424 | .tune0_low = 0xffead0ff, | ||
425 | .tune0_high = 0xffead0ff, | ||
426 | .tune1 = 0x25501d0, | ||
427 | .tune_high_min_millivolts = 864, | ||
428 | } | ||
429 | }, | ||
430 | { | ||
431 | .speedo_id = 2, | ||
432 | .process_id = 0, | ||
433 | .min_millivolts = 870, | ||
434 | .max_millivolts = 1227, | ||
435 | CPU_CVB_TABLE, | ||
436 | .cpu_dfll_data = { | ||
437 | .tune0_low = 0xffead0ff, | ||
438 | .tune1 = 0x20091d9, | ||
439 | } | ||
440 | }, | ||
441 | { | ||
442 | .speedo_id = 2, | ||
443 | .process_id = 1, | ||
444 | .min_millivolts = 870, | ||
445 | .max_millivolts = 1227, | ||
446 | CPU_CVB_TABLE, | ||
447 | .cpu_dfll_data = { | ||
448 | .tune0_low = 0xffead0ff, | ||
449 | .tune1 = 0x25501d0, | ||
450 | } | ||
451 | }, | ||
452 | { | ||
453 | .speedo_id = 1, | ||
454 | .process_id = 0, | ||
455 | .min_millivolts = 837, | ||
456 | .max_millivolts = 1227, | ||
457 | CPU_CVB_TABLE, | ||
458 | .cpu_dfll_data = { | ||
459 | .tune0_low = 0xffead0ff, | ||
460 | .tune0_high = 0xffead0ff, | ||
461 | .tune1 = 0x20091d9, | ||
462 | .tune_high_min_millivolts = 864, | ||
463 | } | ||
464 | }, | ||
465 | { | ||
466 | .speedo_id = 1, | ||
467 | .process_id = 1, | ||
468 | .min_millivolts = 837, | ||
469 | .max_millivolts = 1227, | ||
470 | CPU_CVB_TABLE, | ||
471 | .cpu_dfll_data = { | ||
472 | .tune0_low = 0xffead0ff, | ||
473 | .tune0_high = 0xffead0ff, | ||
474 | .tune1 = 0x25501d0, | ||
475 | .tune_high_min_millivolts = 864, | ||
476 | } | ||
477 | }, | ||
478 | { | ||
479 | .speedo_id = 0, | ||
480 | .process_id = 0, | ||
481 | .min_millivolts = 850, | ||
482 | .max_millivolts = 1170, | ||
483 | CPU_CVB_TABLE, | ||
484 | .cpu_dfll_data = { | ||
485 | .tune0_low = 0xffead0ff, | ||
486 | .tune0_high = 0xffead0ff, | ||
487 | .tune1 = 0x20091d9, | ||
488 | .tune_high_min_millivolts = 864, | ||
489 | } | ||
490 | }, | ||
491 | { | ||
492 | .speedo_id = 0, | ||
493 | .process_id = 1, | ||
494 | .min_millivolts = 850, | ||
495 | .max_millivolts = 1170, | ||
496 | CPU_CVB_TABLE, | ||
497 | .cpu_dfll_data = { | ||
498 | .tune0_low = 0xffead0ff, | ||
499 | .tune0_high = 0xffead0ff, | ||
500 | .tune1 = 0x25501d0, | ||
501 | .tune_high_min_millivolts = 864, | ||
502 | } | ||
503 | }, | ||
504 | }; | ||
505 | |||
506 | static const struct dfll_fcpu_data tegra124_dfll_fcpu_data = { | ||
507 | .cpu_max_freq_table = tegra124_cpu_max_freq_table, | ||
508 | .cpu_max_freq_table_size = ARRAY_SIZE(tegra124_cpu_max_freq_table), | ||
509 | .cpu_cvb_tables = tegra124_cpu_cvb_tables, | ||
510 | .cpu_cvb_tables_size = ARRAY_SIZE(tegra124_cpu_cvb_tables) | ||
511 | }; | ||
512 | |||
513 | static const struct dfll_fcpu_data tegra210_dfll_fcpu_data = { | ||
514 | .cpu_max_freq_table = tegra210_cpu_max_freq_table, | ||
515 | .cpu_max_freq_table_size = ARRAY_SIZE(tegra210_cpu_max_freq_table), | ||
516 | .cpu_cvb_tables = tegra210_cpu_cvb_tables, | ||
517 | .cpu_cvb_tables_size = ARRAY_SIZE(tegra210_cpu_cvb_tables), | ||
518 | }; | ||
519 | |||
520 | static const struct of_device_id tegra124_dfll_fcpu_of_match[] = { | ||
521 | { | ||
522 | .compatible = "nvidia,tegra124-dfll", | ||
523 | .data = &tegra124_dfll_fcpu_data, | ||
524 | }, | ||
525 | { | ||
526 | .compatible = "nvidia,tegra210-dfll", | ||
527 | .data = &tegra210_dfll_fcpu_data | ||
528 | }, | ||
529 | { }, | ||
530 | }; | ||
531 | |||
532 | static void get_alignment_from_dt(struct device *dev, | ||
533 | struct rail_alignment *align) | ||
534 | { | ||
535 | if (of_property_read_u32(dev->of_node, | ||
536 | "nvidia,pwm-voltage-step-microvolts", | ||
537 | &align->step_uv)) | ||
538 | align->step_uv = 0; | ||
539 | |||
540 | if (of_property_read_u32(dev->of_node, | ||
541 | "nvidia,pwm-min-microvolts", | ||
542 | &align->offset_uv)) | ||
543 | align->offset_uv = 0; | ||
544 | } | ||
545 | |||
546 | static int get_alignment_from_regulator(struct device *dev, | ||
547 | struct rail_alignment *align) | ||
548 | { | ||
549 | struct regulator *reg = devm_regulator_get(dev, "vdd-cpu"); | ||
550 | |||
551 | if (IS_ERR(reg)) | ||
552 | return PTR_ERR(reg); | ||
553 | |||
554 | align->offset_uv = regulator_list_voltage(reg, 0); | ||
555 | align->step_uv = regulator_get_linear_step(reg); | ||
556 | |||
557 | devm_regulator_put(reg); | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
85 | static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) | 562 | static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) |
86 | { | 563 | { |
87 | int process_id, speedo_id, speedo_value, err; | 564 | int process_id, speedo_id, speedo_value, err; |
88 | struct tegra_dfll_soc_data *soc; | 565 | struct tegra_dfll_soc_data *soc; |
566 | const struct dfll_fcpu_data *fcpu_data; | ||
567 | struct rail_alignment align; | ||
568 | |||
569 | fcpu_data = of_device_get_match_data(&pdev->dev); | ||
570 | if (!fcpu_data) | ||
571 | return -ENODEV; | ||
89 | 572 | ||
90 | process_id = tegra_sku_info.cpu_process_id; | 573 | process_id = tegra_sku_info.cpu_process_id; |
91 | speedo_id = tegra_sku_info.cpu_speedo_id; | 574 | speedo_id = tegra_sku_info.cpu_speedo_id; |
92 | speedo_value = tegra_sku_info.cpu_speedo_value; | 575 | speedo_value = tegra_sku_info.cpu_speedo_value; |
93 | 576 | ||
94 | if (speedo_id >= ARRAY_SIZE(cpu_max_freq_table)) { | 577 | if (speedo_id >= fcpu_data->cpu_max_freq_table_size) { |
95 | dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n", | 578 | dev_err(&pdev->dev, "unknown max CPU freq for speedo_id=%d\n", |
96 | speedo_id); | 579 | speedo_id); |
97 | return -ENODEV; | 580 | return -ENODEV; |
@@ -107,12 +590,22 @@ static int tegra124_dfll_fcpu_probe(struct platform_device *pdev) | |||
107 | return -ENODEV; | 590 | return -ENODEV; |
108 | } | 591 | } |
109 | 592 | ||
110 | soc->max_freq = cpu_max_freq_table[speedo_id]; | 593 | if (of_property_read_bool(pdev->dev.of_node, "nvidia,pwm-to-pmic")) { |
594 | get_alignment_from_dt(&pdev->dev, &align); | ||
595 | } else { | ||
596 | err = get_alignment_from_regulator(&pdev->dev, &align); | ||
597 | if (err) | ||
598 | return err; | ||
599 | } | ||
600 | |||
601 | soc->max_freq = fcpu_data->cpu_max_freq_table[speedo_id]; | ||
602 | |||
603 | soc->cvb = tegra_cvb_add_opp_table(soc->dev, fcpu_data->cpu_cvb_tables, | ||
604 | fcpu_data->cpu_cvb_tables_size, | ||
605 | &align, process_id, speedo_id, | ||
606 | speedo_value, soc->max_freq); | ||
607 | soc->alignment = align; | ||
111 | 608 | ||
112 | soc->cvb = tegra_cvb_add_opp_table(soc->dev, tegra124_cpu_cvb_tables, | ||
113 | ARRAY_SIZE(tegra124_cpu_cvb_tables), | ||
114 | process_id, speedo_id, speedo_value, | ||
115 | soc->max_freq); | ||
116 | if (IS_ERR(soc->cvb)) { | 609 | if (IS_ERR(soc->cvb)) { |
117 | dev_err(&pdev->dev, "couldn't add OPP table: %ld\n", | 610 | dev_err(&pdev->dev, "couldn't add OPP table: %ld\n", |
118 | PTR_ERR(soc->cvb)); | 611 | PTR_ERR(soc->cvb)); |
@@ -144,11 +637,6 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev) | |||
144 | return 0; | 637 | return 0; |
145 | } | 638 | } |
146 | 639 | ||
147 | static const struct of_device_id tegra124_dfll_fcpu_of_match[] = { | ||
148 | { .compatible = "nvidia,tegra124-dfll", }, | ||
149 | { }, | ||
150 | }; | ||
151 | |||
152 | static const struct dev_pm_ops tegra124_dfll_pm_ops = { | 640 | static const struct dev_pm_ops tegra124_dfll_pm_ops = { |
153 | SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend, | 641 | SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend, |
154 | tegra_dfll_runtime_resume, NULL) | 642 | tegra_dfll_runtime_resume, NULL) |
diff --git a/drivers/clk/tegra/cvb.c b/drivers/clk/tegra/cvb.c index da9e8e7b5ce5..35eeb6adc68e 100644 --- a/drivers/clk/tegra/cvb.c +++ b/drivers/clk/tegra/cvb.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Utility functions for parsing Tegra CVB voltage tables | 2 | * Utility functions for parsing Tegra CVB voltage tables |
3 | * | 3 | * |
4 | * Copyright (C) 2012-2014 NVIDIA Corporation. All rights reserved. | 4 | * Copyright (C) 2012-2019 NVIDIA Corporation. All rights reserved. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
@@ -62,9 +62,9 @@ static int round_voltage(int mv, const struct rail_alignment *align, int up) | |||
62 | } | 62 | } |
63 | 63 | ||
64 | static int build_opp_table(struct device *dev, const struct cvb_table *table, | 64 | static int build_opp_table(struct device *dev, const struct cvb_table *table, |
65 | struct rail_alignment *align, | ||
65 | int speedo_value, unsigned long max_freq) | 66 | int speedo_value, unsigned long max_freq) |
66 | { | 67 | { |
67 | const struct rail_alignment *align = &table->alignment; | ||
68 | int i, ret, dfll_mv, min_mv, max_mv; | 68 | int i, ret, dfll_mv, min_mv, max_mv; |
69 | 69 | ||
70 | min_mv = round_voltage(table->min_millivolts, align, UP); | 70 | min_mv = round_voltage(table->min_millivolts, align, UP); |
@@ -109,8 +109,9 @@ static int build_opp_table(struct device *dev, const struct cvb_table *table, | |||
109 | */ | 109 | */ |
110 | const struct cvb_table * | 110 | const struct cvb_table * |
111 | tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables, | 111 | tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables, |
112 | size_t count, int process_id, int speedo_id, | 112 | size_t count, struct rail_alignment *align, |
113 | int speedo_value, unsigned long max_freq) | 113 | int process_id, int speedo_id, int speedo_value, |
114 | unsigned long max_freq) | ||
114 | { | 115 | { |
115 | size_t i; | 116 | size_t i; |
116 | int ret; | 117 | int ret; |
@@ -124,7 +125,8 @@ tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *tables, | |||
124 | if (table->process_id != -1 && table->process_id != process_id) | 125 | if (table->process_id != -1 && table->process_id != process_id) |
125 | continue; | 126 | continue; |
126 | 127 | ||
127 | ret = build_opp_table(dev, table, speedo_value, max_freq); | 128 | ret = build_opp_table(dev, table, align, speedo_value, |
129 | max_freq); | ||
128 | return ret ? ERR_PTR(ret) : table; | 130 | return ret ? ERR_PTR(ret) : table; |
129 | } | 131 | } |
130 | 132 | ||
diff --git a/drivers/clk/tegra/cvb.h b/drivers/clk/tegra/cvb.h index c1f077993b2a..91a1941c21ef 100644 --- a/drivers/clk/tegra/cvb.h +++ b/drivers/clk/tegra/cvb.h | |||
@@ -41,6 +41,7 @@ struct cvb_cpu_dfll_data { | |||
41 | u32 tune0_low; | 41 | u32 tune0_low; |
42 | u32 tune0_high; | 42 | u32 tune0_high; |
43 | u32 tune1; | 43 | u32 tune1; |
44 | unsigned int tune_high_min_millivolts; | ||
44 | }; | 45 | }; |
45 | 46 | ||
46 | struct cvb_table { | 47 | struct cvb_table { |
@@ -49,7 +50,6 @@ struct cvb_table { | |||
49 | 50 | ||
50 | int min_millivolts; | 51 | int min_millivolts; |
51 | int max_millivolts; | 52 | int max_millivolts; |
52 | struct rail_alignment alignment; | ||
53 | 53 | ||
54 | int speedo_scale; | 54 | int speedo_scale; |
55 | int voltage_scale; | 55 | int voltage_scale; |
@@ -59,8 +59,9 @@ struct cvb_table { | |||
59 | 59 | ||
60 | const struct cvb_table * | 60 | const struct cvb_table * |
61 | tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables, | 61 | tegra_cvb_add_opp_table(struct device *dev, const struct cvb_table *cvb_tables, |
62 | size_t count, int process_id, int speedo_id, | 62 | size_t count, struct rail_alignment *align, |
63 | int speedo_value, unsigned long max_freq); | 63 | int process_id, int speedo_id, int speedo_value, |
64 | unsigned long max_freq); | ||
64 | void tegra_cvb_remove_opp_table(struct device *dev, | 65 | void tegra_cvb_remove_opp_table(struct device *dev, |
65 | const struct cvb_table *table, | 66 | const struct cvb_table *table, |
66 | unsigned long max_freq); | 67 | unsigned long max_freq); |
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 688f10227793..1a6778e81f90 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm | |||
@@ -272,8 +272,8 @@ config ARM_TEGRA20_CPUFREQ | |||
272 | This adds the CPUFreq driver support for Tegra20 SOCs. | 272 | This adds the CPUFreq driver support for Tegra20 SOCs. |
273 | 273 | ||
274 | config ARM_TEGRA124_CPUFREQ | 274 | config ARM_TEGRA124_CPUFREQ |
275 | tristate "Tegra124 CPUFreq support" | 275 | bool "Tegra124 CPUFreq support" |
276 | depends on ARCH_TEGRA && CPUFREQ_DT && REGULATOR | 276 | depends on ARCH_TEGRA && CPUFREQ_DT |
277 | default y | 277 | default y |
278 | help | 278 | help |
279 | This adds the CPUFreq driver support for Tegra124 SOCs. | 279 | This adds the CPUFreq driver support for Tegra124 SOCs. |
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index b1c5468dca16..47729a22c159 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c | |||
@@ -119,6 +119,7 @@ static const struct of_device_id blacklist[] __initconst = { | |||
119 | { .compatible = "mediatek,mt8176", }, | 119 | { .compatible = "mediatek,mt8176", }, |
120 | 120 | ||
121 | { .compatible = "nvidia,tegra124", }, | 121 | { .compatible = "nvidia,tegra124", }, |
122 | { .compatible = "nvidia,tegra210", }, | ||
122 | 123 | ||
123 | { .compatible = "qcom,apq8096", }, | 124 | { .compatible = "qcom,apq8096", }, |
124 | { .compatible = "qcom,msm8996", }, | 125 | { .compatible = "qcom,msm8996", }, |
diff --git a/drivers/cpufreq/tegra124-cpufreq.c b/drivers/cpufreq/tegra124-cpufreq.c index 43530254201a..ba3795e13ac6 100644 --- a/drivers/cpufreq/tegra124-cpufreq.c +++ b/drivers/cpufreq/tegra124-cpufreq.c | |||
@@ -22,11 +22,9 @@ | |||
22 | #include <linux/of.h> | 22 | #include <linux/of.h> |
23 | #include <linux/platform_device.h> | 23 | #include <linux/platform_device.h> |
24 | #include <linux/pm_opp.h> | 24 | #include <linux/pm_opp.h> |
25 | #include <linux/regulator/consumer.h> | ||
26 | #include <linux/types.h> | 25 | #include <linux/types.h> |
27 | 26 | ||
28 | struct tegra124_cpufreq_priv { | 27 | struct tegra124_cpufreq_priv { |
29 | struct regulator *vdd_cpu_reg; | ||
30 | struct clk *cpu_clk; | 28 | struct clk *cpu_clk; |
31 | struct clk *pllp_clk; | 29 | struct clk *pllp_clk; |
32 | struct clk *pllx_clk; | 30 | struct clk *pllx_clk; |
@@ -60,14 +58,6 @@ out: | |||
60 | return ret; | 58 | return ret; |
61 | } | 59 | } |
62 | 60 | ||
63 | static void tegra124_cpu_switch_to_pllx(struct tegra124_cpufreq_priv *priv) | ||
64 | { | ||
65 | clk_set_parent(priv->cpu_clk, priv->pllp_clk); | ||
66 | clk_disable_unprepare(priv->dfll_clk); | ||
67 | regulator_sync_voltage(priv->vdd_cpu_reg); | ||
68 | clk_set_parent(priv->cpu_clk, priv->pllx_clk); | ||
69 | } | ||
70 | |||
71 | static int tegra124_cpufreq_probe(struct platform_device *pdev) | 61 | static int tegra124_cpufreq_probe(struct platform_device *pdev) |
72 | { | 62 | { |
73 | struct tegra124_cpufreq_priv *priv; | 63 | struct tegra124_cpufreq_priv *priv; |
@@ -88,16 +78,10 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev) | |||
88 | if (!np) | 78 | if (!np) |
89 | return -ENODEV; | 79 | return -ENODEV; |
90 | 80 | ||
91 | priv->vdd_cpu_reg = regulator_get(cpu_dev, "vdd-cpu"); | ||
92 | if (IS_ERR(priv->vdd_cpu_reg)) { | ||
93 | ret = PTR_ERR(priv->vdd_cpu_reg); | ||
94 | goto out_put_np; | ||
95 | } | ||
96 | |||
97 | priv->cpu_clk = of_clk_get_by_name(np, "cpu_g"); | 81 | priv->cpu_clk = of_clk_get_by_name(np, "cpu_g"); |
98 | if (IS_ERR(priv->cpu_clk)) { | 82 | if (IS_ERR(priv->cpu_clk)) { |
99 | ret = PTR_ERR(priv->cpu_clk); | 83 | ret = PTR_ERR(priv->cpu_clk); |
100 | goto out_put_vdd_cpu_reg; | 84 | goto out_put_np; |
101 | } | 85 | } |
102 | 86 | ||
103 | priv->dfll_clk = of_clk_get_by_name(np, "dfll"); | 87 | priv->dfll_clk = of_clk_get_by_name(np, "dfll"); |
@@ -129,15 +113,13 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev) | |||
129 | platform_device_register_full(&cpufreq_dt_devinfo); | 113 | platform_device_register_full(&cpufreq_dt_devinfo); |
130 | if (IS_ERR(priv->cpufreq_dt_pdev)) { | 114 | if (IS_ERR(priv->cpufreq_dt_pdev)) { |
131 | ret = PTR_ERR(priv->cpufreq_dt_pdev); | 115 | ret = PTR_ERR(priv->cpufreq_dt_pdev); |
132 | goto out_switch_to_pllx; | 116 | goto out_put_pllp_clk; |
133 | } | 117 | } |
134 | 118 | ||
135 | platform_set_drvdata(pdev, priv); | 119 | platform_set_drvdata(pdev, priv); |
136 | 120 | ||
137 | return 0; | 121 | return 0; |
138 | 122 | ||
139 | out_switch_to_pllx: | ||
140 | tegra124_cpu_switch_to_pllx(priv); | ||
141 | out_put_pllp_clk: | 123 | out_put_pllp_clk: |
142 | clk_put(priv->pllp_clk); | 124 | clk_put(priv->pllp_clk); |
143 | out_put_pllx_clk: | 125 | out_put_pllx_clk: |
@@ -146,34 +128,15 @@ out_put_dfll_clk: | |||
146 | clk_put(priv->dfll_clk); | 128 | clk_put(priv->dfll_clk); |
147 | out_put_cpu_clk: | 129 | out_put_cpu_clk: |
148 | clk_put(priv->cpu_clk); | 130 | clk_put(priv->cpu_clk); |
149 | out_put_vdd_cpu_reg: | ||
150 | regulator_put(priv->vdd_cpu_reg); | ||
151 | out_put_np: | 131 | out_put_np: |
152 | of_node_put(np); | 132 | of_node_put(np); |
153 | 133 | ||
154 | return ret; | 134 | return ret; |
155 | } | 135 | } |
156 | 136 | ||
157 | static int tegra124_cpufreq_remove(struct platform_device *pdev) | ||
158 | { | ||
159 | struct tegra124_cpufreq_priv *priv = platform_get_drvdata(pdev); | ||
160 | |||
161 | platform_device_unregister(priv->cpufreq_dt_pdev); | ||
162 | tegra124_cpu_switch_to_pllx(priv); | ||
163 | |||
164 | clk_put(priv->pllp_clk); | ||
165 | clk_put(priv->pllx_clk); | ||
166 | clk_put(priv->dfll_clk); | ||
167 | clk_put(priv->cpu_clk); | ||
168 | regulator_put(priv->vdd_cpu_reg); | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static struct platform_driver tegra124_cpufreq_platdrv = { | 137 | static struct platform_driver tegra124_cpufreq_platdrv = { |
174 | .driver.name = "cpufreq-tegra124", | 138 | .driver.name = "cpufreq-tegra124", |
175 | .probe = tegra124_cpufreq_probe, | 139 | .probe = tegra124_cpufreq_probe, |
176 | .remove = tegra124_cpufreq_remove, | ||
177 | }; | 140 | }; |
178 | 141 | ||
179 | static int __init tegra_cpufreq_init(void) | 142 | static int __init tegra_cpufreq_init(void) |
@@ -181,7 +144,8 @@ static int __init tegra_cpufreq_init(void) | |||
181 | int ret; | 144 | int ret; |
182 | struct platform_device *pdev; | 145 | struct platform_device *pdev; |
183 | 146 | ||
184 | if (!of_machine_is_compatible("nvidia,tegra124")) | 147 | if (!(of_machine_is_compatible("nvidia,tegra124") || |
148 | of_machine_is_compatible("nvidia,tegra210"))) | ||
185 | return -ENODEV; | 149 | return -ENODEV; |
186 | 150 | ||
187 | /* | 151 | /* |
diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c index 97f5424dbac9..4b56a587dacd 100644 --- a/drivers/firmware/imx/misc.c +++ b/drivers/firmware/imx/misc.c | |||
@@ -18,6 +18,14 @@ struct imx_sc_msg_req_misc_set_ctrl { | |||
18 | u16 resource; | 18 | u16 resource; |
19 | } __packed; | 19 | } __packed; |
20 | 20 | ||
21 | struct imx_sc_msg_req_cpu_start { | ||
22 | struct imx_sc_rpc_msg hdr; | ||
23 | u32 address_hi; | ||
24 | u32 address_lo; | ||
25 | u16 resource; | ||
26 | u8 enable; | ||
27 | } __packed; | ||
28 | |||
21 | struct imx_sc_msg_req_misc_get_ctrl { | 29 | struct imx_sc_msg_req_misc_get_ctrl { |
22 | struct imx_sc_rpc_msg hdr; | 30 | struct imx_sc_rpc_msg hdr; |
23 | u32 ctrl; | 31 | u32 ctrl; |
@@ -97,3 +105,33 @@ int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, | |||
97 | return 0; | 105 | return 0; |
98 | } | 106 | } |
99 | EXPORT_SYMBOL(imx_sc_misc_get_control); | 107 | EXPORT_SYMBOL(imx_sc_misc_get_control); |
108 | |||
109 | /* | ||
110 | * This function starts/stops a CPU identified by @resource | ||
111 | * | ||
112 | * @param[in] ipc IPC handle | ||
113 | * @param[in] resource resource the control is associated with | ||
114 | * @param[in] enable true for start, false for stop | ||
115 | * @param[in] phys_addr initial instruction address to be executed | ||
116 | * | ||
117 | * @return Returns 0 for success and < 0 for errors. | ||
118 | */ | ||
119 | int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource, | ||
120 | bool enable, u64 phys_addr) | ||
121 | { | ||
122 | struct imx_sc_msg_req_cpu_start msg; | ||
123 | struct imx_sc_rpc_msg *hdr = &msg.hdr; | ||
124 | |||
125 | hdr->ver = IMX_SC_RPC_VERSION; | ||
126 | hdr->svc = IMX_SC_RPC_SVC_PM; | ||
127 | hdr->func = IMX_SC_PM_FUNC_CPU_START; | ||
128 | hdr->size = 4; | ||
129 | |||
130 | msg.address_hi = phys_addr >> 32; | ||
131 | msg.address_lo = phys_addr; | ||
132 | msg.resource = resource; | ||
133 | msg.enable = enable; | ||
134 | |||
135 | return imx_scu_call_rpc(ipc, &msg, true); | ||
136 | } | ||
137 | EXPORT_SYMBOL(imx_sc_pm_cpu_start); | ||
diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 407245f2efd0..39a94c7177fc 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c | |||
@@ -322,6 +322,7 @@ static int imx_sc_pd_probe(struct platform_device *pdev) | |||
322 | 322 | ||
323 | static const struct of_device_id imx_sc_pd_match[] = { | 323 | static const struct of_device_id imx_sc_pd_match[] = { |
324 | { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, | 324 | { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, |
325 | { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, | ||
325 | { /* sentinel */ } | 326 | { /* sentinel */ } |
326 | }; | 327 | }; |
327 | 328 | ||
diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index a13558154ac3..61be15d9df7d 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c | |||
@@ -238,6 +238,16 @@ static int rpi_firmware_probe(struct platform_device *pdev) | |||
238 | return 0; | 238 | return 0; |
239 | } | 239 | } |
240 | 240 | ||
241 | static void rpi_firmware_shutdown(struct platform_device *pdev) | ||
242 | { | ||
243 | struct rpi_firmware *fw = platform_get_drvdata(pdev); | ||
244 | |||
245 | if (!fw) | ||
246 | return; | ||
247 | |||
248 | rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_REBOOT, NULL, 0); | ||
249 | } | ||
250 | |||
241 | static int rpi_firmware_remove(struct platform_device *pdev) | 251 | static int rpi_firmware_remove(struct platform_device *pdev) |
242 | { | 252 | { |
243 | struct rpi_firmware *fw = platform_get_drvdata(pdev); | 253 | struct rpi_firmware *fw = platform_get_drvdata(pdev); |
@@ -278,6 +288,7 @@ static struct platform_driver rpi_firmware_driver = { | |||
278 | .of_match_table = rpi_firmware_of_match, | 288 | .of_match_table = rpi_firmware_of_match, |
279 | }, | 289 | }, |
280 | .probe = rpi_firmware_probe, | 290 | .probe = rpi_firmware_probe, |
291 | .shutdown = rpi_firmware_shutdown, | ||
281 | .remove = rpi_firmware_remove, | 292 | .remove = rpi_firmware_remove, |
282 | }; | 293 | }; |
283 | module_platform_driver(rpi_firmware_driver); | 294 | module_platform_driver(rpi_firmware_driver); |
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 1b826dcca719..676b01caff05 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile | |||
@@ -1,4 +1,7 @@ | |||
1 | tegra-bpmp-y = bpmp.o | 1 | tegra-bpmp-y = bpmp.o |
2 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o | ||
3 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o | ||
4 | tegra-bpmp-$(CONFIG_ARCH_TEGRA_194_SOC) += bpmp-tegra186.o | ||
2 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o | 5 | tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o |
3 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o | 6 | obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o |
4 | obj-$(CONFIG_TEGRA_IVC) += ivc.o | 7 | obj-$(CONFIG_TEGRA_IVC) += ivc.o |
diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h new file mode 100644 index 000000000000..54d560c48398 --- /dev/null +++ b/drivers/firmware/tegra/bpmp-private.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
4 | */ | ||
5 | |||
6 | #ifndef __FIRMWARE_TEGRA_BPMP_PRIVATE_H | ||
7 | #define __FIRMWARE_TEGRA_BPMP_PRIVATE_H | ||
8 | |||
9 | #include <soc/tegra/bpmp.h> | ||
10 | |||
11 | struct tegra_bpmp_ops { | ||
12 | int (*init)(struct tegra_bpmp *bpmp); | ||
13 | void (*deinit)(struct tegra_bpmp *bpmp); | ||
14 | bool (*is_response_ready)(struct tegra_bpmp_channel *channel); | ||
15 | bool (*is_request_ready)(struct tegra_bpmp_channel *channel); | ||
16 | int (*ack_response)(struct tegra_bpmp_channel *channel); | ||
17 | int (*ack_request)(struct tegra_bpmp_channel *channel); | ||
18 | bool (*is_response_channel_free)(struct tegra_bpmp_channel *channel); | ||
19 | bool (*is_request_channel_free)(struct tegra_bpmp_channel *channel); | ||
20 | int (*post_response)(struct tegra_bpmp_channel *channel); | ||
21 | int (*post_request)(struct tegra_bpmp_channel *channel); | ||
22 | int (*ring_doorbell)(struct tegra_bpmp *bpmp); | ||
23 | int (*resume)(struct tegra_bpmp *bpmp); | ||
24 | }; | ||
25 | |||
26 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | ||
27 | IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) | ||
28 | extern const struct tegra_bpmp_ops tegra186_bpmp_ops; | ||
29 | #endif | ||
30 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) | ||
31 | extern const struct tegra_bpmp_ops tegra210_bpmp_ops; | ||
32 | #endif | ||
33 | |||
34 | #endif | ||
diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c new file mode 100644 index 000000000000..ea308751635f --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra186.c | |||
@@ -0,0 +1,305 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
4 | */ | ||
5 | |||
6 | #include <linux/genalloc.h> | ||
7 | #include <linux/mailbox_client.h> | ||
8 | #include <linux/platform_device.h> | ||
9 | |||
10 | #include <soc/tegra/bpmp.h> | ||
11 | #include <soc/tegra/bpmp-abi.h> | ||
12 | #include <soc/tegra/ivc.h> | ||
13 | |||
14 | #include "bpmp-private.h" | ||
15 | |||
16 | struct tegra186_bpmp { | ||
17 | struct tegra_bpmp *parent; | ||
18 | |||
19 | struct { | ||
20 | struct gen_pool *pool; | ||
21 | dma_addr_t phys; | ||
22 | void *virt; | ||
23 | } tx, rx; | ||
24 | |||
25 | struct { | ||
26 | struct mbox_client client; | ||
27 | struct mbox_chan *channel; | ||
28 | } mbox; | ||
29 | }; | ||
30 | |||
31 | static inline struct tegra_bpmp * | ||
32 | mbox_client_to_bpmp(struct mbox_client *client) | ||
33 | { | ||
34 | struct tegra186_bpmp *priv; | ||
35 | |||
36 | priv = container_of(client, struct tegra186_bpmp, mbox.client); | ||
37 | |||
38 | return priv->parent; | ||
39 | } | ||
40 | |||
41 | static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel) | ||
42 | { | ||
43 | void *frame; | ||
44 | |||
45 | frame = tegra_ivc_read_get_next_frame(channel->ivc); | ||
46 | if (IS_ERR(frame)) { | ||
47 | channel->ib = NULL; | ||
48 | return false; | ||
49 | } | ||
50 | |||
51 | channel->ib = frame; | ||
52 | |||
53 | return true; | ||
54 | } | ||
55 | |||
56 | static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel) | ||
57 | { | ||
58 | void *frame; | ||
59 | |||
60 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | ||
61 | if (IS_ERR(frame)) { | ||
62 | channel->ob = NULL; | ||
63 | return false; | ||
64 | } | ||
65 | |||
66 | channel->ob = frame; | ||
67 | |||
68 | return true; | ||
69 | } | ||
70 | |||
71 | static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel) | ||
72 | { | ||
73 | return tegra_ivc_read_advance(channel->ivc); | ||
74 | } | ||
75 | |||
76 | static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel) | ||
77 | { | ||
78 | return tegra_ivc_write_advance(channel->ivc); | ||
79 | } | ||
80 | |||
81 | static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
82 | { | ||
83 | struct tegra186_bpmp *priv = bpmp->priv; | ||
84 | int err; | ||
85 | |||
86 | err = mbox_send_message(priv->mbox.channel, NULL); | ||
87 | if (err < 0) | ||
88 | return err; | ||
89 | |||
90 | mbox_client_txdone(priv->mbox.channel, 0); | ||
91 | |||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
96 | { | ||
97 | struct tegra_bpmp *bpmp = data; | ||
98 | struct tegra186_bpmp *priv = bpmp->priv; | ||
99 | |||
100 | if (WARN_ON(priv->mbox.channel == NULL)) | ||
101 | return; | ||
102 | |||
103 | tegra186_bpmp_ring_doorbell(bpmp); | ||
104 | } | ||
105 | |||
106 | static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
107 | struct tegra_bpmp *bpmp, | ||
108 | unsigned int index) | ||
109 | { | ||
110 | struct tegra186_bpmp *priv = bpmp->priv; | ||
111 | size_t message_size, queue_size; | ||
112 | unsigned int offset; | ||
113 | int err; | ||
114 | |||
115 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
116 | GFP_KERNEL); | ||
117 | if (!channel->ivc) | ||
118 | return -ENOMEM; | ||
119 | |||
120 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
121 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
122 | offset = queue_size * index; | ||
123 | |||
124 | err = tegra_ivc_init(channel->ivc, NULL, | ||
125 | priv->rx.virt + offset, priv->rx.phys + offset, | ||
126 | priv->tx.virt + offset, priv->tx.phys + offset, | ||
127 | 1, message_size, tegra186_bpmp_ivc_notify, | ||
128 | bpmp); | ||
129 | if (err < 0) { | ||
130 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
131 | index, err); | ||
132 | return err; | ||
133 | } | ||
134 | |||
135 | init_completion(&channel->completion); | ||
136 | channel->bpmp = bpmp; | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
142 | { | ||
143 | /* reset the channel state */ | ||
144 | tegra_ivc_reset(channel->ivc); | ||
145 | |||
146 | /* sync the channel state with BPMP */ | ||
147 | while (tegra_ivc_notified(channel->ivc)) | ||
148 | ; | ||
149 | } | ||
150 | |||
151 | static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
152 | { | ||
153 | tegra_ivc_cleanup(channel->ivc); | ||
154 | } | ||
155 | |||
156 | static void mbox_handle_rx(struct mbox_client *client, void *data) | ||
157 | { | ||
158 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
159 | |||
160 | tegra_bpmp_handle_rx(bpmp); | ||
161 | } | ||
162 | |||
163 | static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) | ||
164 | { | ||
165 | struct tegra186_bpmp *priv; | ||
166 | unsigned int i; | ||
167 | int err; | ||
168 | |||
169 | priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL); | ||
170 | if (!priv) | ||
171 | return -ENOMEM; | ||
172 | |||
173 | bpmp->priv = priv; | ||
174 | priv->parent = bpmp; | ||
175 | |||
176 | priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0); | ||
177 | if (!priv->tx.pool) { | ||
178 | dev_err(bpmp->dev, "TX shmem pool not found\n"); | ||
179 | return -ENOMEM; | ||
180 | } | ||
181 | |||
182 | priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); | ||
183 | if (!priv->tx.virt) { | ||
184 | dev_err(bpmp->dev, "failed to allocate from TX pool\n"); | ||
185 | return -ENOMEM; | ||
186 | } | ||
187 | |||
188 | priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1); | ||
189 | if (!priv->rx.pool) { | ||
190 | dev_err(bpmp->dev, "RX shmem pool not found\n"); | ||
191 | err = -ENOMEM; | ||
192 | goto free_tx; | ||
193 | } | ||
194 | |||
195 | priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); | ||
196 | if (!priv->rx.virt) { | ||
197 | dev_err(bpmp->dev, "failed to allocate from RX pool\n"); | ||
198 | err = -ENOMEM; | ||
199 | goto free_tx; | ||
200 | } | ||
201 | |||
202 | err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
203 | bpmp->soc->channels.cpu_tx.offset); | ||
204 | if (err < 0) | ||
205 | goto free_rx; | ||
206 | |||
207 | err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp, | ||
208 | bpmp->soc->channels.cpu_rx.offset); | ||
209 | if (err < 0) | ||
210 | goto cleanup_tx_channel; | ||
211 | |||
212 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
213 | unsigned int index = bpmp->soc->channels.thread.offset + i; | ||
214 | |||
215 | err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i], | ||
216 | bpmp, index); | ||
217 | if (err < 0) | ||
218 | goto cleanup_channels; | ||
219 | } | ||
220 | |||
221 | /* mbox registration */ | ||
222 | priv->mbox.client.dev = bpmp->dev; | ||
223 | priv->mbox.client.rx_callback = mbox_handle_rx; | ||
224 | priv->mbox.client.tx_block = false; | ||
225 | priv->mbox.client.knows_txdone = false; | ||
226 | |||
227 | priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0); | ||
228 | if (IS_ERR(priv->mbox.channel)) { | ||
229 | err = PTR_ERR(priv->mbox.channel); | ||
230 | dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err); | ||
231 | goto cleanup_channels; | ||
232 | } | ||
233 | |||
234 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
235 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
236 | |||
237 | for (i = 0; i < bpmp->threaded.count; i++) | ||
238 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
239 | |||
240 | return 0; | ||
241 | |||
242 | cleanup_channels: | ||
243 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
244 | if (!bpmp->threaded_channels[i].bpmp) | ||
245 | continue; | ||
246 | |||
247 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
248 | } | ||
249 | |||
250 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
251 | cleanup_tx_channel: | ||
252 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
253 | free_rx: | ||
254 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
255 | free_tx: | ||
256 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
257 | |||
258 | return err; | ||
259 | } | ||
260 | |||
261 | static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp) | ||
262 | { | ||
263 | struct tegra186_bpmp *priv = bpmp->priv; | ||
264 | unsigned int i; | ||
265 | |||
266 | mbox_free_channel(priv->mbox.channel); | ||
267 | |||
268 | for (i = 0; i < bpmp->threaded.count; i++) | ||
269 | tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
270 | |||
271 | tegra186_bpmp_channel_cleanup(bpmp->rx_channel); | ||
272 | tegra186_bpmp_channel_cleanup(bpmp->tx_channel); | ||
273 | |||
274 | gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); | ||
275 | gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); | ||
276 | } | ||
277 | |||
278 | static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp) | ||
279 | { | ||
280 | unsigned int i; | ||
281 | |||
282 | /* reset message channels */ | ||
283 | tegra186_bpmp_channel_reset(bpmp->tx_channel); | ||
284 | tegra186_bpmp_channel_reset(bpmp->rx_channel); | ||
285 | |||
286 | for (i = 0; i < bpmp->threaded.count; i++) | ||
287 | tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | const struct tegra_bpmp_ops tegra186_bpmp_ops = { | ||
293 | .init = tegra186_bpmp_init, | ||
294 | .deinit = tegra186_bpmp_deinit, | ||
295 | .is_response_ready = tegra186_bpmp_is_message_ready, | ||
296 | .is_request_ready = tegra186_bpmp_is_message_ready, | ||
297 | .ack_response = tegra186_bpmp_ack_message, | ||
298 | .ack_request = tegra186_bpmp_ack_message, | ||
299 | .is_response_channel_free = tegra186_bpmp_is_channel_free, | ||
300 | .is_request_channel_free = tegra186_bpmp_is_channel_free, | ||
301 | .post_response = tegra186_bpmp_post_message, | ||
302 | .post_request = tegra186_bpmp_post_message, | ||
303 | .ring_doorbell = tegra186_bpmp_ring_doorbell, | ||
304 | .resume = tegra186_bpmp_resume, | ||
305 | }; | ||
diff --git a/drivers/firmware/tegra/bpmp-tegra210.c b/drivers/firmware/tegra/bpmp-tegra210.c new file mode 100644 index 000000000000..ae15940a078e --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra210.c | |||
@@ -0,0 +1,243 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2018, NVIDIA CORPORATION. | ||
4 | */ | ||
5 | |||
6 | #include <linux/interrupt.h> | ||
7 | #include <linux/irq.h> | ||
8 | #include <linux/io.h> | ||
9 | #include <linux/of.h> | ||
10 | #include <linux/platform_device.h> | ||
11 | |||
12 | #include <soc/tegra/bpmp.h> | ||
13 | |||
14 | #include "bpmp-private.h" | ||
15 | |||
16 | #define TRIGGER_OFFSET 0x000 | ||
17 | #define RESULT_OFFSET(id) (0xc00 + id * 4) | ||
18 | #define TRIGGER_ID_SHIFT 16 | ||
19 | #define TRIGGER_CMD_GET 4 | ||
20 | |||
21 | #define STA_OFFSET 0 | ||
22 | #define SET_OFFSET 4 | ||
23 | #define CLR_OFFSET 8 | ||
24 | |||
25 | #define CH_MASK(ch) (0x3 << ((ch) * 2)) | ||
26 | #define SL_SIGL(ch) (0x0 << ((ch) * 2)) | ||
27 | #define SL_QUED(ch) (0x1 << ((ch) * 2)) | ||
28 | #define MA_FREE(ch) (0x2 << ((ch) * 2)) | ||
29 | #define MA_ACKD(ch) (0x3 << ((ch) * 2)) | ||
30 | |||
31 | struct tegra210_bpmp { | ||
32 | void __iomem *atomics; | ||
33 | void __iomem *arb_sema; | ||
34 | struct irq_data *tx_irq_data; | ||
35 | }; | ||
36 | |||
37 | static u32 bpmp_channel_status(struct tegra_bpmp *bpmp, unsigned int index) | ||
38 | { | ||
39 | struct tegra210_bpmp *priv = bpmp->priv; | ||
40 | |||
41 | return __raw_readl(priv->arb_sema + STA_OFFSET) & CH_MASK(index); | ||
42 | } | ||
43 | |||
44 | static bool tegra210_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) | ||
45 | { | ||
46 | unsigned int index = channel->index; | ||
47 | |||
48 | return bpmp_channel_status(channel->bpmp, index) == MA_ACKD(index); | ||
49 | } | ||
50 | |||
51 | static bool tegra210_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) | ||
52 | { | ||
53 | unsigned int index = channel->index; | ||
54 | |||
55 | return bpmp_channel_status(channel->bpmp, index) == SL_SIGL(index); | ||
56 | } | ||
57 | |||
58 | static bool | ||
59 | tegra210_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) | ||
60 | { | ||
61 | unsigned int index = channel->index; | ||
62 | |||
63 | return bpmp_channel_status(channel->bpmp, index) == MA_FREE(index); | ||
64 | } | ||
65 | |||
66 | static bool | ||
67 | tegra210_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) | ||
68 | { | ||
69 | unsigned int index = channel->index; | ||
70 | |||
71 | return bpmp_channel_status(channel->bpmp, index) == SL_QUED(index); | ||
72 | } | ||
73 | |||
74 | static int tegra210_bpmp_post_request(struct tegra_bpmp_channel *channel) | ||
75 | { | ||
76 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
77 | |||
78 | __raw_writel(CH_MASK(channel->index), priv->arb_sema + CLR_OFFSET); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int tegra210_bpmp_post_response(struct tegra_bpmp_channel *channel) | ||
84 | { | ||
85 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
86 | |||
87 | __raw_writel(MA_ACKD(channel->index), priv->arb_sema + SET_OFFSET); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int tegra210_bpmp_ack_response(struct tegra_bpmp_channel *channel) | ||
93 | { | ||
94 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
95 | |||
96 | __raw_writel(MA_ACKD(channel->index) ^ MA_FREE(channel->index), | ||
97 | priv->arb_sema + CLR_OFFSET); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int tegra210_bpmp_ack_request(struct tegra_bpmp_channel *channel) | ||
103 | { | ||
104 | struct tegra210_bpmp *priv = channel->bpmp->priv; | ||
105 | |||
106 | __raw_writel(SL_QUED(channel->index), priv->arb_sema + SET_OFFSET); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int tegra210_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
112 | { | ||
113 | struct tegra210_bpmp *priv = bpmp->priv; | ||
114 | struct irq_data *irq_data = priv->tx_irq_data; | ||
115 | |||
116 | /* | ||
117 | * Tegra Legacy Interrupt Controller (LIC) is used to notify BPMP of | ||
118 | * available messages | ||
119 | */ | ||
120 | if (irq_data->chip->irq_retrigger) | ||
121 | return irq_data->chip->irq_retrigger(irq_data); | ||
122 | |||
123 | return -EINVAL; | ||
124 | } | ||
125 | |||
126 | static irqreturn_t rx_irq(int irq, void *data) | ||
127 | { | ||
128 | struct tegra_bpmp *bpmp = data; | ||
129 | |||
130 | tegra_bpmp_handle_rx(bpmp); | ||
131 | |||
132 | return IRQ_HANDLED; | ||
133 | } | ||
134 | |||
135 | static int tegra210_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
136 | struct tegra_bpmp *bpmp, | ||
137 | unsigned int index) | ||
138 | { | ||
139 | struct tegra210_bpmp *priv = bpmp->priv; | ||
140 | u32 address; | ||
141 | void *p; | ||
142 | |||
143 | /* Retrieve channel base address from BPMP */ | ||
144 | writel(index << TRIGGER_ID_SHIFT | TRIGGER_CMD_GET, | ||
145 | priv->atomics + TRIGGER_OFFSET); | ||
146 | address = readl(priv->atomics + RESULT_OFFSET(index)); | ||
147 | |||
148 | p = devm_ioremap(bpmp->dev, address, 0x80); | ||
149 | if (!p) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | channel->ib = p; | ||
153 | channel->ob = p; | ||
154 | channel->index = index; | ||
155 | init_completion(&channel->completion); | ||
156 | channel->bpmp = bpmp; | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int tegra210_bpmp_init(struct tegra_bpmp *bpmp) | ||
162 | { | ||
163 | struct platform_device *pdev = to_platform_device(bpmp->dev); | ||
164 | struct tegra210_bpmp *priv; | ||
165 | struct resource *res; | ||
166 | unsigned int i; | ||
167 | int err; | ||
168 | |||
169 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
170 | if (!priv) | ||
171 | return -ENOMEM; | ||
172 | |||
173 | bpmp->priv = priv; | ||
174 | |||
175 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
176 | priv->atomics = devm_ioremap_resource(&pdev->dev, res); | ||
177 | if (IS_ERR(priv->atomics)) | ||
178 | return PTR_ERR(priv->atomics); | ||
179 | |||
180 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
181 | priv->arb_sema = devm_ioremap_resource(&pdev->dev, res); | ||
182 | if (IS_ERR(priv->arb_sema)) | ||
183 | return PTR_ERR(priv->arb_sema); | ||
184 | |||
185 | err = tegra210_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
186 | bpmp->soc->channels.cpu_tx.offset); | ||
187 | if (err < 0) | ||
188 | return err; | ||
189 | |||
190 | err = tegra210_bpmp_channel_init(bpmp->rx_channel, bpmp, | ||
191 | bpmp->soc->channels.cpu_rx.offset); | ||
192 | if (err < 0) | ||
193 | return err; | ||
194 | |||
195 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
196 | unsigned int index = bpmp->soc->channels.thread.offset + i; | ||
197 | |||
198 | err = tegra210_bpmp_channel_init(&bpmp->threaded_channels[i], | ||
199 | bpmp, index); | ||
200 | if (err < 0) | ||
201 | return err; | ||
202 | } | ||
203 | |||
204 | err = platform_get_irq_byname(pdev, "tx"); | ||
205 | if (err < 0) { | ||
206 | dev_err(&pdev->dev, "failed to get TX IRQ: %d\n", err); | ||
207 | return err; | ||
208 | } | ||
209 | |||
210 | priv->tx_irq_data = irq_get_irq_data(err); | ||
211 | if (!priv->tx_irq_data) { | ||
212 | dev_err(&pdev->dev, "failed to get IRQ data for TX IRQ\n"); | ||
213 | return err; | ||
214 | } | ||
215 | |||
216 | err = platform_get_irq_byname(pdev, "rx"); | ||
217 | if (err < 0) { | ||
218 | dev_err(&pdev->dev, "failed to get rx IRQ: %d\n", err); | ||
219 | return err; | ||
220 | } | ||
221 | |||
222 | err = devm_request_irq(&pdev->dev, err, rx_irq, | ||
223 | IRQF_NO_SUSPEND, dev_name(&pdev->dev), bpmp); | ||
224 | if (err < 0) { | ||
225 | dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); | ||
226 | return err; | ||
227 | } | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | const struct tegra_bpmp_ops tegra210_bpmp_ops = { | ||
233 | .init = tegra210_bpmp_init, | ||
234 | .is_response_ready = tegra210_bpmp_is_response_ready, | ||
235 | .is_request_ready = tegra210_bpmp_is_request_ready, | ||
236 | .ack_response = tegra210_bpmp_ack_response, | ||
237 | .ack_request = tegra210_bpmp_ack_request, | ||
238 | .is_response_channel_free = tegra210_bpmp_is_response_channel_free, | ||
239 | .is_request_channel_free = tegra210_bpmp_is_request_channel_free, | ||
240 | .post_response = tegra210_bpmp_post_response, | ||
241 | .post_request = tegra210_bpmp_post_request, | ||
242 | .ring_doorbell = tegra210_bpmp_ring_doorbell, | ||
243 | }; | ||
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 689478b92bce..dd775e8ba5a0 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c | |||
@@ -26,6 +26,8 @@ | |||
26 | #include <soc/tegra/bpmp-abi.h> | 26 | #include <soc/tegra/bpmp-abi.h> |
27 | #include <soc/tegra/ivc.h> | 27 | #include <soc/tegra/ivc.h> |
28 | 28 | ||
29 | #include "bpmp-private.h" | ||
30 | |||
29 | #define MSG_ACK BIT(0) | 31 | #define MSG_ACK BIT(0) |
30 | #define MSG_RING BIT(1) | 32 | #define MSG_RING BIT(1) |
31 | #define TAG_SZ 32 | 33 | #define TAG_SZ 32 |
@@ -36,6 +38,14 @@ mbox_client_to_bpmp(struct mbox_client *client) | |||
36 | return container_of(client, struct tegra_bpmp, mbox.client); | 38 | return container_of(client, struct tegra_bpmp, mbox.client); |
37 | } | 39 | } |
38 | 40 | ||
41 | static inline const struct tegra_bpmp_ops * | ||
42 | channel_to_ops(struct tegra_bpmp_channel *channel) | ||
43 | { | ||
44 | struct tegra_bpmp *bpmp = channel->bpmp; | ||
45 | |||
46 | return bpmp->soc->ops; | ||
47 | } | ||
48 | |||
39 | struct tegra_bpmp *tegra_bpmp_get(struct device *dev) | 49 | struct tegra_bpmp *tegra_bpmp_get(struct device *dev) |
40 | { | 50 | { |
41 | struct platform_device *pdev; | 51 | struct platform_device *pdev; |
@@ -96,22 +106,21 @@ static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg) | |||
96 | (msg->rx.size == 0 || msg->rx.data); | 106 | (msg->rx.size == 0 || msg->rx.data); |
97 | } | 107 | } |
98 | 108 | ||
99 | static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel) | 109 | static bool tegra_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) |
100 | { | 110 | { |
101 | void *frame; | 111 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
102 | 112 | ||
103 | frame = tegra_ivc_read_get_next_frame(channel->ivc); | 113 | return ops->is_response_ready(channel); |
104 | if (IS_ERR(frame)) { | 114 | } |
105 | channel->ib = NULL; | ||
106 | return false; | ||
107 | } | ||
108 | 115 | ||
109 | channel->ib = frame; | 116 | static bool tegra_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) |
117 | { | ||
118 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
110 | 119 | ||
111 | return true; | 120 | return ops->is_request_ready(channel); |
112 | } | 121 | } |
113 | 122 | ||
114 | static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel) | 123 | static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) |
115 | { | 124 | { |
116 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; | 125 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; |
117 | ktime_t end; | 126 | ktime_t end; |
@@ -119,29 +128,45 @@ static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel) | |||
119 | end = ktime_add_us(ktime_get(), timeout); | 128 | end = ktime_add_us(ktime_get(), timeout); |
120 | 129 | ||
121 | do { | 130 | do { |
122 | if (tegra_bpmp_master_acked(channel)) | 131 | if (tegra_bpmp_is_response_ready(channel)) |
123 | return 0; | 132 | return 0; |
124 | } while (ktime_before(ktime_get(), end)); | 133 | } while (ktime_before(ktime_get(), end)); |
125 | 134 | ||
126 | return -ETIMEDOUT; | 135 | return -ETIMEDOUT; |
127 | } | 136 | } |
128 | 137 | ||
129 | static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel) | 138 | static int tegra_bpmp_ack_response(struct tegra_bpmp_channel *channel) |
130 | { | 139 | { |
131 | void *frame; | 140 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); |
132 | 141 | ||
133 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | 142 | return ops->ack_response(channel); |
134 | if (IS_ERR(frame)) { | 143 | } |
135 | channel->ob = NULL; | ||
136 | return false; | ||
137 | } | ||
138 | 144 | ||
139 | channel->ob = frame; | 145 | static int tegra_bpmp_ack_request(struct tegra_bpmp_channel *channel) |
146 | { | ||
147 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
140 | 148 | ||
141 | return true; | 149 | return ops->ack_request(channel); |
142 | } | 150 | } |
143 | 151 | ||
144 | static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) | 152 | static bool |
153 | tegra_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) | ||
154 | { | ||
155 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
156 | |||
157 | return ops->is_request_channel_free(channel); | ||
158 | } | ||
159 | |||
160 | static bool | ||
161 | tegra_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) | ||
162 | { | ||
163 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
164 | |||
165 | return ops->is_response_channel_free(channel); | ||
166 | } | ||
167 | |||
168 | static int | ||
169 | tegra_bpmp_wait_request_channel_free(struct tegra_bpmp_channel *channel) | ||
145 | { | 170 | { |
146 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; | 171 | unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; |
147 | ktime_t start, now; | 172 | ktime_t start, now; |
@@ -149,7 +174,7 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) | |||
149 | start = ns_to_ktime(local_clock()); | 174 | start = ns_to_ktime(local_clock()); |
150 | 175 | ||
151 | do { | 176 | do { |
152 | if (tegra_bpmp_master_free(channel)) | 177 | if (tegra_bpmp_is_request_channel_free(channel)) |
153 | return 0; | 178 | return 0; |
154 | 179 | ||
155 | now = ns_to_ktime(local_clock()); | 180 | now = ns_to_ktime(local_clock()); |
@@ -158,6 +183,25 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) | |||
158 | return -ETIMEDOUT; | 183 | return -ETIMEDOUT; |
159 | } | 184 | } |
160 | 185 | ||
186 | static int tegra_bpmp_post_request(struct tegra_bpmp_channel *channel) | ||
187 | { | ||
188 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
189 | |||
190 | return ops->post_request(channel); | ||
191 | } | ||
192 | |||
193 | static int tegra_bpmp_post_response(struct tegra_bpmp_channel *channel) | ||
194 | { | ||
195 | const struct tegra_bpmp_ops *ops = channel_to_ops(channel); | ||
196 | |||
197 | return ops->post_response(channel); | ||
198 | } | ||
199 | |||
200 | static int tegra_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) | ||
201 | { | ||
202 | return bpmp->soc->ops->ring_doorbell(bpmp); | ||
203 | } | ||
204 | |||
161 | static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, | 205 | static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, |
162 | void *data, size_t size, int *ret) | 206 | void *data, size_t size, int *ret) |
163 | { | 207 | { |
@@ -166,7 +210,7 @@ static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, | |||
166 | if (data && size > 0) | 210 | if (data && size > 0) |
167 | memcpy(data, channel->ib->data, size); | 211 | memcpy(data, channel->ib->data, size); |
168 | 212 | ||
169 | err = tegra_ivc_read_advance(channel->ivc); | 213 | err = tegra_bpmp_ack_response(channel); |
170 | if (err < 0) | 214 | if (err < 0) |
171 | return err; | 215 | return err; |
172 | 216 | ||
@@ -210,7 +254,7 @@ static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, | |||
210 | if (data && size > 0) | 254 | if (data && size > 0) |
211 | memcpy(channel->ob->data, data, size); | 255 | memcpy(channel->ob->data, data, size); |
212 | 256 | ||
213 | return tegra_ivc_write_advance(channel->ivc); | 257 | return tegra_bpmp_post_request(channel); |
214 | } | 258 | } |
215 | 259 | ||
216 | static struct tegra_bpmp_channel * | 260 | static struct tegra_bpmp_channel * |
@@ -238,7 +282,7 @@ tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq, | |||
238 | 282 | ||
239 | channel = &bpmp->threaded_channels[index]; | 283 | channel = &bpmp->threaded_channels[index]; |
240 | 284 | ||
241 | if (!tegra_bpmp_master_free(channel)) { | 285 | if (!tegra_bpmp_is_request_channel_free(channel)) { |
242 | err = -EBUSY; | 286 | err = -EBUSY; |
243 | goto unlock; | 287 | goto unlock; |
244 | } | 288 | } |
@@ -270,7 +314,7 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, | |||
270 | { | 314 | { |
271 | int err; | 315 | int err; |
272 | 316 | ||
273 | err = tegra_bpmp_wait_master_free(channel); | 317 | err = tegra_bpmp_wait_request_channel_free(channel); |
274 | if (err < 0) | 318 | if (err < 0) |
275 | return err; | 319 | return err; |
276 | 320 | ||
@@ -302,13 +346,11 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, | |||
302 | 346 | ||
303 | spin_unlock(&bpmp->atomic_tx_lock); | 347 | spin_unlock(&bpmp->atomic_tx_lock); |
304 | 348 | ||
305 | err = mbox_send_message(bpmp->mbox.channel, NULL); | 349 | err = tegra_bpmp_ring_doorbell(bpmp); |
306 | if (err < 0) | 350 | if (err < 0) |
307 | return err; | 351 | return err; |
308 | 352 | ||
309 | mbox_client_txdone(bpmp->mbox.channel, 0); | 353 | err = tegra_bpmp_wait_response(channel); |
310 | |||
311 | err = tegra_bpmp_wait_ack(channel); | ||
312 | if (err < 0) | 354 | if (err < 0) |
313 | return err; | 355 | return err; |
314 | 356 | ||
@@ -335,12 +377,10 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, | |||
335 | if (IS_ERR(channel)) | 377 | if (IS_ERR(channel)) |
336 | return PTR_ERR(channel); | 378 | return PTR_ERR(channel); |
337 | 379 | ||
338 | err = mbox_send_message(bpmp->mbox.channel, NULL); | 380 | err = tegra_bpmp_ring_doorbell(bpmp); |
339 | if (err < 0) | 381 | if (err < 0) |
340 | return err; | 382 | return err; |
341 | 383 | ||
342 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
343 | |||
344 | timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout); | 384 | timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout); |
345 | 385 | ||
346 | err = wait_for_completion_timeout(&channel->completion, timeout); | 386 | err = wait_for_completion_timeout(&channel->completion, timeout); |
@@ -369,38 +409,34 @@ void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, | |||
369 | { | 409 | { |
370 | unsigned long flags = channel->ib->flags; | 410 | unsigned long flags = channel->ib->flags; |
371 | struct tegra_bpmp *bpmp = channel->bpmp; | 411 | struct tegra_bpmp *bpmp = channel->bpmp; |
372 | struct tegra_bpmp_mb_data *frame; | ||
373 | int err; | 412 | int err; |
374 | 413 | ||
375 | if (WARN_ON(size > MSG_DATA_MIN_SZ)) | 414 | if (WARN_ON(size > MSG_DATA_MIN_SZ)) |
376 | return; | 415 | return; |
377 | 416 | ||
378 | err = tegra_ivc_read_advance(channel->ivc); | 417 | err = tegra_bpmp_ack_request(channel); |
379 | if (WARN_ON(err < 0)) | 418 | if (WARN_ON(err < 0)) |
380 | return; | 419 | return; |
381 | 420 | ||
382 | if ((flags & MSG_ACK) == 0) | 421 | if ((flags & MSG_ACK) == 0) |
383 | return; | 422 | return; |
384 | 423 | ||
385 | frame = tegra_ivc_write_get_next_frame(channel->ivc); | 424 | if (WARN_ON(!tegra_bpmp_is_response_channel_free(channel))) |
386 | if (WARN_ON(IS_ERR(frame))) | ||
387 | return; | 425 | return; |
388 | 426 | ||
389 | frame->code = code; | 427 | channel->ob->code = code; |
390 | 428 | ||
391 | if (data && size > 0) | 429 | if (data && size > 0) |
392 | memcpy(frame->data, data, size); | 430 | memcpy(channel->ob->data, data, size); |
393 | 431 | ||
394 | err = tegra_ivc_write_advance(channel->ivc); | 432 | err = tegra_bpmp_post_response(channel); |
395 | if (WARN_ON(err < 0)) | 433 | if (WARN_ON(err < 0)) |
396 | return; | 434 | return; |
397 | 435 | ||
398 | if (flags & MSG_RING) { | 436 | if (flags & MSG_RING) { |
399 | err = mbox_send_message(bpmp->mbox.channel, NULL); | 437 | err = tegra_bpmp_ring_doorbell(bpmp); |
400 | if (WARN_ON(err < 0)) | 438 | if (WARN_ON(err < 0)) |
401 | return; | 439 | return; |
402 | |||
403 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
404 | } | 440 | } |
405 | } | 441 | } |
406 | EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return); | 442 | EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return); |
@@ -627,9 +663,8 @@ static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) | |||
627 | complete(&channel->completion); | 663 | complete(&channel->completion); |
628 | } | 664 | } |
629 | 665 | ||
630 | static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | 666 | void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp) |
631 | { | 667 | { |
632 | struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); | ||
633 | struct tegra_bpmp_channel *channel; | 668 | struct tegra_bpmp_channel *channel; |
634 | unsigned int i, count; | 669 | unsigned int i, count; |
635 | unsigned long *busy; | 670 | unsigned long *busy; |
@@ -638,7 +673,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
638 | count = bpmp->soc->channels.thread.count; | 673 | count = bpmp->soc->channels.thread.count; |
639 | busy = bpmp->threaded.busy; | 674 | busy = bpmp->threaded.busy; |
640 | 675 | ||
641 | if (tegra_bpmp_master_acked(channel)) | 676 | if (tegra_bpmp_is_request_ready(channel)) |
642 | tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel); | 677 | tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel); |
643 | 678 | ||
644 | spin_lock(&bpmp->lock); | 679 | spin_lock(&bpmp->lock); |
@@ -648,7 +683,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
648 | 683 | ||
649 | channel = &bpmp->threaded_channels[i]; | 684 | channel = &bpmp->threaded_channels[i]; |
650 | 685 | ||
651 | if (tegra_bpmp_master_acked(channel)) { | 686 | if (tegra_bpmp_is_response_ready(channel)) { |
652 | tegra_bpmp_channel_signal(channel); | 687 | tegra_bpmp_channel_signal(channel); |
653 | clear_bit(i, busy); | 688 | clear_bit(i, busy); |
654 | } | 689 | } |
@@ -657,74 +692,9 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) | |||
657 | spin_unlock(&bpmp->lock); | 692 | spin_unlock(&bpmp->lock); |
658 | } | 693 | } |
659 | 694 | ||
660 | static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) | ||
661 | { | ||
662 | struct tegra_bpmp *bpmp = data; | ||
663 | int err; | ||
664 | |||
665 | if (WARN_ON(bpmp->mbox.channel == NULL)) | ||
666 | return; | ||
667 | |||
668 | err = mbox_send_message(bpmp->mbox.channel, NULL); | ||
669 | if (err < 0) | ||
670 | return; | ||
671 | |||
672 | mbox_client_txdone(bpmp->mbox.channel, 0); | ||
673 | } | ||
674 | |||
675 | static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel, | ||
676 | struct tegra_bpmp *bpmp, | ||
677 | unsigned int index) | ||
678 | { | ||
679 | size_t message_size, queue_size; | ||
680 | unsigned int offset; | ||
681 | int err; | ||
682 | |||
683 | channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), | ||
684 | GFP_KERNEL); | ||
685 | if (!channel->ivc) | ||
686 | return -ENOMEM; | ||
687 | |||
688 | message_size = tegra_ivc_align(MSG_MIN_SZ); | ||
689 | queue_size = tegra_ivc_total_queue_size(message_size); | ||
690 | offset = queue_size * index; | ||
691 | |||
692 | err = tegra_ivc_init(channel->ivc, NULL, | ||
693 | bpmp->rx.virt + offset, bpmp->rx.phys + offset, | ||
694 | bpmp->tx.virt + offset, bpmp->tx.phys + offset, | ||
695 | 1, message_size, tegra_bpmp_ivc_notify, | ||
696 | bpmp); | ||
697 | if (err < 0) { | ||
698 | dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", | ||
699 | index, err); | ||
700 | return err; | ||
701 | } | ||
702 | |||
703 | init_completion(&channel->completion); | ||
704 | channel->bpmp = bpmp; | ||
705 | |||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | static void tegra_bpmp_channel_reset(struct tegra_bpmp_channel *channel) | ||
710 | { | ||
711 | /* reset the channel state */ | ||
712 | tegra_ivc_reset(channel->ivc); | ||
713 | |||
714 | /* sync the channel state with BPMP */ | ||
715 | while (tegra_ivc_notified(channel->ivc)) | ||
716 | ; | ||
717 | } | ||
718 | |||
719 | static void tegra_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) | ||
720 | { | ||
721 | tegra_ivc_cleanup(channel->ivc); | ||
722 | } | ||
723 | |||
724 | static int tegra_bpmp_probe(struct platform_device *pdev) | 695 | static int tegra_bpmp_probe(struct platform_device *pdev) |
725 | { | 696 | { |
726 | struct tegra_bpmp *bpmp; | 697 | struct tegra_bpmp *bpmp; |
727 | unsigned int i; | ||
728 | char tag[TAG_SZ]; | 698 | char tag[TAG_SZ]; |
729 | size_t size; | 699 | size_t size; |
730 | int err; | 700 | int err; |
@@ -736,32 +706,6 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
736 | bpmp->soc = of_device_get_match_data(&pdev->dev); | 706 | bpmp->soc = of_device_get_match_data(&pdev->dev); |
737 | bpmp->dev = &pdev->dev; | 707 | bpmp->dev = &pdev->dev; |
738 | 708 | ||
739 | bpmp->tx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 0); | ||
740 | if (!bpmp->tx.pool) { | ||
741 | dev_err(&pdev->dev, "TX shmem pool not found\n"); | ||
742 | return -ENOMEM; | ||
743 | } | ||
744 | |||
745 | bpmp->tx.virt = gen_pool_dma_alloc(bpmp->tx.pool, 4096, &bpmp->tx.phys); | ||
746 | if (!bpmp->tx.virt) { | ||
747 | dev_err(&pdev->dev, "failed to allocate from TX pool\n"); | ||
748 | return -ENOMEM; | ||
749 | } | ||
750 | |||
751 | bpmp->rx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 1); | ||
752 | if (!bpmp->rx.pool) { | ||
753 | dev_err(&pdev->dev, "RX shmem pool not found\n"); | ||
754 | err = -ENOMEM; | ||
755 | goto free_tx; | ||
756 | } | ||
757 | |||
758 | bpmp->rx.virt = gen_pool_dma_alloc(bpmp->rx.pool, 4096, &bpmp->rx.phys); | ||
759 | if (!bpmp->rx.virt) { | ||
760 | dev_err(&pdev->dev, "failed to allocate from RX pool\n"); | ||
761 | err = -ENOMEM; | ||
762 | goto free_tx; | ||
763 | } | ||
764 | |||
765 | INIT_LIST_HEAD(&bpmp->mrqs); | 709 | INIT_LIST_HEAD(&bpmp->mrqs); |
766 | spin_lock_init(&bpmp->lock); | 710 | spin_lock_init(&bpmp->lock); |
767 | 711 | ||
@@ -771,81 +715,38 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
771 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); | 715 | size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); |
772 | 716 | ||
773 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 717 | bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
774 | if (!bpmp->threaded.allocated) { | 718 | if (!bpmp->threaded.allocated) |
775 | err = -ENOMEM; | 719 | return -ENOMEM; |
776 | goto free_rx; | ||
777 | } | ||
778 | 720 | ||
779 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); | 721 | bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); |
780 | if (!bpmp->threaded.busy) { | 722 | if (!bpmp->threaded.busy) |
781 | err = -ENOMEM; | 723 | return -ENOMEM; |
782 | goto free_rx; | ||
783 | } | ||
784 | 724 | ||
785 | spin_lock_init(&bpmp->atomic_tx_lock); | 725 | spin_lock_init(&bpmp->atomic_tx_lock); |
786 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), | 726 | bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), |
787 | GFP_KERNEL); | 727 | GFP_KERNEL); |
788 | if (!bpmp->tx_channel) { | 728 | if (!bpmp->tx_channel) |
789 | err = -ENOMEM; | 729 | return -ENOMEM; |
790 | goto free_rx; | ||
791 | } | ||
792 | 730 | ||
793 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), | 731 | bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), |
794 | GFP_KERNEL); | 732 | GFP_KERNEL); |
795 | if (!bpmp->rx_channel) { | 733 | if (!bpmp->rx_channel) |
796 | err = -ENOMEM; | 734 | return -ENOMEM; |
797 | goto free_rx; | ||
798 | } | ||
799 | 735 | ||
800 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, | 736 | bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, |
801 | sizeof(*bpmp->threaded_channels), | 737 | sizeof(*bpmp->threaded_channels), |
802 | GFP_KERNEL); | 738 | GFP_KERNEL); |
803 | if (!bpmp->threaded_channels) { | 739 | if (!bpmp->threaded_channels) |
804 | err = -ENOMEM; | 740 | return -ENOMEM; |
805 | goto free_rx; | ||
806 | } | ||
807 | |||
808 | err = tegra_bpmp_channel_init(bpmp->tx_channel, bpmp, | ||
809 | bpmp->soc->channels.cpu_tx.offset); | ||
810 | if (err < 0) | ||
811 | goto free_rx; | ||
812 | 741 | ||
813 | err = tegra_bpmp_channel_init(bpmp->rx_channel, bpmp, | 742 | err = bpmp->soc->ops->init(bpmp); |
814 | bpmp->soc->channels.cpu_rx.offset); | ||
815 | if (err < 0) | 743 | if (err < 0) |
816 | goto cleanup_tx_channel; | 744 | return err; |
817 | |||
818 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
819 | err = tegra_bpmp_channel_init( | ||
820 | &bpmp->threaded_channels[i], bpmp, | ||
821 | bpmp->soc->channels.thread.offset + i); | ||
822 | if (err < 0) | ||
823 | goto cleanup_threaded_channels; | ||
824 | } | ||
825 | |||
826 | /* mbox registration */ | ||
827 | bpmp->mbox.client.dev = &pdev->dev; | ||
828 | bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx; | ||
829 | bpmp->mbox.client.tx_block = false; | ||
830 | bpmp->mbox.client.knows_txdone = false; | ||
831 | |||
832 | bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0); | ||
833 | if (IS_ERR(bpmp->mbox.channel)) { | ||
834 | err = PTR_ERR(bpmp->mbox.channel); | ||
835 | dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err); | ||
836 | goto cleanup_threaded_channels; | ||
837 | } | ||
838 | |||
839 | /* reset message channels */ | ||
840 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
841 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
842 | for (i = 0; i < bpmp->threaded.count; i++) | ||
843 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
844 | 745 | ||
845 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, | 746 | err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, |
846 | tegra_bpmp_mrq_handle_ping, bpmp); | 747 | tegra_bpmp_mrq_handle_ping, bpmp); |
847 | if (err < 0) | 748 | if (err < 0) |
848 | goto free_mbox; | 749 | goto deinit; |
849 | 750 | ||
850 | err = tegra_bpmp_ping(bpmp); | 751 | err = tegra_bpmp_ping(bpmp); |
851 | if (err < 0) { | 752 | if (err < 0) { |
@@ -867,17 +768,23 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
867 | if (err < 0) | 768 | if (err < 0) |
868 | goto free_mrq; | 769 | goto free_mrq; |
869 | 770 | ||
870 | err = tegra_bpmp_init_clocks(bpmp); | 771 | if (of_find_property(pdev->dev.of_node, "#clock-cells", NULL)) { |
871 | if (err < 0) | 772 | err = tegra_bpmp_init_clocks(bpmp); |
872 | goto free_mrq; | 773 | if (err < 0) |
774 | goto free_mrq; | ||
775 | } | ||
873 | 776 | ||
874 | err = tegra_bpmp_init_resets(bpmp); | 777 | if (of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) { |
875 | if (err < 0) | 778 | err = tegra_bpmp_init_resets(bpmp); |
876 | goto free_mrq; | 779 | if (err < 0) |
780 | goto free_mrq; | ||
781 | } | ||
877 | 782 | ||
878 | err = tegra_bpmp_init_powergates(bpmp); | 783 | if (of_find_property(pdev->dev.of_node, "#power-domain-cells", NULL)) { |
879 | if (err < 0) | 784 | err = tegra_bpmp_init_powergates(bpmp); |
880 | goto free_mrq; | 785 | if (err < 0) |
786 | goto free_mrq; | ||
787 | } | ||
881 | 788 | ||
882 | err = tegra_bpmp_init_debugfs(bpmp); | 789 | err = tegra_bpmp_init_debugfs(bpmp); |
883 | if (err < 0) | 790 | if (err < 0) |
@@ -887,41 +794,27 @@ static int tegra_bpmp_probe(struct platform_device *pdev) | |||
887 | 794 | ||
888 | free_mrq: | 795 | free_mrq: |
889 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); | 796 | tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); |
890 | free_mbox: | 797 | deinit: |
891 | mbox_free_channel(bpmp->mbox.channel); | 798 | if (bpmp->soc->ops->deinit) |
892 | cleanup_threaded_channels: | 799 | bpmp->soc->ops->deinit(bpmp); |
893 | for (i = 0; i < bpmp->threaded.count; i++) { | ||
894 | if (bpmp->threaded_channels[i].bpmp) | ||
895 | tegra_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); | ||
896 | } | ||
897 | 800 | ||
898 | tegra_bpmp_channel_cleanup(bpmp->rx_channel); | ||
899 | cleanup_tx_channel: | ||
900 | tegra_bpmp_channel_cleanup(bpmp->tx_channel); | ||
901 | free_rx: | ||
902 | gen_pool_free(bpmp->rx.pool, (unsigned long)bpmp->rx.virt, 4096); | ||
903 | free_tx: | ||
904 | gen_pool_free(bpmp->tx.pool, (unsigned long)bpmp->tx.virt, 4096); | ||
905 | return err; | 801 | return err; |
906 | } | 802 | } |
907 | 803 | ||
908 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) | 804 | static int __maybe_unused tegra_bpmp_resume(struct device *dev) |
909 | { | 805 | { |
910 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); | 806 | struct tegra_bpmp *bpmp = dev_get_drvdata(dev); |
911 | unsigned int i; | ||
912 | |||
913 | /* reset message channels */ | ||
914 | tegra_bpmp_channel_reset(bpmp->tx_channel); | ||
915 | tegra_bpmp_channel_reset(bpmp->rx_channel); | ||
916 | |||
917 | for (i = 0; i < bpmp->threaded.count; i++) | ||
918 | tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); | ||
919 | 807 | ||
920 | return 0; | 808 | if (bpmp->soc->ops->resume) |
809 | return bpmp->soc->ops->resume(bpmp); | ||
810 | else | ||
811 | return 0; | ||
921 | } | 812 | } |
922 | 813 | ||
923 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); | 814 | static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); |
924 | 815 | ||
816 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | ||
817 | IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) | ||
925 | static const struct tegra_bpmp_soc tegra186_soc = { | 818 | static const struct tegra_bpmp_soc tegra186_soc = { |
926 | .channels = { | 819 | .channels = { |
927 | .cpu_tx = { | 820 | .cpu_tx = { |
@@ -938,11 +831,42 @@ static const struct tegra_bpmp_soc tegra186_soc = { | |||
938 | .timeout = 0, | 831 | .timeout = 0, |
939 | }, | 832 | }, |
940 | }, | 833 | }, |
834 | .ops = &tegra186_bpmp_ops, | ||
941 | .num_resets = 193, | 835 | .num_resets = 193, |
942 | }; | 836 | }; |
837 | #endif | ||
838 | |||
839 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) | ||
840 | static const struct tegra_bpmp_soc tegra210_soc = { | ||
841 | .channels = { | ||
842 | .cpu_tx = { | ||
843 | .offset = 0, | ||
844 | .count = 1, | ||
845 | .timeout = 60 * USEC_PER_SEC, | ||
846 | }, | ||
847 | .thread = { | ||
848 | .offset = 4, | ||
849 | .count = 1, | ||
850 | .timeout = 600 * USEC_PER_SEC, | ||
851 | }, | ||
852 | .cpu_rx = { | ||
853 | .offset = 8, | ||
854 | .count = 1, | ||
855 | .timeout = 0, | ||
856 | }, | ||
857 | }, | ||
858 | .ops = &tegra210_bpmp_ops, | ||
859 | }; | ||
860 | #endif | ||
943 | 861 | ||
944 | static const struct of_device_id tegra_bpmp_match[] = { | 862 | static const struct of_device_id tegra_bpmp_match[] = { |
863 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ | ||
864 | IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) | ||
945 | { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, | 865 | { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, |
866 | #endif | ||
867 | #if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) | ||
868 | { .compatible = "nvidia,tegra210-bpmp", .data = &tegra210_soc }, | ||
869 | #endif | ||
946 | { } | 870 | { } |
947 | }; | 871 | }; |
948 | 872 | ||
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 69ed1464175c..3fbbb61012c4 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c | |||
@@ -146,25 +146,8 @@ static int ti_sci_debug_show(struct seq_file *s, void *unused) | |||
146 | return 0; | 146 | return 0; |
147 | } | 147 | } |
148 | 148 | ||
149 | /** | 149 | /* Provide the log file operations interface*/ |
150 | * ti_sci_debug_open() - debug file open | 150 | DEFINE_SHOW_ATTRIBUTE(ti_sci_debug); |
151 | * @inode: inode pointer | ||
152 | * @file: file pointer | ||
153 | * | ||
154 | * Return: result of single_open | ||
155 | */ | ||
156 | static int ti_sci_debug_open(struct inode *inode, struct file *file) | ||
157 | { | ||
158 | return single_open(file, ti_sci_debug_show, inode->i_private); | ||
159 | } | ||
160 | |||
161 | /* log file operations */ | ||
162 | static const struct file_operations ti_sci_debug_fops = { | ||
163 | .open = ti_sci_debug_open, | ||
164 | .read = seq_read, | ||
165 | .llseek = seq_lseek, | ||
166 | .release = single_release, | ||
167 | }; | ||
168 | 151 | ||
169 | /** | 152 | /** |
170 | * ti_sci_debugfs_create() - Create log debug file | 153 | * ti_sci_debugfs_create() - Create log debug file |
diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig index 8f44b9cd295a..bd33bbf70daf 100644 --- a/drivers/firmware/xilinx/Kconfig +++ b/drivers/firmware/xilinx/Kconfig | |||
@@ -6,6 +6,7 @@ menu "Zynq MPSoC Firmware Drivers" | |||
6 | 6 | ||
7 | config ZYNQMP_FIRMWARE | 7 | config ZYNQMP_FIRMWARE |
8 | bool "Enable Xilinx Zynq MPSoC firmware interface" | 8 | bool "Enable Xilinx Zynq MPSoC firmware interface" |
9 | select MFD_CORE | ||
9 | help | 10 | help |
10 | Firmware interface driver is used by different | 11 | Firmware interface driver is used by different |
11 | drivers to communicate with the firmware for | 12 | drivers to communicate with the firmware for |
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 9a1c72a9280f..98f936125643 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/compiler.h> | 14 | #include <linux/compiler.h> |
15 | #include <linux/device.h> | 15 | #include <linux/device.h> |
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/mfd/core.h> | ||
17 | #include <linux/module.h> | 18 | #include <linux/module.h> |
18 | #include <linux/of.h> | 19 | #include <linux/of.h> |
19 | #include <linux/of_platform.h> | 20 | #include <linux/of_platform.h> |
@@ -23,6 +24,12 @@ | |||
23 | #include <linux/firmware/xlnx-zynqmp.h> | 24 | #include <linux/firmware/xlnx-zynqmp.h> |
24 | #include "zynqmp-debug.h" | 25 | #include "zynqmp-debug.h" |
25 | 26 | ||
27 | static const struct mfd_cell firmware_devs[] = { | ||
28 | { | ||
29 | .name = "zynqmp_power_controller", | ||
30 | }, | ||
31 | }; | ||
32 | |||
26 | /** | 33 | /** |
27 | * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes | 34 | * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes |
28 | * @ret_status: PMUFW return code | 35 | * @ret_status: PMUFW return code |
@@ -187,6 +194,29 @@ static int zynqmp_pm_get_api_version(u32 *version) | |||
187 | } | 194 | } |
188 | 195 | ||
189 | /** | 196 | /** |
197 | * zynqmp_pm_get_chipid - Get silicon ID registers | ||
198 | * @idcode: IDCODE register | ||
199 | * @version: version register | ||
200 | * | ||
201 | * Return: Returns the status of the operation and the idcode and version | ||
202 | * registers in @idcode and @version. | ||
203 | */ | ||
204 | static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version) | ||
205 | { | ||
206 | u32 ret_payload[PAYLOAD_ARG_CNT]; | ||
207 | int ret; | ||
208 | |||
209 | if (!idcode || !version) | ||
210 | return -EINVAL; | ||
211 | |||
212 | ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload); | ||
213 | *idcode = ret_payload[1]; | ||
214 | *version = ret_payload[2]; | ||
215 | |||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | /** | ||
190 | * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version | 220 | * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version |
191 | * @version: Returned version value | 221 | * @version: Returned version value |
192 | * | 222 | * |
@@ -469,8 +499,129 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, | |||
469 | arg1, arg2, out); | 499 | arg1, arg2, out); |
470 | } | 500 | } |
471 | 501 | ||
502 | /** | ||
503 | * zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release) | ||
504 | * @reset: Reset to be configured | ||
505 | * @assert_flag: Flag stating should reset be asserted (1) or | ||
506 | * released (0) | ||
507 | * | ||
508 | * Return: Returns status, either success or error+reason | ||
509 | */ | ||
510 | static int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset, | ||
511 | const enum zynqmp_pm_reset_action assert_flag) | ||
512 | { | ||
513 | return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, reset, assert_flag, | ||
514 | 0, 0, NULL); | ||
515 | } | ||
516 | |||
517 | /** | ||
518 | * zynqmp_pm_reset_get_status - Get status of the reset | ||
519 | * @reset: Reset whose status should be returned | ||
520 | * @status: Returned status | ||
521 | * | ||
522 | * Return: Returns status, either success or error+reason | ||
523 | */ | ||
524 | static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, | ||
525 | u32 *status) | ||
526 | { | ||
527 | u32 ret_payload[PAYLOAD_ARG_CNT]; | ||
528 | int ret; | ||
529 | |||
530 | if (!status) | ||
531 | return -EINVAL; | ||
532 | |||
533 | ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, reset, 0, | ||
534 | 0, 0, ret_payload); | ||
535 | *status = ret_payload[1]; | ||
536 | |||
537 | return ret; | ||
538 | } | ||
539 | |||
540 | /** | ||
541 | * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller | ||
542 | * master has initialized its own power management | ||
543 | * | ||
544 | * This API function is to be used for notify the power management controller | ||
545 | * about the completed power management initialization. | ||
546 | * | ||
547 | * Return: Returns status, either success or error+reason | ||
548 | */ | ||
549 | static int zynqmp_pm_init_finalize(void) | ||
550 | { | ||
551 | return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, 0, 0, 0, 0, NULL); | ||
552 | } | ||
553 | |||
554 | /** | ||
555 | * zynqmp_pm_set_suspend_mode() - Set system suspend mode | ||
556 | * @mode: Mode to set for system suspend | ||
557 | * | ||
558 | * This API function is used to set mode of system suspend. | ||
559 | * | ||
560 | * Return: Returns status, either success or error+reason | ||
561 | */ | ||
562 | static int zynqmp_pm_set_suspend_mode(u32 mode) | ||
563 | { | ||
564 | return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, mode, 0, 0, 0, NULL); | ||
565 | } | ||
566 | |||
567 | /** | ||
568 | * zynqmp_pm_request_node() - Request a node with specific capabilities | ||
569 | * @node: Node ID of the slave | ||
570 | * @capabilities: Requested capabilities of the slave | ||
571 | * @qos: Quality of service (not supported) | ||
572 | * @ack: Flag to specify whether acknowledge is requested | ||
573 | * | ||
574 | * This function is used by master to request particular node from firmware. | ||
575 | * Every master must request node before using it. | ||
576 | * | ||
577 | * Return: Returns status, either success or error+reason | ||
578 | */ | ||
579 | static int zynqmp_pm_request_node(const u32 node, const u32 capabilities, | ||
580 | const u32 qos, | ||
581 | const enum zynqmp_pm_request_ack ack) | ||
582 | { | ||
583 | return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, node, capabilities, | ||
584 | qos, ack, NULL); | ||
585 | } | ||
586 | |||
587 | /** | ||
588 | * zynqmp_pm_release_node() - Release a node | ||
589 | * @node: Node ID of the slave | ||
590 | * | ||
591 | * This function is used by master to inform firmware that master | ||
592 | * has released node. Once released, master must not use that node | ||
593 | * without re-request. | ||
594 | * | ||
595 | * Return: Returns status, either success or error+reason | ||
596 | */ | ||
597 | static int zynqmp_pm_release_node(const u32 node) | ||
598 | { | ||
599 | return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, node, 0, 0, 0, NULL); | ||
600 | } | ||
601 | |||
602 | /** | ||
603 | * zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves | ||
604 | * @node: Node ID of the slave | ||
605 | * @capabilities: Requested capabilities of the slave | ||
606 | * @qos: Quality of service (not supported) | ||
607 | * @ack: Flag to specify whether acknowledge is requested | ||
608 | * | ||
609 | * This API function is to be used for slaves a PU already has requested | ||
610 | * to change its capabilities. | ||
611 | * | ||
612 | * Return: Returns status, either success or error+reason | ||
613 | */ | ||
614 | static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities, | ||
615 | const u32 qos, | ||
616 | const enum zynqmp_pm_request_ack ack) | ||
617 | { | ||
618 | return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, node, capabilities, | ||
619 | qos, ack, NULL); | ||
620 | } | ||
621 | |||
472 | static const struct zynqmp_eemi_ops eemi_ops = { | 622 | static const struct zynqmp_eemi_ops eemi_ops = { |
473 | .get_api_version = zynqmp_pm_get_api_version, | 623 | .get_api_version = zynqmp_pm_get_api_version, |
624 | .get_chipid = zynqmp_pm_get_chipid, | ||
474 | .query_data = zynqmp_pm_query_data, | 625 | .query_data = zynqmp_pm_query_data, |
475 | .clock_enable = zynqmp_pm_clock_enable, | 626 | .clock_enable = zynqmp_pm_clock_enable, |
476 | .clock_disable = zynqmp_pm_clock_disable, | 627 | .clock_disable = zynqmp_pm_clock_disable, |
@@ -482,6 +633,13 @@ static const struct zynqmp_eemi_ops eemi_ops = { | |||
482 | .clock_setparent = zynqmp_pm_clock_setparent, | 633 | .clock_setparent = zynqmp_pm_clock_setparent, |
483 | .clock_getparent = zynqmp_pm_clock_getparent, | 634 | .clock_getparent = zynqmp_pm_clock_getparent, |
484 | .ioctl = zynqmp_pm_ioctl, | 635 | .ioctl = zynqmp_pm_ioctl, |
636 | .reset_assert = zynqmp_pm_reset_assert, | ||
637 | .reset_get_status = zynqmp_pm_reset_get_status, | ||
638 | .init_finalize = zynqmp_pm_init_finalize, | ||
639 | .set_suspend_mode = zynqmp_pm_set_suspend_mode, | ||
640 | .request_node = zynqmp_pm_request_node, | ||
641 | .release_node = zynqmp_pm_release_node, | ||
642 | .set_requirement = zynqmp_pm_set_requirement, | ||
485 | }; | 643 | }; |
486 | 644 | ||
487 | /** | 645 | /** |
@@ -538,11 +696,19 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) | |||
538 | 696 | ||
539 | zynqmp_pm_api_debugfs_init(); | 697 | zynqmp_pm_api_debugfs_init(); |
540 | 698 | ||
699 | ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs, | ||
700 | ARRAY_SIZE(firmware_devs), NULL, 0, NULL); | ||
701 | if (ret) { | ||
702 | dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret); | ||
703 | return ret; | ||
704 | } | ||
705 | |||
541 | return of_platform_populate(dev->of_node, NULL, NULL, dev); | 706 | return of_platform_populate(dev->of_node, NULL, NULL, dev); |
542 | } | 707 | } |
543 | 708 | ||
544 | static int zynqmp_firmware_remove(struct platform_device *pdev) | 709 | static int zynqmp_firmware_remove(struct platform_device *pdev) |
545 | { | 710 | { |
711 | mfd_remove_devices(&pdev->dev); | ||
546 | zynqmp_pm_api_debugfs_exit(); | 712 | zynqmp_pm_api_debugfs_exit(); |
547 | 713 | ||
548 | return 0; | 714 | return 0; |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 12980a4ad460..ee6fb6af655e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o | |||
10 | obj-$(CONFIG_MFD_ACT8945A) += act8945a.o | 10 | obj-$(CONFIG_MFD_ACT8945A) += act8945a.o |
11 | obj-$(CONFIG_MFD_SM501) += sm501.o | 11 | obj-$(CONFIG_MFD_SM501) += sm501.o |
12 | obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o | 12 | obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o |
13 | obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o | ||
13 | obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o | 14 | obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o |
14 | obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o | 15 | obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o |
15 | cros_ec_core-objs := cros_ec.o | 16 | cros_ec_core-objs := cros_ec.o |
diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c new file mode 100644 index 000000000000..42fe67f1538e --- /dev/null +++ b/drivers/mfd/bcm2835-pm.c | |||
@@ -0,0 +1,92 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * PM MFD driver for Broadcom BCM2835 | ||
4 | * | ||
5 | * This driver binds to the PM block and creates the MFD device for | ||
6 | * the WDT and power drivers. | ||
7 | */ | ||
8 | |||
9 | #include <linux/delay.h> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/mfd/bcm2835-pm.h> | ||
12 | #include <linux/mfd/core.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/of_address.h> | ||
15 | #include <linux/of_platform.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/watchdog.h> | ||
19 | |||
20 | static const struct mfd_cell bcm2835_pm_devs[] = { | ||
21 | { .name = "bcm2835-wdt" }, | ||
22 | }; | ||
23 | |||
24 | static const struct mfd_cell bcm2835_power_devs[] = { | ||
25 | { .name = "bcm2835-power" }, | ||
26 | }; | ||
27 | |||
28 | static int bcm2835_pm_probe(struct platform_device *pdev) | ||
29 | { | ||
30 | struct resource *res; | ||
31 | struct device *dev = &pdev->dev; | ||
32 | struct bcm2835_pm *pm; | ||
33 | int ret; | ||
34 | |||
35 | pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL); | ||
36 | if (!pm) | ||
37 | return -ENOMEM; | ||
38 | platform_set_drvdata(pdev, pm); | ||
39 | |||
40 | pm->dev = dev; | ||
41 | |||
42 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
43 | pm->base = devm_ioremap_resource(dev, res); | ||
44 | if (IS_ERR(pm->base)) | ||
45 | return PTR_ERR(pm->base); | ||
46 | |||
47 | ret = devm_mfd_add_devices(dev, -1, | ||
48 | bcm2835_pm_devs, ARRAY_SIZE(bcm2835_pm_devs), | ||
49 | NULL, 0, NULL); | ||
50 | if (ret) | ||
51 | return ret; | ||
52 | |||
53 | /* We'll use the presence of the AXI ASB regs in the | ||
54 | * bcm2835-pm binding as the key for whether we can reference | ||
55 | * the full PM register range and support power domains. | ||
56 | */ | ||
57 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
58 | if (res) { | ||
59 | pm->asb = devm_ioremap_resource(dev, res); | ||
60 | if (IS_ERR(pm->asb)) | ||
61 | return PTR_ERR(pm->asb); | ||
62 | |||
63 | ret = devm_mfd_add_devices(dev, -1, | ||
64 | bcm2835_power_devs, | ||
65 | ARRAY_SIZE(bcm2835_power_devs), | ||
66 | NULL, 0, NULL); | ||
67 | if (ret) | ||
68 | return ret; | ||
69 | } | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | static const struct of_device_id bcm2835_pm_of_match[] = { | ||
75 | { .compatible = "brcm,bcm2835-pm-wdt", }, | ||
76 | { .compatible = "brcm,bcm2835-pm", }, | ||
77 | {}, | ||
78 | }; | ||
79 | MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match); | ||
80 | |||
81 | static struct platform_driver bcm2835_pm_driver = { | ||
82 | .probe = bcm2835_pm_probe, | ||
83 | .driver = { | ||
84 | .name = "bcm2835-pm", | ||
85 | .of_match_table = bcm2835_pm_of_match, | ||
86 | }, | ||
87 | }; | ||
88 | module_platform_driver(bcm2835_pm_driver); | ||
89 | |||
90 | MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); | ||
91 | MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM MFD"); | ||
92 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 02bf20f51349..2ba49e959c3f 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | |||
@@ -2468,9 +2468,14 @@ static int setup_rx_flow(struct dpaa2_eth_priv *priv, | |||
2468 | queue.destination.type = DPNI_DEST_DPCON; | 2468 | queue.destination.type = DPNI_DEST_DPCON; |
2469 | queue.destination.priority = 1; | 2469 | queue.destination.priority = 1; |
2470 | queue.user_context = (u64)(uintptr_t)fq; | 2470 | queue.user_context = (u64)(uintptr_t)fq; |
2471 | queue.flc.stash_control = 1; | ||
2472 | queue.flc.value &= 0xFFFFFFFFFFFFFFC0; | ||
2473 | /* 01 01 00 - data, annotation, flow context */ | ||
2474 | queue.flc.value |= 0x14; | ||
2471 | err = dpni_set_queue(priv->mc_io, 0, priv->mc_token, | 2475 | err = dpni_set_queue(priv->mc_io, 0, priv->mc_token, |
2472 | DPNI_QUEUE_RX, 0, fq->flowid, | 2476 | DPNI_QUEUE_RX, 0, fq->flowid, |
2473 | DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST, | 2477 | DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST | |
2478 | DPNI_QUEUE_OPT_FLC, | ||
2474 | &queue); | 2479 | &queue); |
2475 | if (err) { | 2480 | if (err) { |
2476 | dev_err(dev, "dpni_set_queue(RX) failed\n"); | 2481 | dev_err(dev, "dpni_set_queue(RX) failed\n"); |
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 0a7a470ee859..4ad846ceac7c 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig | |||
@@ -192,4 +192,14 @@ config SC27XX_EFUSE | |||
192 | This driver can also be built as a module. If so, the module | 192 | This driver can also be built as a module. If so, the module |
193 | will be called nvmem-sc27xx-efuse. | 193 | will be called nvmem-sc27xx-efuse. |
194 | 194 | ||
195 | config NVMEM_ZYNQMP | ||
196 | bool "Xilinx ZYNQMP SoC nvmem firmware support" | ||
197 | depends on ARCH_ZYNQMP | ||
198 | help | ||
199 | This is a driver to access hardware related data like | ||
200 | soc revision, IDCODE... etc by using the firmware | ||
201 | interface. | ||
202 | |||
203 | If sure, say yes. If unsure, say no. | ||
204 | |||
195 | endif | 205 | endif |
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 4e8c61628f1a..2ece8ffffdda 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile | |||
@@ -41,3 +41,5 @@ obj-$(CONFIG_RAVE_SP_EEPROM) += nvmem-rave-sp-eeprom.o | |||
41 | nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o | 41 | nvmem-rave-sp-eeprom-y := rave-sp-eeprom.o |
42 | obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o | 42 | obj-$(CONFIG_SC27XX_EFUSE) += nvmem-sc27xx-efuse.o |
43 | nvmem-sc27xx-efuse-y := sc27xx-efuse.o | 43 | nvmem-sc27xx-efuse-y := sc27xx-efuse.o |
44 | obj-$(CONFIG_NVMEM_ZYNQMP) += nvmem_zynqmp_nvmem.o | ||
45 | nvmem_zynqmp_nvmem-y := zynqmp_nvmem.o | ||
diff --git a/drivers/nvmem/zynqmp_nvmem.c b/drivers/nvmem/zynqmp_nvmem.c new file mode 100644 index 000000000000..490c8fcaec80 --- /dev/null +++ b/drivers/nvmem/zynqmp_nvmem.c | |||
@@ -0,0 +1,86 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * Copyright (C) 2019 Xilinx, Inc. | ||
4 | */ | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/nvmem-provider.h> | ||
8 | #include <linux/of.h> | ||
9 | #include <linux/platform_device.h> | ||
10 | #include <linux/firmware/xlnx-zynqmp.h> | ||
11 | |||
12 | #define SILICON_REVISION_MASK 0xF | ||
13 | |||
14 | struct zynqmp_nvmem_data { | ||
15 | struct device *dev; | ||
16 | struct nvmem_device *nvmem; | ||
17 | }; | ||
18 | |||
19 | static int zynqmp_nvmem_read(void *context, unsigned int offset, | ||
20 | void *val, size_t bytes) | ||
21 | { | ||
22 | int ret; | ||
23 | int idcode, version; | ||
24 | struct zynqmp_nvmem_data *priv = context; | ||
25 | |||
26 | const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
27 | |||
28 | if (!eemi_ops || !eemi_ops->get_chipid) | ||
29 | return -ENXIO; | ||
30 | |||
31 | ret = eemi_ops->get_chipid(&idcode, &version); | ||
32 | if (ret < 0) | ||
33 | return ret; | ||
34 | |||
35 | dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version); | ||
36 | *(int *)val = version & SILICON_REVISION_MASK; | ||
37 | |||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | static struct nvmem_config econfig = { | ||
42 | .name = "zynqmp-nvmem", | ||
43 | .owner = THIS_MODULE, | ||
44 | .word_size = 1, | ||
45 | .size = 1, | ||
46 | .read_only = true, | ||
47 | }; | ||
48 | |||
49 | static const struct of_device_id zynqmp_nvmem_match[] = { | ||
50 | { .compatible = "xlnx,zynqmp-nvmem-fw", }, | ||
51 | { /* sentinel */ }, | ||
52 | }; | ||
53 | MODULE_DEVICE_TABLE(of, zynqmp_nvmem_match); | ||
54 | |||
55 | static int zynqmp_nvmem_probe(struct platform_device *pdev) | ||
56 | { | ||
57 | struct device *dev = &pdev->dev; | ||
58 | struct zynqmp_nvmem_data *priv; | ||
59 | |||
60 | priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL); | ||
61 | if (!priv) | ||
62 | return -ENOMEM; | ||
63 | |||
64 | priv->dev = dev; | ||
65 | econfig.dev = dev; | ||
66 | econfig.reg_read = zynqmp_nvmem_read; | ||
67 | econfig.priv = priv; | ||
68 | |||
69 | priv->nvmem = devm_nvmem_register(dev, &econfig); | ||
70 | |||
71 | return PTR_ERR_OR_ZERO(priv->nvmem); | ||
72 | } | ||
73 | |||
74 | static struct platform_driver zynqmp_nvmem_driver = { | ||
75 | .probe = zynqmp_nvmem_probe, | ||
76 | .driver = { | ||
77 | .name = "zynqmp-nvmem", | ||
78 | .of_match_table = zynqmp_nvmem_match, | ||
79 | }, | ||
80 | }; | ||
81 | |||
82 | module_platform_driver(zynqmp_nvmem_driver); | ||
83 | |||
84 | MODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>, Nava kishore Manne <navam@xilinx.com>"); | ||
85 | MODULE_DESCRIPTION("ZynqMP NVMEM driver"); | ||
86 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 18f1639dbc4a..e06a0ab05ad6 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c | |||
@@ -131,6 +131,24 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) | |||
131 | EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); | 131 | EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); |
132 | 132 | ||
133 | /** | 133 | /** |
134 | * dev_pm_opp_get_level() - Gets the level corresponding to an available opp | ||
135 | * @opp: opp for which level value has to be returned for | ||
136 | * | ||
137 | * Return: level read from device tree corresponding to the opp, else | ||
138 | * return 0. | ||
139 | */ | ||
140 | unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) | ||
141 | { | ||
142 | if (IS_ERR_OR_NULL(opp) || !opp->available) { | ||
143 | pr_err("%s: Invalid parameters\n", __func__); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | return opp->level; | ||
148 | } | ||
149 | EXPORT_SYMBOL_GPL(dev_pm_opp_get_level); | ||
150 | |||
151 | /** | ||
134 | * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not | 152 | * dev_pm_opp_is_turbo() - Returns if opp is turbo OPP or not |
135 | * @opp: opp for which turbo mode is being verified | 153 | * @opp: opp for which turbo mode is being verified |
136 | * | 154 | * |
diff --git a/drivers/opp/of.c b/drivers/opp/of.c index 06f0f632ec47..1779f2c93291 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c | |||
@@ -594,6 +594,8 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, | |||
594 | new_opp->rate = (unsigned long)rate; | 594 | new_opp->rate = (unsigned long)rate; |
595 | } | 595 | } |
596 | 596 | ||
597 | of_property_read_u32(np, "opp-level", &new_opp->level); | ||
598 | |||
597 | /* Check if the OPP supports hardware's hierarchy of versions or not */ | 599 | /* Check if the OPP supports hardware's hierarchy of versions or not */ |
598 | if (!_opp_is_supported(dev, opp_table, np)) { | 600 | if (!_opp_is_supported(dev, opp_table, np)) { |
599 | dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); | 601 | dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); |
diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index e24d81497375..4458175aa661 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h | |||
@@ -60,6 +60,7 @@ extern struct list_head opp_tables; | |||
60 | * @suspend: true if suspend OPP | 60 | * @suspend: true if suspend OPP |
61 | * @pstate: Device's power domain's performance state. | 61 | * @pstate: Device's power domain's performance state. |
62 | * @rate: Frequency in hertz | 62 | * @rate: Frequency in hertz |
63 | * @level: Performance level | ||
63 | * @supplies: Power supplies voltage/current values | 64 | * @supplies: Power supplies voltage/current values |
64 | * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's | 65 | * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's |
65 | * frequency from any other OPP's frequency. | 66 | * frequency from any other OPP's frequency. |
@@ -80,6 +81,7 @@ struct dev_pm_opp { | |||
80 | bool suspend; | 81 | bool suspend; |
81 | unsigned int pstate; | 82 | unsigned int pstate; |
82 | unsigned long rate; | 83 | unsigned long rate; |
84 | unsigned int level; | ||
83 | 85 | ||
84 | struct dev_pm_opp_supply *supplies; | 86 | struct dev_pm_opp_supply *supplies; |
85 | 87 | ||
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 2e01bd833ffd..2c8c23db92fb 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig | |||
@@ -40,6 +40,14 @@ config RESET_BERLIN | |||
40 | help | 40 | help |
41 | This enables the reset controller driver for Marvell Berlin SoCs. | 41 | This enables the reset controller driver for Marvell Berlin SoCs. |
42 | 42 | ||
43 | config RESET_BRCMSTB | ||
44 | tristate "Broadcom STB reset controller" | ||
45 | depends on ARCH_BRCMSTB || COMPILE_TEST | ||
46 | default ARCH_BRCMSTB | ||
47 | help | ||
48 | This enables the reset controller driver for Broadcom STB SoCs using | ||
49 | a SUN_TOP_CTRL_SW_INIT style controller. | ||
50 | |||
43 | config RESET_HSDK | 51 | config RESET_HSDK |
44 | bool "Synopsys HSDK Reset Driver" | 52 | bool "Synopsys HSDK Reset Driver" |
45 | depends on HAS_IOMEM | 53 | depends on HAS_IOMEM |
@@ -48,9 +56,9 @@ config RESET_HSDK | |||
48 | This enables the reset controller driver for HSDK board. | 56 | This enables the reset controller driver for HSDK board. |
49 | 57 | ||
50 | config RESET_IMX7 | 58 | config RESET_IMX7 |
51 | bool "i.MX7 Reset Driver" if COMPILE_TEST | 59 | bool "i.MX7/8 Reset Driver" if COMPILE_TEST |
52 | depends on HAS_IOMEM | 60 | depends on HAS_IOMEM |
53 | default SOC_IMX7D | 61 | default SOC_IMX7D || (ARM64 && ARCH_MXC) |
54 | select MFD_SYSCON | 62 | select MFD_SYSCON |
55 | help | 63 | help |
56 | This enables the reset controller driver for i.MX7 SoCs. | 64 | This enables the reset controller driver for i.MX7 SoCs. |
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index dc7874df78d9..61456b8f659c 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o | |||
7 | obj-$(CONFIG_RESET_ATH79) += reset-ath79.o | 7 | obj-$(CONFIG_RESET_ATH79) += reset-ath79.o |
8 | obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o | 8 | obj-$(CONFIG_RESET_AXS10X) += reset-axs10x.o |
9 | obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o | 9 | obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o |
10 | obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o | ||
10 | obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o | 11 | obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o |
11 | obj-$(CONFIG_RESET_IMX7) += reset-imx7.o | 12 | obj-$(CONFIG_RESET_IMX7) += reset-imx7.o |
12 | obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o | 13 | obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o |
@@ -26,4 +27,5 @@ obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o | |||
26 | obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o | 27 | obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o |
27 | obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o | 28 | obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o |
28 | obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o | 29 | obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o |
30 | obj-$(CONFIG_ARCH_ZYNQMP) += reset-zynqmp.o | ||
29 | 31 | ||
diff --git a/drivers/reset/reset-brcmstb.c b/drivers/reset/reset-brcmstb.c new file mode 100644 index 000000000000..a608f445dad6 --- /dev/null +++ b/drivers/reset/reset-brcmstb.c | |||
@@ -0,0 +1,132 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Broadcom STB generic reset controller for SW_INIT style reset controller | ||
4 | * | ||
5 | * Author: Florian Fainelli <f.fainelli@gmail.com> | ||
6 | * Copyright (C) 2018 Broadcom | ||
7 | */ | ||
8 | #include <linux/delay.h> | ||
9 | #include <linux/device.h> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/of.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/reset-controller.h> | ||
15 | #include <linux/types.h> | ||
16 | |||
17 | struct brcmstb_reset { | ||
18 | void __iomem *base; | ||
19 | struct reset_controller_dev rcdev; | ||
20 | }; | ||
21 | |||
22 | #define SW_INIT_SET 0x00 | ||
23 | #define SW_INIT_CLEAR 0x04 | ||
24 | #define SW_INIT_STATUS 0x08 | ||
25 | |||
26 | #define SW_INIT_BIT(id) BIT((id) & 0x1f) | ||
27 | #define SW_INIT_BANK(id) ((id) >> 5) | ||
28 | |||
29 | /* A full bank contains extra registers that we are not utilizing but still | ||
30 | * qualify as a single bank. | ||
31 | */ | ||
32 | #define SW_INIT_BANK_SIZE 0x18 | ||
33 | |||
34 | static inline | ||
35 | struct brcmstb_reset *to_brcmstb(struct reset_controller_dev *rcdev) | ||
36 | { | ||
37 | return container_of(rcdev, struct brcmstb_reset, rcdev); | ||
38 | } | ||
39 | |||
40 | static int brcmstb_reset_assert(struct reset_controller_dev *rcdev, | ||
41 | unsigned long id) | ||
42 | { | ||
43 | unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE; | ||
44 | struct brcmstb_reset *priv = to_brcmstb(rcdev); | ||
45 | |||
46 | writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_SET); | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int brcmstb_reset_deassert(struct reset_controller_dev *rcdev, | ||
52 | unsigned long id) | ||
53 | { | ||
54 | unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE; | ||
55 | struct brcmstb_reset *priv = to_brcmstb(rcdev); | ||
56 | |||
57 | writel_relaxed(SW_INIT_BIT(id), priv->base + off + SW_INIT_CLEAR); | ||
58 | /* Maximum reset delay after de-asserting a line and seeing block | ||
59 | * operation is typically 14us for the worst case, build some slack | ||
60 | * here. | ||
61 | */ | ||
62 | usleep_range(100, 200); | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static int brcmstb_reset_status(struct reset_controller_dev *rcdev, | ||
68 | unsigned long id) | ||
69 | { | ||
70 | unsigned int off = SW_INIT_BANK(id) * SW_INIT_BANK_SIZE; | ||
71 | struct brcmstb_reset *priv = to_brcmstb(rcdev); | ||
72 | |||
73 | return readl_relaxed(priv->base + off + SW_INIT_STATUS) & | ||
74 | SW_INIT_BIT(id); | ||
75 | } | ||
76 | |||
77 | static const struct reset_control_ops brcmstb_reset_ops = { | ||
78 | .assert = brcmstb_reset_assert, | ||
79 | .deassert = brcmstb_reset_deassert, | ||
80 | .status = brcmstb_reset_status, | ||
81 | }; | ||
82 | |||
83 | static int brcmstb_reset_probe(struct platform_device *pdev) | ||
84 | { | ||
85 | struct device *kdev = &pdev->dev; | ||
86 | struct brcmstb_reset *priv; | ||
87 | struct resource *res; | ||
88 | |||
89 | priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL); | ||
90 | if (!priv) | ||
91 | return -ENOMEM; | ||
92 | |||
93 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
94 | if (!IS_ALIGNED(res->start, SW_INIT_BANK_SIZE) || | ||
95 | !IS_ALIGNED(resource_size(res), SW_INIT_BANK_SIZE)) { | ||
96 | dev_err(kdev, "incorrect register range\n"); | ||
97 | return -EINVAL; | ||
98 | } | ||
99 | |||
100 | priv->base = devm_ioremap_resource(kdev, res); | ||
101 | if (IS_ERR(priv->base)) | ||
102 | return PTR_ERR(priv->base); | ||
103 | |||
104 | dev_set_drvdata(kdev, priv); | ||
105 | |||
106 | priv->rcdev.owner = THIS_MODULE; | ||
107 | priv->rcdev.nr_resets = DIV_ROUND_DOWN_ULL(resource_size(res), | ||
108 | SW_INIT_BANK_SIZE) * 32; | ||
109 | priv->rcdev.ops = &brcmstb_reset_ops; | ||
110 | priv->rcdev.of_node = kdev->of_node; | ||
111 | /* Use defaults: 1 cell and simple xlate function */ | ||
112 | |||
113 | return devm_reset_controller_register(kdev, &priv->rcdev); | ||
114 | } | ||
115 | |||
116 | static const struct of_device_id brcmstb_reset_of_match[] = { | ||
117 | { .compatible = "brcm,brcmstb-reset" }, | ||
118 | { /* sentinel */ } | ||
119 | }; | ||
120 | |||
121 | static struct platform_driver brcmstb_reset_driver = { | ||
122 | .probe = brcmstb_reset_probe, | ||
123 | .driver = { | ||
124 | .name = "brcmstb-reset", | ||
125 | .of_match_table = brcmstb_reset_of_match, | ||
126 | }, | ||
127 | }; | ||
128 | module_platform_driver(brcmstb_reset_driver); | ||
129 | |||
130 | MODULE_AUTHOR("Broadcom"); | ||
131 | MODULE_DESCRIPTION("Broadcom STB reset controller"); | ||
132 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c index 77911fa8f31d..aed76e33a0a9 100644 --- a/drivers/reset/reset-imx7.c +++ b/drivers/reset/reset-imx7.c | |||
@@ -17,14 +17,27 @@ | |||
17 | 17 | ||
18 | #include <linux/mfd/syscon.h> | 18 | #include <linux/mfd/syscon.h> |
19 | #include <linux/mod_devicetable.h> | 19 | #include <linux/mod_devicetable.h> |
20 | #include <linux/of_device.h> | ||
20 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
21 | #include <linux/reset-controller.h> | 22 | #include <linux/reset-controller.h> |
22 | #include <linux/regmap.h> | 23 | #include <linux/regmap.h> |
23 | #include <dt-bindings/reset/imx7-reset.h> | 24 | #include <dt-bindings/reset/imx7-reset.h> |
25 | #include <dt-bindings/reset/imx8mq-reset.h> | ||
26 | |||
27 | struct imx7_src_signal { | ||
28 | unsigned int offset, bit; | ||
29 | }; | ||
30 | |||
31 | struct imx7_src_variant { | ||
32 | const struct imx7_src_signal *signals; | ||
33 | unsigned int signals_num; | ||
34 | struct reset_control_ops ops; | ||
35 | }; | ||
24 | 36 | ||
25 | struct imx7_src { | 37 | struct imx7_src { |
26 | struct reset_controller_dev rcdev; | 38 | struct reset_controller_dev rcdev; |
27 | struct regmap *regmap; | 39 | struct regmap *regmap; |
40 | const struct imx7_src_signal *signals; | ||
28 | }; | 41 | }; |
29 | 42 | ||
30 | enum imx7_src_registers { | 43 | enum imx7_src_registers { |
@@ -39,9 +52,14 @@ enum imx7_src_registers { | |||
39 | SRC_DDRC_RCR = 0x1000, | 52 | SRC_DDRC_RCR = 0x1000, |
40 | }; | 53 | }; |
41 | 54 | ||
42 | struct imx7_src_signal { | 55 | static int imx7_reset_update(struct imx7_src *imx7src, |
43 | unsigned int offset, bit; | 56 | unsigned long id, unsigned int value) |
44 | }; | 57 | { |
58 | const struct imx7_src_signal *signal = &imx7src->signals[id]; | ||
59 | |||
60 | return regmap_update_bits(imx7src->regmap, | ||
61 | signal->offset, signal->bit, value); | ||
62 | } | ||
45 | 63 | ||
46 | static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = { | 64 | static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = { |
47 | [IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) }, | 65 | [IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) }, |
@@ -81,8 +99,8 @@ static int imx7_reset_set(struct reset_controller_dev *rcdev, | |||
81 | unsigned long id, bool assert) | 99 | unsigned long id, bool assert) |
82 | { | 100 | { |
83 | struct imx7_src *imx7src = to_imx7_src(rcdev); | 101 | struct imx7_src *imx7src = to_imx7_src(rcdev); |
84 | const struct imx7_src_signal *signal = &imx7_src_signals[id]; | 102 | const unsigned int bit = imx7src->signals[id].bit; |
85 | unsigned int value = assert ? signal->bit : 0; | 103 | unsigned int value = assert ? bit : 0; |
86 | 104 | ||
87 | switch (id) { | 105 | switch (id) { |
88 | case IMX7_RESET_PCIEPHY: | 106 | case IMX7_RESET_PCIEPHY: |
@@ -95,12 +113,11 @@ static int imx7_reset_set(struct reset_controller_dev *rcdev, | |||
95 | break; | 113 | break; |
96 | 114 | ||
97 | case IMX7_RESET_PCIE_CTRL_APPS_EN: | 115 | case IMX7_RESET_PCIE_CTRL_APPS_EN: |
98 | value = (assert) ? 0 : signal->bit; | 116 | value = assert ? 0 : bit; |
99 | break; | 117 | break; |
100 | } | 118 | } |
101 | 119 | ||
102 | return regmap_update_bits(imx7src->regmap, | 120 | return imx7_reset_update(imx7src, id, value); |
103 | signal->offset, signal->bit, value); | ||
104 | } | 121 | } |
105 | 122 | ||
106 | static int imx7_reset_assert(struct reset_controller_dev *rcdev, | 123 | static int imx7_reset_assert(struct reset_controller_dev *rcdev, |
@@ -115,9 +132,133 @@ static int imx7_reset_deassert(struct reset_controller_dev *rcdev, | |||
115 | return imx7_reset_set(rcdev, id, false); | 132 | return imx7_reset_set(rcdev, id, false); |
116 | } | 133 | } |
117 | 134 | ||
118 | static const struct reset_control_ops imx7_reset_ops = { | 135 | static const struct imx7_src_variant variant_imx7 = { |
119 | .assert = imx7_reset_assert, | 136 | .signals = imx7_src_signals, |
120 | .deassert = imx7_reset_deassert, | 137 | .signals_num = ARRAY_SIZE(imx7_src_signals), |
138 | .ops = { | ||
139 | .assert = imx7_reset_assert, | ||
140 | .deassert = imx7_reset_deassert, | ||
141 | }, | ||
142 | }; | ||
143 | |||
144 | enum imx8mq_src_registers { | ||
145 | SRC_A53RCR0 = 0x0004, | ||
146 | SRC_HDMI_RCR = 0x0030, | ||
147 | SRC_DISP_RCR = 0x0034, | ||
148 | SRC_GPU_RCR = 0x0040, | ||
149 | SRC_VPU_RCR = 0x0044, | ||
150 | SRC_PCIE2_RCR = 0x0048, | ||
151 | SRC_MIPIPHY1_RCR = 0x004c, | ||
152 | SRC_MIPIPHY2_RCR = 0x0050, | ||
153 | SRC_DDRC2_RCR = 0x1004, | ||
154 | }; | ||
155 | |||
156 | static const struct imx7_src_signal imx8mq_src_signals[IMX8MQ_RESET_NUM] = { | ||
157 | [IMX8MQ_RESET_A53_CORE_POR_RESET0] = { SRC_A53RCR0, BIT(0) }, | ||
158 | [IMX8MQ_RESET_A53_CORE_POR_RESET1] = { SRC_A53RCR0, BIT(1) }, | ||
159 | [IMX8MQ_RESET_A53_CORE_POR_RESET2] = { SRC_A53RCR0, BIT(2) }, | ||
160 | [IMX8MQ_RESET_A53_CORE_POR_RESET3] = { SRC_A53RCR0, BIT(3) }, | ||
161 | [IMX8MQ_RESET_A53_CORE_RESET0] = { SRC_A53RCR0, BIT(4) }, | ||
162 | [IMX8MQ_RESET_A53_CORE_RESET1] = { SRC_A53RCR0, BIT(5) }, | ||
163 | [IMX8MQ_RESET_A53_CORE_RESET2] = { SRC_A53RCR0, BIT(6) }, | ||
164 | [IMX8MQ_RESET_A53_CORE_RESET3] = { SRC_A53RCR0, BIT(7) }, | ||
165 | [IMX8MQ_RESET_A53_DBG_RESET0] = { SRC_A53RCR0, BIT(8) }, | ||
166 | [IMX8MQ_RESET_A53_DBG_RESET1] = { SRC_A53RCR0, BIT(9) }, | ||
167 | [IMX8MQ_RESET_A53_DBG_RESET2] = { SRC_A53RCR0, BIT(10) }, | ||
168 | [IMX8MQ_RESET_A53_DBG_RESET3] = { SRC_A53RCR0, BIT(11) }, | ||
169 | [IMX8MQ_RESET_A53_ETM_RESET0] = { SRC_A53RCR0, BIT(12) }, | ||
170 | [IMX8MQ_RESET_A53_ETM_RESET1] = { SRC_A53RCR0, BIT(13) }, | ||
171 | [IMX8MQ_RESET_A53_ETM_RESET2] = { SRC_A53RCR0, BIT(14) }, | ||
172 | [IMX8MQ_RESET_A53_ETM_RESET3] = { SRC_A53RCR0, BIT(15) }, | ||
173 | [IMX8MQ_RESET_A53_SOC_DBG_RESET] = { SRC_A53RCR0, BIT(20) }, | ||
174 | [IMX8MQ_RESET_A53_L2RESET] = { SRC_A53RCR0, BIT(21) }, | ||
175 | [IMX8MQ_RESET_SW_NON_SCLR_M4C_RST] = { SRC_M4RCR, BIT(0) }, | ||
176 | [IMX8MQ_RESET_OTG1_PHY_RESET] = { SRC_USBOPHY1_RCR, BIT(0) }, | ||
177 | [IMX8MQ_RESET_OTG2_PHY_RESET] = { SRC_USBOPHY2_RCR, BIT(0) }, | ||
178 | [IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N] = { SRC_MIPIPHY_RCR, BIT(1) }, | ||
179 | [IMX8MQ_RESET_MIPI_DSI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(2) }, | ||
180 | [IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N] = { SRC_MIPIPHY_RCR, BIT(3) }, | ||
181 | [IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N] = { SRC_MIPIPHY_RCR, BIT(4) }, | ||
182 | [IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N] = { SRC_MIPIPHY_RCR, BIT(5) }, | ||
183 | [IMX8MQ_RESET_PCIEPHY] = { SRC_PCIEPHY_RCR, | ||
184 | BIT(2) | BIT(1) }, | ||
185 | [IMX8MQ_RESET_PCIEPHY_PERST] = { SRC_PCIEPHY_RCR, BIT(3) }, | ||
186 | [IMX8MQ_RESET_PCIE_CTRL_APPS_EN] = { SRC_PCIEPHY_RCR, BIT(6) }, | ||
187 | [IMX8MQ_RESET_PCIE_CTRL_APPS_TURNOFF] = { SRC_PCIEPHY_RCR, BIT(11) }, | ||
188 | [IMX8MQ_RESET_HDMI_PHY_APB_RESET] = { SRC_HDMI_RCR, BIT(0) }, | ||
189 | [IMX8MQ_RESET_DISP_RESET] = { SRC_DISP_RCR, BIT(0) }, | ||
190 | [IMX8MQ_RESET_GPU_RESET] = { SRC_GPU_RCR, BIT(0) }, | ||
191 | [IMX8MQ_RESET_VPU_RESET] = { SRC_VPU_RCR, BIT(0) }, | ||
192 | [IMX8MQ_RESET_PCIEPHY2] = { SRC_PCIE2_RCR, | ||
193 | BIT(2) | BIT(1) }, | ||
194 | [IMX8MQ_RESET_PCIEPHY2_PERST] = { SRC_PCIE2_RCR, BIT(3) }, | ||
195 | [IMX8MQ_RESET_PCIE2_CTRL_APPS_EN] = { SRC_PCIE2_RCR, BIT(6) }, | ||
196 | [IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF] = { SRC_PCIE2_RCR, BIT(11) }, | ||
197 | [IMX8MQ_RESET_MIPI_CSI1_CORE_RESET] = { SRC_MIPIPHY1_RCR, BIT(0) }, | ||
198 | [IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET] = { SRC_MIPIPHY1_RCR, BIT(1) }, | ||
199 | [IMX8MQ_RESET_MIPI_CSI1_ESC_RESET] = { SRC_MIPIPHY1_RCR, BIT(2) }, | ||
200 | [IMX8MQ_RESET_MIPI_CSI2_CORE_RESET] = { SRC_MIPIPHY2_RCR, BIT(0) }, | ||
201 | [IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET] = { SRC_MIPIPHY2_RCR, BIT(1) }, | ||
202 | [IMX8MQ_RESET_MIPI_CSI2_ESC_RESET] = { SRC_MIPIPHY2_RCR, BIT(2) }, | ||
203 | [IMX8MQ_RESET_DDRC1_PRST] = { SRC_DDRC_RCR, BIT(0) }, | ||
204 | [IMX8MQ_RESET_DDRC1_CORE_RESET] = { SRC_DDRC_RCR, BIT(1) }, | ||
205 | [IMX8MQ_RESET_DDRC1_PHY_RESET] = { SRC_DDRC_RCR, BIT(2) }, | ||
206 | [IMX8MQ_RESET_DDRC2_PHY_RESET] = { SRC_DDRC2_RCR, BIT(0) }, | ||
207 | [IMX8MQ_RESET_DDRC2_CORE_RESET] = { SRC_DDRC2_RCR, BIT(1) }, | ||
208 | [IMX8MQ_RESET_DDRC2_PRST] = { SRC_DDRC2_RCR, BIT(2) }, | ||
209 | }; | ||
210 | |||
211 | static int imx8mq_reset_set(struct reset_controller_dev *rcdev, | ||
212 | unsigned long id, bool assert) | ||
213 | { | ||
214 | struct imx7_src *imx7src = to_imx7_src(rcdev); | ||
215 | const unsigned int bit = imx7src->signals[id].bit; | ||
216 | unsigned int value = assert ? bit : 0; | ||
217 | |||
218 | switch (id) { | ||
219 | case IMX8MQ_RESET_PCIEPHY: | ||
220 | case IMX8MQ_RESET_PCIEPHY2: /* fallthrough */ | ||
221 | /* | ||
222 | * wait for more than 10us to release phy g_rst and | ||
223 | * btnrst | ||
224 | */ | ||
225 | if (!assert) | ||
226 | udelay(10); | ||
227 | break; | ||
228 | |||
229 | case IMX8MQ_RESET_PCIE_CTRL_APPS_EN: | ||
230 | case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN: /* fallthrough */ | ||
231 | case IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N: /* fallthrough */ | ||
232 | case IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N: /* fallthrough */ | ||
233 | case IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N: /* fallthrough */ | ||
234 | case IMX8MQ_RESET_MIPI_DSI_RESET_N: /* fallthrough */ | ||
235 | case IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N: /* fallthrough */ | ||
236 | value = assert ? 0 : bit; | ||
237 | break; | ||
238 | } | ||
239 | |||
240 | return imx7_reset_update(imx7src, id, value); | ||
241 | } | ||
242 | |||
243 | static int imx8mq_reset_assert(struct reset_controller_dev *rcdev, | ||
244 | unsigned long id) | ||
245 | { | ||
246 | return imx8mq_reset_set(rcdev, id, true); | ||
247 | } | ||
248 | |||
249 | static int imx8mq_reset_deassert(struct reset_controller_dev *rcdev, | ||
250 | unsigned long id) | ||
251 | { | ||
252 | return imx8mq_reset_set(rcdev, id, false); | ||
253 | } | ||
254 | |||
255 | static const struct imx7_src_variant variant_imx8mq = { | ||
256 | .signals = imx8mq_src_signals, | ||
257 | .signals_num = ARRAY_SIZE(imx8mq_src_signals), | ||
258 | .ops = { | ||
259 | .assert = imx8mq_reset_assert, | ||
260 | .deassert = imx8mq_reset_deassert, | ||
261 | }, | ||
121 | }; | 262 | }; |
122 | 263 | ||
123 | static int imx7_reset_probe(struct platform_device *pdev) | 264 | static int imx7_reset_probe(struct platform_device *pdev) |
@@ -125,11 +266,13 @@ static int imx7_reset_probe(struct platform_device *pdev) | |||
125 | struct imx7_src *imx7src; | 266 | struct imx7_src *imx7src; |
126 | struct device *dev = &pdev->dev; | 267 | struct device *dev = &pdev->dev; |
127 | struct regmap_config config = { .name = "src" }; | 268 | struct regmap_config config = { .name = "src" }; |
269 | const struct imx7_src_variant *variant = of_device_get_match_data(dev); | ||
128 | 270 | ||
129 | imx7src = devm_kzalloc(dev, sizeof(*imx7src), GFP_KERNEL); | 271 | imx7src = devm_kzalloc(dev, sizeof(*imx7src), GFP_KERNEL); |
130 | if (!imx7src) | 272 | if (!imx7src) |
131 | return -ENOMEM; | 273 | return -ENOMEM; |
132 | 274 | ||
275 | imx7src->signals = variant->signals; | ||
133 | imx7src->regmap = syscon_node_to_regmap(dev->of_node); | 276 | imx7src->regmap = syscon_node_to_regmap(dev->of_node); |
134 | if (IS_ERR(imx7src->regmap)) { | 277 | if (IS_ERR(imx7src->regmap)) { |
135 | dev_err(dev, "Unable to get imx7-src regmap"); | 278 | dev_err(dev, "Unable to get imx7-src regmap"); |
@@ -138,15 +281,16 @@ static int imx7_reset_probe(struct platform_device *pdev) | |||
138 | regmap_attach_dev(dev, imx7src->regmap, &config); | 281 | regmap_attach_dev(dev, imx7src->regmap, &config); |
139 | 282 | ||
140 | imx7src->rcdev.owner = THIS_MODULE; | 283 | imx7src->rcdev.owner = THIS_MODULE; |
141 | imx7src->rcdev.nr_resets = IMX7_RESET_NUM; | 284 | imx7src->rcdev.nr_resets = variant->signals_num; |
142 | imx7src->rcdev.ops = &imx7_reset_ops; | 285 | imx7src->rcdev.ops = &variant->ops; |
143 | imx7src->rcdev.of_node = dev->of_node; | 286 | imx7src->rcdev.of_node = dev->of_node; |
144 | 287 | ||
145 | return devm_reset_controller_register(dev, &imx7src->rcdev); | 288 | return devm_reset_controller_register(dev, &imx7src->rcdev); |
146 | } | 289 | } |
147 | 290 | ||
148 | static const struct of_device_id imx7_reset_dt_ids[] = { | 291 | static const struct of_device_id imx7_reset_dt_ids[] = { |
149 | { .compatible = "fsl,imx7d-src", }, | 292 | { .compatible = "fsl,imx7d-src", .data = &variant_imx7 }, |
293 | { .compatible = "fsl,imx8mq-src", .data = &variant_imx8mq }, | ||
150 | { /* sentinel */ }, | 294 | { /* sentinel */ }, |
151 | }; | 295 | }; |
152 | 296 | ||
diff --git a/drivers/reset/reset-socfpga.c b/drivers/reset/reset-socfpga.c index 318cfc51c441..96953992c2bb 100644 --- a/drivers/reset/reset-socfpga.c +++ b/drivers/reset/reset-socfpga.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/of_address.h> | 11 | #include <linux/of_address.h> |
12 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
13 | #include <linux/reset-controller.h> | 13 | #include <linux/reset-controller.h> |
14 | #include <linux/reset/socfpga.h> | ||
14 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
15 | #include <linux/spinlock.h> | 16 | #include <linux/spinlock.h> |
16 | #include <linux/types.h> | 17 | #include <linux/types.h> |
@@ -18,7 +19,6 @@ | |||
18 | #include "reset-simple.h" | 19 | #include "reset-simple.h" |
19 | 20 | ||
20 | #define SOCFPGA_NR_BANKS 8 | 21 | #define SOCFPGA_NR_BANKS 8 |
21 | void __init socfpga_reset_init(void); | ||
22 | 22 | ||
23 | static int a10_reset_init(struct device_node *np) | 23 | static int a10_reset_init(struct device_node *np) |
24 | { | 24 | { |
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index db9a1a75523f..b06d724d8f21 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/reset-controller.h> | 20 | #include <linux/reset-controller.h> |
21 | #include <linux/reset/sunxi.h> | ||
21 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
22 | #include <linux/spinlock.h> | 23 | #include <linux/spinlock.h> |
23 | #include <linux/types.h> | 24 | #include <linux/types.h> |
diff --git a/drivers/reset/reset-zynqmp.c b/drivers/reset/reset-zynqmp.c new file mode 100644 index 000000000000..2ef1f13aa47b --- /dev/null +++ b/drivers/reset/reset-zynqmp.c | |||
@@ -0,0 +1,114 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * Copyright (C) 2018 Xilinx, Inc. | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | #include <linux/err.h> | ||
8 | #include <linux/of.h> | ||
9 | #include <linux/platform_device.h> | ||
10 | #include <linux/reset-controller.h> | ||
11 | #include <linux/firmware/xlnx-zynqmp.h> | ||
12 | |||
13 | #define ZYNQMP_NR_RESETS (ZYNQMP_PM_RESET_END - ZYNQMP_PM_RESET_START) | ||
14 | #define ZYNQMP_RESET_ID ZYNQMP_PM_RESET_START | ||
15 | |||
16 | struct zynqmp_reset_data { | ||
17 | struct reset_controller_dev rcdev; | ||
18 | const struct zynqmp_eemi_ops *eemi_ops; | ||
19 | }; | ||
20 | |||
21 | static inline struct zynqmp_reset_data * | ||
22 | to_zynqmp_reset_data(struct reset_controller_dev *rcdev) | ||
23 | { | ||
24 | return container_of(rcdev, struct zynqmp_reset_data, rcdev); | ||
25 | } | ||
26 | |||
27 | static int zynqmp_reset_assert(struct reset_controller_dev *rcdev, | ||
28 | unsigned long id) | ||
29 | { | ||
30 | struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev); | ||
31 | |||
32 | return priv->eemi_ops->reset_assert(ZYNQMP_RESET_ID + id, | ||
33 | PM_RESET_ACTION_ASSERT); | ||
34 | } | ||
35 | |||
36 | static int zynqmp_reset_deassert(struct reset_controller_dev *rcdev, | ||
37 | unsigned long id) | ||
38 | { | ||
39 | struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev); | ||
40 | |||
41 | return priv->eemi_ops->reset_assert(ZYNQMP_RESET_ID + id, | ||
42 | PM_RESET_ACTION_RELEASE); | ||
43 | } | ||
44 | |||
45 | static int zynqmp_reset_status(struct reset_controller_dev *rcdev, | ||
46 | unsigned long id) | ||
47 | { | ||
48 | struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev); | ||
49 | int val, err; | ||
50 | |||
51 | err = priv->eemi_ops->reset_get_status(ZYNQMP_RESET_ID + id, &val); | ||
52 | if (err) | ||
53 | return err; | ||
54 | |||
55 | return val; | ||
56 | } | ||
57 | |||
58 | static int zynqmp_reset_reset(struct reset_controller_dev *rcdev, | ||
59 | unsigned long id) | ||
60 | { | ||
61 | struct zynqmp_reset_data *priv = to_zynqmp_reset_data(rcdev); | ||
62 | |||
63 | return priv->eemi_ops->reset_assert(ZYNQMP_RESET_ID + id, | ||
64 | PM_RESET_ACTION_PULSE); | ||
65 | } | ||
66 | |||
67 | static struct reset_control_ops zynqmp_reset_ops = { | ||
68 | .reset = zynqmp_reset_reset, | ||
69 | .assert = zynqmp_reset_assert, | ||
70 | .deassert = zynqmp_reset_deassert, | ||
71 | .status = zynqmp_reset_status, | ||
72 | }; | ||
73 | |||
74 | static int zynqmp_reset_probe(struct platform_device *pdev) | ||
75 | { | ||
76 | struct zynqmp_reset_data *priv; | ||
77 | |||
78 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | ||
79 | if (!priv) | ||
80 | return -ENOMEM; | ||
81 | |||
82 | platform_set_drvdata(pdev, priv); | ||
83 | |||
84 | priv->eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
85 | if (!priv->eemi_ops) | ||
86 | return -ENXIO; | ||
87 | |||
88 | priv->rcdev.ops = &zynqmp_reset_ops; | ||
89 | priv->rcdev.owner = THIS_MODULE; | ||
90 | priv->rcdev.of_node = pdev->dev.of_node; | ||
91 | priv->rcdev.nr_resets = ZYNQMP_NR_RESETS; | ||
92 | |||
93 | return devm_reset_controller_register(&pdev->dev, &priv->rcdev); | ||
94 | } | ||
95 | |||
96 | static const struct of_device_id zynqmp_reset_dt_ids[] = { | ||
97 | { .compatible = "xlnx,zynqmp-reset", }, | ||
98 | { /* sentinel */ }, | ||
99 | }; | ||
100 | |||
101 | static struct platform_driver zynqmp_reset_driver = { | ||
102 | .probe = zynqmp_reset_probe, | ||
103 | .driver = { | ||
104 | .name = KBUILD_MODNAME, | ||
105 | .of_match_table = zynqmp_reset_dt_ids, | ||
106 | }, | ||
107 | }; | ||
108 | |||
109 | static int __init zynqmp_reset_init(void) | ||
110 | { | ||
111 | return platform_driver_register(&zynqmp_reset_driver); | ||
112 | } | ||
113 | |||
114 | arch_initcall(zynqmp_reset_init); | ||
diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c index fce33ca76bb6..be95a37c3fec 100644 --- a/drivers/soc/amlogic/meson-canvas.c +++ b/drivers/soc/amlogic/meson-canvas.c | |||
@@ -51,16 +51,30 @@ struct meson_canvas *meson_canvas_get(struct device *dev) | |||
51 | { | 51 | { |
52 | struct device_node *canvas_node; | 52 | struct device_node *canvas_node; |
53 | struct platform_device *canvas_pdev; | 53 | struct platform_device *canvas_pdev; |
54 | struct meson_canvas *canvas; | ||
54 | 55 | ||
55 | canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0); | 56 | canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0); |
56 | if (!canvas_node) | 57 | if (!canvas_node) |
57 | return ERR_PTR(-ENODEV); | 58 | return ERR_PTR(-ENODEV); |
58 | 59 | ||
59 | canvas_pdev = of_find_device_by_node(canvas_node); | 60 | canvas_pdev = of_find_device_by_node(canvas_node); |
60 | if (!canvas_pdev) | 61 | if (!canvas_pdev) { |
62 | of_node_put(canvas_node); | ||
61 | return ERR_PTR(-EPROBE_DEFER); | 63 | return ERR_PTR(-EPROBE_DEFER); |
64 | } | ||
65 | |||
66 | of_node_put(canvas_node); | ||
67 | |||
68 | /* | ||
69 | * If priv is NULL, it's probably because the canvas hasn't | ||
70 | * properly initialized. Bail out with -EINVAL because, in the | ||
71 | * current state, this driver probe cannot return -EPROBE_DEFER | ||
72 | */ | ||
73 | canvas = dev_get_drvdata(&canvas_pdev->dev); | ||
74 | if (!canvas) | ||
75 | return ERR_PTR(-EINVAL); | ||
62 | 76 | ||
63 | return dev_get_drvdata(&canvas_pdev->dev); | 77 | return canvas; |
64 | } | 78 | } |
65 | EXPORT_SYMBOL_GPL(meson_canvas_get); | 79 | EXPORT_SYMBOL_GPL(meson_canvas_get); |
66 | 80 | ||
diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index daea191a66fa..19d4cbc93a17 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c | |||
@@ -165,6 +165,194 @@ static struct meson_msr_id clk_msr_gx[CLK_MSR_MAX] = { | |||
165 | CLK_MSR_ID(82, "ge2d"), | 165 | CLK_MSR_ID(82, "ge2d"), |
166 | }; | 166 | }; |
167 | 167 | ||
168 | static struct meson_msr_id clk_msr_axg[CLK_MSR_MAX] = { | ||
169 | CLK_MSR_ID(0, "ring_osc_out_ee_0"), | ||
170 | CLK_MSR_ID(1, "ring_osc_out_ee_1"), | ||
171 | CLK_MSR_ID(2, "ring_osc_out_ee_2"), | ||
172 | CLK_MSR_ID(3, "a53_ring_osc"), | ||
173 | CLK_MSR_ID(4, "gp0_pll"), | ||
174 | CLK_MSR_ID(5, "gp1_pll"), | ||
175 | CLK_MSR_ID(7, "clk81"), | ||
176 | CLK_MSR_ID(9, "encl"), | ||
177 | CLK_MSR_ID(17, "sys_pll_div16"), | ||
178 | CLK_MSR_ID(18, "sys_cpu_div16"), | ||
179 | CLK_MSR_ID(20, "rtc_osc_out"), | ||
180 | CLK_MSR_ID(23, "mmc_clk"), | ||
181 | CLK_MSR_ID(28, "sar_adc"), | ||
182 | CLK_MSR_ID(31, "mpll_test_out"), | ||
183 | CLK_MSR_ID(40, "mod_eth_tx_clk"), | ||
184 | CLK_MSR_ID(41, "mod_eth_rx_clk_rmii"), | ||
185 | CLK_MSR_ID(42, "mp0_out"), | ||
186 | CLK_MSR_ID(43, "fclk_div5"), | ||
187 | CLK_MSR_ID(44, "pwm_b"), | ||
188 | CLK_MSR_ID(45, "pwm_a"), | ||
189 | CLK_MSR_ID(46, "vpu"), | ||
190 | CLK_MSR_ID(47, "ddr_dpll_pt"), | ||
191 | CLK_MSR_ID(48, "mp1_out"), | ||
192 | CLK_MSR_ID(49, "mp2_out"), | ||
193 | CLK_MSR_ID(50, "mp3_out"), | ||
194 | CLK_MSR_ID(51, "sd_emmm_c"), | ||
195 | CLK_MSR_ID(52, "sd_emmc_b"), | ||
196 | CLK_MSR_ID(61, "gpio_msr"), | ||
197 | CLK_MSR_ID(66, "audio_slv_lrclk_c"), | ||
198 | CLK_MSR_ID(67, "audio_slv_lrclk_b"), | ||
199 | CLK_MSR_ID(68, "audio_slv_lrclk_a"), | ||
200 | CLK_MSR_ID(69, "audio_slv_sclk_c"), | ||
201 | CLK_MSR_ID(70, "audio_slv_sclk_b"), | ||
202 | CLK_MSR_ID(71, "audio_slv_sclk_a"), | ||
203 | CLK_MSR_ID(72, "pwm_d"), | ||
204 | CLK_MSR_ID(73, "pwm_c"), | ||
205 | CLK_MSR_ID(74, "wifi_beacon"), | ||
206 | CLK_MSR_ID(75, "tdmin_lb_lrcl"), | ||
207 | CLK_MSR_ID(76, "tdmin_lb_sclk"), | ||
208 | CLK_MSR_ID(77, "rng_ring_osc_0"), | ||
209 | CLK_MSR_ID(78, "rng_ring_osc_1"), | ||
210 | CLK_MSR_ID(79, "rng_ring_osc_2"), | ||
211 | CLK_MSR_ID(80, "rng_ring_osc_3"), | ||
212 | CLK_MSR_ID(81, "vapb"), | ||
213 | CLK_MSR_ID(82, "ge2d"), | ||
214 | CLK_MSR_ID(84, "audio_resample"), | ||
215 | CLK_MSR_ID(85, "audio_pdm_sys"), | ||
216 | CLK_MSR_ID(86, "audio_spdifout"), | ||
217 | CLK_MSR_ID(87, "audio_spdifin"), | ||
218 | CLK_MSR_ID(88, "audio_lrclk_f"), | ||
219 | CLK_MSR_ID(89, "audio_lrclk_e"), | ||
220 | CLK_MSR_ID(90, "audio_lrclk_d"), | ||
221 | CLK_MSR_ID(91, "audio_lrclk_c"), | ||
222 | CLK_MSR_ID(92, "audio_lrclk_b"), | ||
223 | CLK_MSR_ID(93, "audio_lrclk_a"), | ||
224 | CLK_MSR_ID(94, "audio_sclk_f"), | ||
225 | CLK_MSR_ID(95, "audio_sclk_e"), | ||
226 | CLK_MSR_ID(96, "audio_sclk_d"), | ||
227 | CLK_MSR_ID(97, "audio_sclk_c"), | ||
228 | CLK_MSR_ID(98, "audio_sclk_b"), | ||
229 | CLK_MSR_ID(99, "audio_sclk_a"), | ||
230 | CLK_MSR_ID(100, "audio_mclk_f"), | ||
231 | CLK_MSR_ID(101, "audio_mclk_e"), | ||
232 | CLK_MSR_ID(102, "audio_mclk_d"), | ||
233 | CLK_MSR_ID(103, "audio_mclk_c"), | ||
234 | CLK_MSR_ID(104, "audio_mclk_b"), | ||
235 | CLK_MSR_ID(105, "audio_mclk_a"), | ||
236 | CLK_MSR_ID(106, "pcie_refclk_n"), | ||
237 | CLK_MSR_ID(107, "pcie_refclk_p"), | ||
238 | CLK_MSR_ID(108, "audio_locker_out"), | ||
239 | CLK_MSR_ID(109, "audio_locker_in"), | ||
240 | }; | ||
241 | |||
242 | static struct meson_msr_id clk_msr_g12a[CLK_MSR_MAX] = { | ||
243 | CLK_MSR_ID(0, "ring_osc_out_ee_0"), | ||
244 | CLK_MSR_ID(1, "ring_osc_out_ee_1"), | ||
245 | CLK_MSR_ID(2, "ring_osc_out_ee_2"), | ||
246 | CLK_MSR_ID(3, "sys_cpu_ring_osc"), | ||
247 | CLK_MSR_ID(4, "gp0_pll"), | ||
248 | CLK_MSR_ID(6, "enci"), | ||
249 | CLK_MSR_ID(7, "clk81"), | ||
250 | CLK_MSR_ID(8, "encp"), | ||
251 | CLK_MSR_ID(9, "encl"), | ||
252 | CLK_MSR_ID(10, "vdac"), | ||
253 | CLK_MSR_ID(11, "eth_tx"), | ||
254 | CLK_MSR_ID(12, "hifi_pll"), | ||
255 | CLK_MSR_ID(13, "mod_tcon"), | ||
256 | CLK_MSR_ID(14, "fec_0"), | ||
257 | CLK_MSR_ID(15, "fec_1"), | ||
258 | CLK_MSR_ID(16, "fec_2"), | ||
259 | CLK_MSR_ID(17, "sys_pll_div16"), | ||
260 | CLK_MSR_ID(18, "sys_cpu_div16"), | ||
261 | CLK_MSR_ID(19, "lcd_an_ph2"), | ||
262 | CLK_MSR_ID(20, "rtc_osc_out"), | ||
263 | CLK_MSR_ID(21, "lcd_an_ph3"), | ||
264 | CLK_MSR_ID(22, "eth_phy_ref"), | ||
265 | CLK_MSR_ID(23, "mpll_50m"), | ||
266 | CLK_MSR_ID(24, "eth_125m"), | ||
267 | CLK_MSR_ID(25, "eth_rmii"), | ||
268 | CLK_MSR_ID(26, "sc_int"), | ||
269 | CLK_MSR_ID(27, "in_mac"), | ||
270 | CLK_MSR_ID(28, "sar_adc"), | ||
271 | CLK_MSR_ID(29, "pcie_inp"), | ||
272 | CLK_MSR_ID(30, "pcie_inn"), | ||
273 | CLK_MSR_ID(31, "mpll_test_out"), | ||
274 | CLK_MSR_ID(32, "vdec"), | ||
275 | CLK_MSR_ID(33, "sys_cpu_ring_osc_1"), | ||
276 | CLK_MSR_ID(34, "eth_mpll_50m"), | ||
277 | CLK_MSR_ID(35, "mali"), | ||
278 | CLK_MSR_ID(36, "hdmi_tx_pixel"), | ||
279 | CLK_MSR_ID(37, "cdac"), | ||
280 | CLK_MSR_ID(38, "vdin_meas"), | ||
281 | CLK_MSR_ID(39, "bt656"), | ||
282 | CLK_MSR_ID(41, "eth_rx_or_rmii"), | ||
283 | CLK_MSR_ID(42, "mp0_out"), | ||
284 | CLK_MSR_ID(43, "fclk_div5"), | ||
285 | CLK_MSR_ID(44, "pwm_b"), | ||
286 | CLK_MSR_ID(45, "pwm_a"), | ||
287 | CLK_MSR_ID(46, "vpu"), | ||
288 | CLK_MSR_ID(47, "ddr_dpll_pt"), | ||
289 | CLK_MSR_ID(48, "mp1_out"), | ||
290 | CLK_MSR_ID(49, "mp2_out"), | ||
291 | CLK_MSR_ID(50, "mp3_out"), | ||
292 | CLK_MSR_ID(51, "sd_emmc_c"), | ||
293 | CLK_MSR_ID(52, "sd_emmc_b"), | ||
294 | CLK_MSR_ID(53, "sd_emmc_a"), | ||
295 | CLK_MSR_ID(54, "vpu_clkc"), | ||
296 | CLK_MSR_ID(55, "vid_pll_div_out"), | ||
297 | CLK_MSR_ID(56, "wave420l_a"), | ||
298 | CLK_MSR_ID(57, "wave420l_c"), | ||
299 | CLK_MSR_ID(58, "wave420l_b"), | ||
300 | CLK_MSR_ID(59, "hcodec"), | ||
301 | CLK_MSR_ID(61, "gpio_msr"), | ||
302 | CLK_MSR_ID(62, "hevcb"), | ||
303 | CLK_MSR_ID(63, "dsi_meas"), | ||
304 | CLK_MSR_ID(64, "spicc_1"), | ||
305 | CLK_MSR_ID(65, "spicc_0"), | ||
306 | CLK_MSR_ID(66, "vid_lock"), | ||
307 | CLK_MSR_ID(67, "dsi_phy"), | ||
308 | CLK_MSR_ID(68, "hdcp22_esm"), | ||
309 | CLK_MSR_ID(69, "hdcp22_skp"), | ||
310 | CLK_MSR_ID(70, "pwm_f"), | ||
311 | CLK_MSR_ID(71, "pwm_e"), | ||
312 | CLK_MSR_ID(72, "pwm_d"), | ||
313 | CLK_MSR_ID(73, "pwm_c"), | ||
314 | CLK_MSR_ID(75, "hevcf"), | ||
315 | CLK_MSR_ID(77, "rng_ring_osc_0"), | ||
316 | CLK_MSR_ID(78, "rng_ring_osc_1"), | ||
317 | CLK_MSR_ID(79, "rng_ring_osc_2"), | ||
318 | CLK_MSR_ID(80, "rng_ring_osc_3"), | ||
319 | CLK_MSR_ID(81, "vapb"), | ||
320 | CLK_MSR_ID(82, "ge2d"), | ||
321 | CLK_MSR_ID(83, "co_rx"), | ||
322 | CLK_MSR_ID(84, "co_tx"), | ||
323 | CLK_MSR_ID(89, "hdmi_todig"), | ||
324 | CLK_MSR_ID(90, "hdmitx_sys"), | ||
325 | CLK_MSR_ID(94, "eth_phy_rx"), | ||
326 | CLK_MSR_ID(95, "eth_phy_pll"), | ||
327 | CLK_MSR_ID(96, "vpu_b"), | ||
328 | CLK_MSR_ID(97, "cpu_b_tmp"), | ||
329 | CLK_MSR_ID(98, "ts"), | ||
330 | CLK_MSR_ID(99, "ring_osc_out_ee_3"), | ||
331 | CLK_MSR_ID(100, "ring_osc_out_ee_4"), | ||
332 | CLK_MSR_ID(101, "ring_osc_out_ee_5"), | ||
333 | CLK_MSR_ID(102, "ring_osc_out_ee_6"), | ||
334 | CLK_MSR_ID(103, "ring_osc_out_ee_7"), | ||
335 | CLK_MSR_ID(104, "ring_osc_out_ee_8"), | ||
336 | CLK_MSR_ID(105, "ring_osc_out_ee_9"), | ||
337 | CLK_MSR_ID(106, "ephy_test"), | ||
338 | CLK_MSR_ID(107, "au_dac_g128x"), | ||
339 | CLK_MSR_ID(108, "audio_locker_out"), | ||
340 | CLK_MSR_ID(109, "audio_locker_in"), | ||
341 | CLK_MSR_ID(110, "audio_tdmout_c_sclk"), | ||
342 | CLK_MSR_ID(111, "audio_tdmout_b_sclk"), | ||
343 | CLK_MSR_ID(112, "audio_tdmout_a_sclk"), | ||
344 | CLK_MSR_ID(113, "audio_tdmin_lb_sclk"), | ||
345 | CLK_MSR_ID(114, "audio_tdmin_c_sclk"), | ||
346 | CLK_MSR_ID(115, "audio_tdmin_b_sclk"), | ||
347 | CLK_MSR_ID(116, "audio_tdmin_a_sclk"), | ||
348 | CLK_MSR_ID(117, "audio_resample"), | ||
349 | CLK_MSR_ID(118, "audio_pdm_sys"), | ||
350 | CLK_MSR_ID(119, "audio_spdifout_b"), | ||
351 | CLK_MSR_ID(120, "audio_spdifout"), | ||
352 | CLK_MSR_ID(121, "audio_spdifin"), | ||
353 | CLK_MSR_ID(122, "audio_pdm_dclk"), | ||
354 | }; | ||
355 | |||
168 | static int meson_measure_id(struct meson_msr_id *clk_msr_id, | 356 | static int meson_measure_id(struct meson_msr_id *clk_msr_id, |
169 | unsigned int duration) | 357 | unsigned int duration) |
170 | { | 358 | { |
@@ -337,6 +525,14 @@ static const struct of_device_id meson_msr_match_table[] = { | |||
337 | .compatible = "amlogic,meson8b-clk-measure", | 525 | .compatible = "amlogic,meson8b-clk-measure", |
338 | .data = (void *)clk_msr_m8, | 526 | .data = (void *)clk_msr_m8, |
339 | }, | 527 | }, |
528 | { | ||
529 | .compatible = "amlogic,meson-axg-clk-measure", | ||
530 | .data = (void *)clk_msr_axg, | ||
531 | }, | ||
532 | { | ||
533 | .compatible = "amlogic,meson-g12a-clk-measure", | ||
534 | .data = (void *)clk_msr_g12a, | ||
535 | }, | ||
340 | { /* sentinel */ } | 536 | { /* sentinel */ } |
341 | }; | 537 | }; |
342 | 538 | ||
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig index 055a845ed979..03fa91fbe2da 100644 --- a/drivers/soc/bcm/Kconfig +++ b/drivers/soc/bcm/Kconfig | |||
@@ -1,5 +1,17 @@ | |||
1 | menu "Broadcom SoC drivers" | 1 | menu "Broadcom SoC drivers" |
2 | 2 | ||
3 | config BCM2835_POWER | ||
4 | bool "BCM2835 power domain driver" | ||
5 | depends on ARCH_BCM2835 || (COMPILE_TEST && OF) | ||
6 | default y if ARCH_BCM2835 | ||
7 | select PM_GENERIC_DOMAINS if PM | ||
8 | select RESET_CONTROLLER | ||
9 | help | ||
10 | This enables support for the BCM2835 power domains and reset | ||
11 | controller. Any usage of power domains by the Raspberry Pi | ||
12 | firmware means that Linux usage of the same power domain | ||
13 | must be accessed using the RASPBERRYPI_POWER driver | ||
14 | |||
3 | config RASPBERRYPI_POWER | 15 | config RASPBERRYPI_POWER |
4 | bool "Raspberry Pi power domain driver" | 16 | bool "Raspberry Pi power domain driver" |
5 | depends on ARCH_BCM2835 || (COMPILE_TEST && OF) | 17 | depends on ARCH_BCM2835 || (COMPILE_TEST && OF) |
diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile index dc4fced72d21..c81df4b2403c 100644 --- a/drivers/soc/bcm/Makefile +++ b/drivers/soc/bcm/Makefile | |||
@@ -1,2 +1,3 @@ | |||
1 | obj-$(CONFIG_BCM2835_POWER) += bcm2835-power.o | ||
1 | obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o | 2 | obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o |
2 | obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ | 3 | obj-$(CONFIG_SOC_BRCMSTB) += brcmstb/ |
diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c new file mode 100644 index 000000000000..9351349cf0a9 --- /dev/null +++ b/drivers/soc/bcm/bcm2835-power.c | |||
@@ -0,0 +1,661 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * Power domain driver for Broadcom BCM2835 | ||
4 | * | ||
5 | * Copyright (C) 2018 Broadcom | ||
6 | */ | ||
7 | |||
8 | #include <dt-bindings/soc/bcm2835-pm.h> | ||
9 | #include <linux/clk.h> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/io.h> | ||
12 | #include <linux/mfd/bcm2835-pm.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/pm_domain.h> | ||
16 | #include <linux/reset-controller.h> | ||
17 | #include <linux/types.h> | ||
18 | |||
19 | #define PM_GNRIC 0x00 | ||
20 | #define PM_AUDIO 0x04 | ||
21 | #define PM_STATUS 0x18 | ||
22 | #define PM_RSTC 0x1c | ||
23 | #define PM_RSTS 0x20 | ||
24 | #define PM_WDOG 0x24 | ||
25 | #define PM_PADS0 0x28 | ||
26 | #define PM_PADS2 0x2c | ||
27 | #define PM_PADS3 0x30 | ||
28 | #define PM_PADS4 0x34 | ||
29 | #define PM_PADS5 0x38 | ||
30 | #define PM_PADS6 0x3c | ||
31 | #define PM_CAM0 0x44 | ||
32 | #define PM_CAM0_LDOHPEN BIT(2) | ||
33 | #define PM_CAM0_LDOLPEN BIT(1) | ||
34 | #define PM_CAM0_CTRLEN BIT(0) | ||
35 | |||
36 | #define PM_CAM1 0x48 | ||
37 | #define PM_CAM1_LDOHPEN BIT(2) | ||
38 | #define PM_CAM1_LDOLPEN BIT(1) | ||
39 | #define PM_CAM1_CTRLEN BIT(0) | ||
40 | |||
41 | #define PM_CCP2TX 0x4c | ||
42 | #define PM_CCP2TX_LDOEN BIT(1) | ||
43 | #define PM_CCP2TX_CTRLEN BIT(0) | ||
44 | |||
45 | #define PM_DSI0 0x50 | ||
46 | #define PM_DSI0_LDOHPEN BIT(2) | ||
47 | #define PM_DSI0_LDOLPEN BIT(1) | ||
48 | #define PM_DSI0_CTRLEN BIT(0) | ||
49 | |||
50 | #define PM_DSI1 0x54 | ||
51 | #define PM_DSI1_LDOHPEN BIT(2) | ||
52 | #define PM_DSI1_LDOLPEN BIT(1) | ||
53 | #define PM_DSI1_CTRLEN BIT(0) | ||
54 | |||
55 | #define PM_HDMI 0x58 | ||
56 | #define PM_HDMI_RSTDR BIT(19) | ||
57 | #define PM_HDMI_LDOPD BIT(1) | ||
58 | #define PM_HDMI_CTRLEN BIT(0) | ||
59 | |||
60 | #define PM_USB 0x5c | ||
61 | /* The power gates must be enabled with this bit before enabling the LDO in the | ||
62 | * USB block. | ||
63 | */ | ||
64 | #define PM_USB_CTRLEN BIT(0) | ||
65 | |||
66 | #define PM_PXLDO 0x60 | ||
67 | #define PM_PXBG 0x64 | ||
68 | #define PM_DFT 0x68 | ||
69 | #define PM_SMPS 0x6c | ||
70 | #define PM_XOSC 0x70 | ||
71 | #define PM_SPAREW 0x74 | ||
72 | #define PM_SPARER 0x78 | ||
73 | #define PM_AVS_RSTDR 0x7c | ||
74 | #define PM_AVS_STAT 0x80 | ||
75 | #define PM_AVS_EVENT 0x84 | ||
76 | #define PM_AVS_INTEN 0x88 | ||
77 | #define PM_DUMMY 0xfc | ||
78 | |||
79 | #define PM_IMAGE 0x108 | ||
80 | #define PM_GRAFX 0x10c | ||
81 | #define PM_PROC 0x110 | ||
82 | #define PM_ENAB BIT(12) | ||
83 | #define PM_ISPRSTN BIT(8) | ||
84 | #define PM_H264RSTN BIT(7) | ||
85 | #define PM_PERIRSTN BIT(6) | ||
86 | #define PM_V3DRSTN BIT(6) | ||
87 | #define PM_ISFUNC BIT(5) | ||
88 | #define PM_MRDONE BIT(4) | ||
89 | #define PM_MEMREP BIT(3) | ||
90 | #define PM_ISPOW BIT(2) | ||
91 | #define PM_POWOK BIT(1) | ||
92 | #define PM_POWUP BIT(0) | ||
93 | #define PM_INRUSH_SHIFT 13 | ||
94 | #define PM_INRUSH_3_5_MA 0 | ||
95 | #define PM_INRUSH_5_MA 1 | ||
96 | #define PM_INRUSH_10_MA 2 | ||
97 | #define PM_INRUSH_20_MA 3 | ||
98 | #define PM_INRUSH_MASK (3 << PM_INRUSH_SHIFT) | ||
99 | |||
100 | #define PM_PASSWORD 0x5a000000 | ||
101 | |||
102 | #define PM_WDOG_TIME_SET 0x000fffff | ||
103 | #define PM_RSTC_WRCFG_CLR 0xffffffcf | ||
104 | #define PM_RSTS_HADWRH_SET 0x00000040 | ||
105 | #define PM_RSTC_WRCFG_SET 0x00000030 | ||
106 | #define PM_RSTC_WRCFG_FULL_RESET 0x00000020 | ||
107 | #define PM_RSTC_RESET 0x00000102 | ||
108 | |||
109 | #define PM_READ(reg) readl(power->base + (reg)) | ||
110 | #define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg)) | ||
111 | |||
112 | #define ASB_BRDG_VERSION 0x00 | ||
113 | #define ASB_CPR_CTRL 0x04 | ||
114 | |||
115 | #define ASB_V3D_S_CTRL 0x08 | ||
116 | #define ASB_V3D_M_CTRL 0x0c | ||
117 | #define ASB_ISP_S_CTRL 0x10 | ||
118 | #define ASB_ISP_M_CTRL 0x14 | ||
119 | #define ASB_H264_S_CTRL 0x18 | ||
120 | #define ASB_H264_M_CTRL 0x1c | ||
121 | |||
122 | #define ASB_REQ_STOP BIT(0) | ||
123 | #define ASB_ACK BIT(1) | ||
124 | #define ASB_EMPTY BIT(2) | ||
125 | #define ASB_FULL BIT(3) | ||
126 | |||
127 | #define ASB_AXI_BRDG_ID 0x20 | ||
128 | |||
129 | #define ASB_READ(reg) readl(power->asb + (reg)) | ||
130 | #define ASB_WRITE(reg, val) writel(PM_PASSWORD | (val), power->asb + (reg)) | ||
131 | |||
132 | struct bcm2835_power_domain { | ||
133 | struct generic_pm_domain base; | ||
134 | struct bcm2835_power *power; | ||
135 | u32 domain; | ||
136 | struct clk *clk; | ||
137 | }; | ||
138 | |||
139 | struct bcm2835_power { | ||
140 | struct device *dev; | ||
141 | /* PM registers. */ | ||
142 | void __iomem *base; | ||
143 | /* AXI Async bridge registers. */ | ||
144 | void __iomem *asb; | ||
145 | |||
146 | struct genpd_onecell_data pd_xlate; | ||
147 | struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; | ||
148 | struct reset_controller_dev reset; | ||
149 | }; | ||
150 | |||
151 | static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) | ||
152 | { | ||
153 | u64 start = ktime_get_ns(); | ||
154 | |||
155 | /* Enable the module's async AXI bridges. */ | ||
156 | ASB_WRITE(reg, ASB_READ(reg) & ~ASB_REQ_STOP); | ||
157 | while (ASB_READ(reg) & ASB_ACK) { | ||
158 | cpu_relax(); | ||
159 | if (ktime_get_ns() - start >= 1000) | ||
160 | return -ETIMEDOUT; | ||
161 | } | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) | ||
167 | { | ||
168 | u64 start = ktime_get_ns(); | ||
169 | |||
170 | /* Enable the module's async AXI bridges. */ | ||
171 | ASB_WRITE(reg, ASB_READ(reg) | ASB_REQ_STOP); | ||
172 | while (!(ASB_READ(reg) & ASB_ACK)) { | ||
173 | cpu_relax(); | ||
174 | if (ktime_get_ns() - start >= 1000) | ||
175 | return -ETIMEDOUT; | ||
176 | } | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) | ||
182 | { | ||
183 | struct bcm2835_power *power = pd->power; | ||
184 | |||
185 | /* Enable functional isolation */ | ||
186 | PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); | ||
187 | |||
188 | /* Enable electrical isolation */ | ||
189 | PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); | ||
190 | |||
191 | /* Open the power switches. */ | ||
192 | PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP); | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg) | ||
198 | { | ||
199 | struct bcm2835_power *power = pd->power; | ||
200 | struct device *dev = power->dev; | ||
201 | u64 start; | ||
202 | int ret; | ||
203 | int inrush; | ||
204 | bool powok; | ||
205 | |||
206 | /* If it was already powered on by the fw, leave it that way. */ | ||
207 | if (PM_READ(pm_reg) & PM_POWUP) | ||
208 | return 0; | ||
209 | |||
210 | /* Enable power. Allowing too much current at once may result | ||
211 | * in POWOK never getting set, so start low and ramp it up as | ||
212 | * necessary to succeed. | ||
213 | */ | ||
214 | powok = false; | ||
215 | for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; inrush++) { | ||
216 | PM_WRITE(pm_reg, | ||
217 | (PM_READ(pm_reg) & ~PM_INRUSH_MASK) | | ||
218 | (inrush << PM_INRUSH_SHIFT) | | ||
219 | PM_POWUP); | ||
220 | |||
221 | start = ktime_get_ns(); | ||
222 | while (!(powok = !!(PM_READ(pm_reg) & PM_POWOK))) { | ||
223 | cpu_relax(); | ||
224 | if (ktime_get_ns() - start >= 3000) | ||
225 | break; | ||
226 | } | ||
227 | } | ||
228 | if (!powok) { | ||
229 | dev_err(dev, "Timeout waiting for %s power OK\n", | ||
230 | pd->base.name); | ||
231 | ret = -ETIMEDOUT; | ||
232 | goto err_disable_powup; | ||
233 | } | ||
234 | |||
235 | /* Disable electrical isolation */ | ||
236 | PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW); | ||
237 | |||
238 | /* Repair memory */ | ||
239 | PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP); | ||
240 | start = ktime_get_ns(); | ||
241 | while (!(PM_READ(pm_reg) & PM_MRDONE)) { | ||
242 | cpu_relax(); | ||
243 | if (ktime_get_ns() - start >= 1000) { | ||
244 | dev_err(dev, "Timeout waiting for %s memory repair\n", | ||
245 | pd->base.name); | ||
246 | ret = -ETIMEDOUT; | ||
247 | goto err_disable_ispow; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | /* Disable functional isolation */ | ||
252 | PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC); | ||
253 | |||
254 | return 0; | ||
255 | |||
256 | err_disable_ispow: | ||
257 | PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); | ||
258 | err_disable_powup: | ||
259 | PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK)); | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | static int bcm2835_asb_power_on(struct bcm2835_power_domain *pd, | ||
264 | u32 pm_reg, | ||
265 | u32 asb_m_reg, | ||
266 | u32 asb_s_reg, | ||
267 | u32 reset_flags) | ||
268 | { | ||
269 | struct bcm2835_power *power = pd->power; | ||
270 | int ret; | ||
271 | |||
272 | ret = clk_prepare_enable(pd->clk); | ||
273 | if (ret) { | ||
274 | dev_err(power->dev, "Failed to enable clock for %s\n", | ||
275 | pd->base.name); | ||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | /* Wait 32 clocks for reset to propagate, 1 us will be enough */ | ||
280 | udelay(1); | ||
281 | |||
282 | clk_disable_unprepare(pd->clk); | ||
283 | |||
284 | /* Deassert the resets. */ | ||
285 | PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags); | ||
286 | |||
287 | ret = clk_prepare_enable(pd->clk); | ||
288 | if (ret) { | ||
289 | dev_err(power->dev, "Failed to enable clock for %s\n", | ||
290 | pd->base.name); | ||
291 | goto err_enable_resets; | ||
292 | } | ||
293 | |||
294 | ret = bcm2835_asb_enable(power, asb_m_reg); | ||
295 | if (ret) { | ||
296 | dev_err(power->dev, "Failed to enable ASB master for %s\n", | ||
297 | pd->base.name); | ||
298 | goto err_disable_clk; | ||
299 | } | ||
300 | ret = bcm2835_asb_enable(power, asb_s_reg); | ||
301 | if (ret) { | ||
302 | dev_err(power->dev, "Failed to enable ASB slave for %s\n", | ||
303 | pd->base.name); | ||
304 | goto err_disable_asb_master; | ||
305 | } | ||
306 | |||
307 | return 0; | ||
308 | |||
309 | err_disable_asb_master: | ||
310 | bcm2835_asb_disable(power, asb_m_reg); | ||
311 | err_disable_clk: | ||
312 | clk_disable_unprepare(pd->clk); | ||
313 | err_enable_resets: | ||
314 | PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); | ||
315 | return ret; | ||
316 | } | ||
317 | |||
318 | static int bcm2835_asb_power_off(struct bcm2835_power_domain *pd, | ||
319 | u32 pm_reg, | ||
320 | u32 asb_m_reg, | ||
321 | u32 asb_s_reg, | ||
322 | u32 reset_flags) | ||
323 | { | ||
324 | struct bcm2835_power *power = pd->power; | ||
325 | int ret; | ||
326 | |||
327 | ret = bcm2835_asb_disable(power, asb_s_reg); | ||
328 | if (ret) { | ||
329 | dev_warn(power->dev, "Failed to disable ASB slave for %s\n", | ||
330 | pd->base.name); | ||
331 | return ret; | ||
332 | } | ||
333 | ret = bcm2835_asb_disable(power, asb_m_reg); | ||
334 | if (ret) { | ||
335 | dev_warn(power->dev, "Failed to disable ASB master for %s\n", | ||
336 | pd->base.name); | ||
337 | bcm2835_asb_enable(power, asb_s_reg); | ||
338 | return ret; | ||
339 | } | ||
340 | |||
341 | clk_disable_unprepare(pd->clk); | ||
342 | |||
343 | /* Assert the resets. */ | ||
344 | PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain) | ||
350 | { | ||
351 | struct bcm2835_power_domain *pd = | ||
352 | container_of(domain, struct bcm2835_power_domain, base); | ||
353 | struct bcm2835_power *power = pd->power; | ||
354 | |||
355 | switch (pd->domain) { | ||
356 | case BCM2835_POWER_DOMAIN_GRAFX: | ||
357 | return bcm2835_power_power_on(pd, PM_GRAFX); | ||
358 | |||
359 | case BCM2835_POWER_DOMAIN_GRAFX_V3D: | ||
360 | return bcm2835_asb_power_on(pd, PM_GRAFX, | ||
361 | ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, | ||
362 | PM_V3DRSTN); | ||
363 | |||
364 | case BCM2835_POWER_DOMAIN_IMAGE: | ||
365 | return bcm2835_power_power_on(pd, PM_IMAGE); | ||
366 | |||
367 | case BCM2835_POWER_DOMAIN_IMAGE_PERI: | ||
368 | return bcm2835_asb_power_on(pd, PM_IMAGE, | ||
369 | 0, 0, | ||
370 | PM_PERIRSTN); | ||
371 | |||
372 | case BCM2835_POWER_DOMAIN_IMAGE_ISP: | ||
373 | return bcm2835_asb_power_on(pd, PM_IMAGE, | ||
374 | ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, | ||
375 | PM_ISPRSTN); | ||
376 | |||
377 | case BCM2835_POWER_DOMAIN_IMAGE_H264: | ||
378 | return bcm2835_asb_power_on(pd, PM_IMAGE, | ||
379 | ASB_H264_M_CTRL, ASB_H264_S_CTRL, | ||
380 | PM_H264RSTN); | ||
381 | |||
382 | case BCM2835_POWER_DOMAIN_USB: | ||
383 | PM_WRITE(PM_USB, PM_USB_CTRLEN); | ||
384 | return 0; | ||
385 | |||
386 | case BCM2835_POWER_DOMAIN_DSI0: | ||
387 | PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); | ||
388 | PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN); | ||
389 | return 0; | ||
390 | |||
391 | case BCM2835_POWER_DOMAIN_DSI1: | ||
392 | PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); | ||
393 | PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN); | ||
394 | return 0; | ||
395 | |||
396 | case BCM2835_POWER_DOMAIN_CCP2TX: | ||
397 | PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); | ||
398 | PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN); | ||
399 | return 0; | ||
400 | |||
401 | case BCM2835_POWER_DOMAIN_HDMI: | ||
402 | PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR); | ||
403 | PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN); | ||
404 | PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD); | ||
405 | usleep_range(100, 200); | ||
406 | PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR); | ||
407 | return 0; | ||
408 | |||
409 | default: | ||
410 | dev_err(power->dev, "Invalid domain %d\n", pd->domain); | ||
411 | return -EINVAL; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain) | ||
416 | { | ||
417 | struct bcm2835_power_domain *pd = | ||
418 | container_of(domain, struct bcm2835_power_domain, base); | ||
419 | struct bcm2835_power *power = pd->power; | ||
420 | |||
421 | switch (pd->domain) { | ||
422 | case BCM2835_POWER_DOMAIN_GRAFX: | ||
423 | return bcm2835_power_power_off(pd, PM_GRAFX); | ||
424 | |||
425 | case BCM2835_POWER_DOMAIN_GRAFX_V3D: | ||
426 | return bcm2835_asb_power_off(pd, PM_GRAFX, | ||
427 | ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, | ||
428 | PM_V3DRSTN); | ||
429 | |||
430 | case BCM2835_POWER_DOMAIN_IMAGE: | ||
431 | return bcm2835_power_power_off(pd, PM_IMAGE); | ||
432 | |||
433 | case BCM2835_POWER_DOMAIN_IMAGE_PERI: | ||
434 | return bcm2835_asb_power_off(pd, PM_IMAGE, | ||
435 | 0, 0, | ||
436 | PM_PERIRSTN); | ||
437 | |||
438 | case BCM2835_POWER_DOMAIN_IMAGE_ISP: | ||
439 | return bcm2835_asb_power_off(pd, PM_IMAGE, | ||
440 | ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, | ||
441 | PM_ISPRSTN); | ||
442 | |||
443 | case BCM2835_POWER_DOMAIN_IMAGE_H264: | ||
444 | return bcm2835_asb_power_off(pd, PM_IMAGE, | ||
445 | ASB_H264_M_CTRL, ASB_H264_S_CTRL, | ||
446 | PM_H264RSTN); | ||
447 | |||
448 | case BCM2835_POWER_DOMAIN_USB: | ||
449 | PM_WRITE(PM_USB, 0); | ||
450 | return 0; | ||
451 | |||
452 | case BCM2835_POWER_DOMAIN_DSI0: | ||
453 | PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); | ||
454 | PM_WRITE(PM_DSI0, 0); | ||
455 | return 0; | ||
456 | |||
457 | case BCM2835_POWER_DOMAIN_DSI1: | ||
458 | PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); | ||
459 | PM_WRITE(PM_DSI1, 0); | ||
460 | return 0; | ||
461 | |||
462 | case BCM2835_POWER_DOMAIN_CCP2TX: | ||
463 | PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); | ||
464 | PM_WRITE(PM_CCP2TX, 0); | ||
465 | return 0; | ||
466 | |||
467 | case BCM2835_POWER_DOMAIN_HDMI: | ||
468 | PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD); | ||
469 | PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN); | ||
470 | return 0; | ||
471 | |||
472 | default: | ||
473 | dev_err(power->dev, "Invalid domain %d\n", pd->domain); | ||
474 | return -EINVAL; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | static void | ||
479 | bcm2835_init_power_domain(struct bcm2835_power *power, | ||
480 | int pd_xlate_index, const char *name) | ||
481 | { | ||
482 | struct device *dev = power->dev; | ||
483 | struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index]; | ||
484 | |||
485 | dom->clk = devm_clk_get(dev->parent, name); | ||
486 | |||
487 | dom->base.name = name; | ||
488 | dom->base.power_on = bcm2835_power_pd_power_on; | ||
489 | dom->base.power_off = bcm2835_power_pd_power_off; | ||
490 | |||
491 | dom->domain = pd_xlate_index; | ||
492 | dom->power = power; | ||
493 | |||
494 | /* XXX: on/off at boot? */ | ||
495 | pm_genpd_init(&dom->base, NULL, true); | ||
496 | |||
497 | power->pd_xlate.domains[pd_xlate_index] = &dom->base; | ||
498 | } | ||
499 | |||
500 | /** bcm2835_reset_reset - Resets a block that has a reset line in the | ||
501 | * PM block. | ||
502 | * | ||
503 | * The consumer of the reset controller must have the power domain up | ||
504 | * -- there's no reset ability with the power domain down. To reset | ||
505 | * the sub-block, we just disable its access to memory through the | ||
506 | * ASB, reset, and re-enable. | ||
507 | */ | ||
508 | static int bcm2835_reset_reset(struct reset_controller_dev *rcdev, | ||
509 | unsigned long id) | ||
510 | { | ||
511 | struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, | ||
512 | reset); | ||
513 | struct bcm2835_power_domain *pd; | ||
514 | int ret; | ||
515 | |||
516 | switch (id) { | ||
517 | case BCM2835_RESET_V3D: | ||
518 | pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D]; | ||
519 | break; | ||
520 | case BCM2835_RESET_H264: | ||
521 | pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264]; | ||
522 | break; | ||
523 | case BCM2835_RESET_ISP: | ||
524 | pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP]; | ||
525 | break; | ||
526 | default: | ||
527 | dev_err(power->dev, "Bad reset id %ld\n", id); | ||
528 | return -EINVAL; | ||
529 | } | ||
530 | |||
531 | ret = bcm2835_power_pd_power_off(&pd->base); | ||
532 | if (ret) | ||
533 | return ret; | ||
534 | |||
535 | return bcm2835_power_pd_power_on(&pd->base); | ||
536 | } | ||
537 | |||
538 | static int bcm2835_reset_status(struct reset_controller_dev *rcdev, | ||
539 | unsigned long id) | ||
540 | { | ||
541 | struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, | ||
542 | reset); | ||
543 | |||
544 | switch (id) { | ||
545 | case BCM2835_RESET_V3D: | ||
546 | return !PM_READ(PM_GRAFX & PM_V3DRSTN); | ||
547 | case BCM2835_RESET_H264: | ||
548 | return !PM_READ(PM_IMAGE & PM_H264RSTN); | ||
549 | case BCM2835_RESET_ISP: | ||
550 | return !PM_READ(PM_IMAGE & PM_ISPRSTN); | ||
551 | default: | ||
552 | return -EINVAL; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | static const struct reset_control_ops bcm2835_reset_ops = { | ||
557 | .reset = bcm2835_reset_reset, | ||
558 | .status = bcm2835_reset_status, | ||
559 | }; | ||
560 | |||
561 | static const char *const power_domain_names[] = { | ||
562 | [BCM2835_POWER_DOMAIN_GRAFX] = "grafx", | ||
563 | [BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d", | ||
564 | |||
565 | [BCM2835_POWER_DOMAIN_IMAGE] = "image", | ||
566 | [BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image", | ||
567 | [BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264", | ||
568 | [BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp", | ||
569 | |||
570 | [BCM2835_POWER_DOMAIN_USB] = "usb", | ||
571 | [BCM2835_POWER_DOMAIN_DSI0] = "dsi0", | ||
572 | [BCM2835_POWER_DOMAIN_DSI1] = "dsi1", | ||
573 | [BCM2835_POWER_DOMAIN_CAM0] = "cam0", | ||
574 | [BCM2835_POWER_DOMAIN_CAM1] = "cam1", | ||
575 | [BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx", | ||
576 | [BCM2835_POWER_DOMAIN_HDMI] = "hdmi", | ||
577 | }; | ||
578 | |||
579 | static int bcm2835_power_probe(struct platform_device *pdev) | ||
580 | { | ||
581 | struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent); | ||
582 | struct device *dev = &pdev->dev; | ||
583 | struct bcm2835_power *power; | ||
584 | static const struct { | ||
585 | int parent, child; | ||
586 | } domain_deps[] = { | ||
587 | { BCM2835_POWER_DOMAIN_GRAFX, BCM2835_POWER_DOMAIN_GRAFX_V3D }, | ||
588 | { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_PERI }, | ||
589 | { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_H264 }, | ||
590 | { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_ISP }, | ||
591 | { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_USB }, | ||
592 | { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 }, | ||
593 | { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 }, | ||
594 | }; | ||
595 | int ret, i; | ||
596 | u32 id; | ||
597 | |||
598 | power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); | ||
599 | if (!power) | ||
600 | return -ENOMEM; | ||
601 | platform_set_drvdata(pdev, power); | ||
602 | |||
603 | power->dev = dev; | ||
604 | power->base = pm->base; | ||
605 | power->asb = pm->asb; | ||
606 | |||
607 | id = ASB_READ(ASB_AXI_BRDG_ID); | ||
608 | if (id != 0x62726467 /* "BRDG" */) { | ||
609 | dev_err(dev, "ASB register ID returned 0x%08x\n", id); | ||
610 | return -ENODEV; | ||
611 | } | ||
612 | |||
613 | power->pd_xlate.domains = devm_kcalloc(dev, | ||
614 | ARRAY_SIZE(power_domain_names), | ||
615 | sizeof(*power->pd_xlate.domains), | ||
616 | GFP_KERNEL); | ||
617 | if (!power->pd_xlate.domains) | ||
618 | return -ENOMEM; | ||
619 | |||
620 | power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names); | ||
621 | |||
622 | for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) | ||
623 | bcm2835_init_power_domain(power, i, power_domain_names[i]); | ||
624 | |||
625 | for (i = 0; i < ARRAY_SIZE(domain_deps); i++) { | ||
626 | pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base, | ||
627 | &power->domains[domain_deps[i].child].base); | ||
628 | } | ||
629 | |||
630 | power->reset.owner = THIS_MODULE; | ||
631 | power->reset.nr_resets = BCM2835_RESET_COUNT; | ||
632 | power->reset.ops = &bcm2835_reset_ops; | ||
633 | power->reset.of_node = dev->parent->of_node; | ||
634 | |||
635 | ret = devm_reset_controller_register(dev, &power->reset); | ||
636 | if (ret) | ||
637 | return ret; | ||
638 | |||
639 | of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate); | ||
640 | |||
641 | dev_info(dev, "Broadcom BCM2835 power domains driver"); | ||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | static int bcm2835_power_remove(struct platform_device *pdev) | ||
646 | { | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | static struct platform_driver bcm2835_power_driver = { | ||
651 | .probe = bcm2835_power_probe, | ||
652 | .remove = bcm2835_power_remove, | ||
653 | .driver = { | ||
654 | .name = "bcm2835-power", | ||
655 | }, | ||
656 | }; | ||
657 | module_platform_driver(bcm2835_power_driver); | ||
658 | |||
659 | MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); | ||
660 | MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset"); | ||
661 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig index 8f80e8bbf29e..61f8e1433d0a 100644 --- a/drivers/soc/fsl/Kconfig +++ b/drivers/soc/fsl/Kconfig | |||
@@ -22,6 +22,7 @@ config FSL_GUTS | |||
22 | config FSL_MC_DPIO | 22 | config FSL_MC_DPIO |
23 | tristate "QorIQ DPAA2 DPIO driver" | 23 | tristate "QorIQ DPAA2 DPIO driver" |
24 | depends on FSL_MC_BUS | 24 | depends on FSL_MC_BUS |
25 | select SOC_BUS | ||
25 | help | 26 | help |
26 | Driver for the DPAA2 DPIO object. A DPIO provides queue and | 27 | Driver for the DPAA2 DPIO object. A DPIO provides queue and |
27 | buffer management facilities for software to interact with | 28 | buffer management facilities for software to interact with |
diff --git a/drivers/soc/fsl/dpio/dpio-cmd.h b/drivers/soc/fsl/dpio/dpio-cmd.h index 5814d2f395a4..e13fd3ac1939 100644 --- a/drivers/soc/fsl/dpio/dpio-cmd.h +++ b/drivers/soc/fsl/dpio/dpio-cmd.h | |||
@@ -26,6 +26,7 @@ | |||
26 | #define DPIO_CMDID_DISABLE DPIO_CMD(0x003) | 26 | #define DPIO_CMDID_DISABLE DPIO_CMD(0x003) |
27 | #define DPIO_CMDID_GET_ATTR DPIO_CMD(0x004) | 27 | #define DPIO_CMDID_GET_ATTR DPIO_CMD(0x004) |
28 | #define DPIO_CMDID_RESET DPIO_CMD(0x005) | 28 | #define DPIO_CMDID_RESET DPIO_CMD(0x005) |
29 | #define DPIO_CMDID_SET_STASHING_DEST DPIO_CMD(0x120) | ||
29 | 30 | ||
30 | struct dpio_cmd_open { | 31 | struct dpio_cmd_open { |
31 | __le32 dpio_id; | 32 | __le32 dpio_id; |
@@ -47,4 +48,8 @@ struct dpio_rsp_get_attr { | |||
47 | __le32 qbman_version; | 48 | __le32 qbman_version; |
48 | }; | 49 | }; |
49 | 50 | ||
51 | struct dpio_stashing_dest { | ||
52 | u8 sdest; | ||
53 | }; | ||
54 | |||
50 | #endif /* _FSL_DPIO_CMD_H */ | 55 | #endif /* _FSL_DPIO_CMD_H */ |
diff --git a/drivers/soc/fsl/dpio/dpio-driver.c b/drivers/soc/fsl/dpio/dpio-driver.c index 2d4af32a0dec..c0cdc8946031 100644 --- a/drivers/soc/fsl/dpio/dpio-driver.c +++ b/drivers/soc/fsl/dpio/dpio-driver.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/dma-mapping.h> | 14 | #include <linux/dma-mapping.h> |
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/sys_soc.h> | ||
17 | 18 | ||
18 | #include <linux/fsl/mc.h> | 19 | #include <linux/fsl/mc.h> |
19 | #include <soc/fsl/dpaa2-io.h> | 20 | #include <soc/fsl/dpaa2-io.h> |
@@ -32,6 +33,46 @@ struct dpio_priv { | |||
32 | 33 | ||
33 | static cpumask_var_t cpus_unused_mask; | 34 | static cpumask_var_t cpus_unused_mask; |
34 | 35 | ||
36 | static const struct soc_device_attribute ls1088a_soc[] = { | ||
37 | {.family = "QorIQ LS1088A"}, | ||
38 | { /* sentinel */ } | ||
39 | }; | ||
40 | |||
41 | static const struct soc_device_attribute ls2080a_soc[] = { | ||
42 | {.family = "QorIQ LS2080A"}, | ||
43 | { /* sentinel */ } | ||
44 | }; | ||
45 | |||
46 | static const struct soc_device_attribute ls2088a_soc[] = { | ||
47 | {.family = "QorIQ LS2088A"}, | ||
48 | { /* sentinel */ } | ||
49 | }; | ||
50 | |||
51 | static const struct soc_device_attribute lx2160a_soc[] = { | ||
52 | {.family = "QorIQ LX2160A"}, | ||
53 | { /* sentinel */ } | ||
54 | }; | ||
55 | |||
56 | static int dpaa2_dpio_get_cluster_sdest(struct fsl_mc_device *dpio_dev, int cpu) | ||
57 | { | ||
58 | int cluster_base, cluster_size; | ||
59 | |||
60 | if (soc_device_match(ls1088a_soc)) { | ||
61 | cluster_base = 2; | ||
62 | cluster_size = 4; | ||
63 | } else if (soc_device_match(ls2080a_soc) || | ||
64 | soc_device_match(ls2088a_soc) || | ||
65 | soc_device_match(lx2160a_soc)) { | ||
66 | cluster_base = 0; | ||
67 | cluster_size = 2; | ||
68 | } else { | ||
69 | dev_err(&dpio_dev->dev, "unknown SoC version\n"); | ||
70 | return -1; | ||
71 | } | ||
72 | |||
73 | return cluster_base + cpu / cluster_size; | ||
74 | } | ||
75 | |||
35 | static irqreturn_t dpio_irq_handler(int irq_num, void *arg) | 76 | static irqreturn_t dpio_irq_handler(int irq_num, void *arg) |
36 | { | 77 | { |
37 | struct device *dev = (struct device *)arg; | 78 | struct device *dev = (struct device *)arg; |
@@ -89,6 +130,7 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev) | |||
89 | int err = -ENOMEM; | 130 | int err = -ENOMEM; |
90 | struct device *dev = &dpio_dev->dev; | 131 | struct device *dev = &dpio_dev->dev; |
91 | int possible_next_cpu; | 132 | int possible_next_cpu; |
133 | int sdest; | ||
92 | 134 | ||
93 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | 135 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
94 | if (!priv) | 136 | if (!priv) |
@@ -145,6 +187,16 @@ static int dpaa2_dpio_probe(struct fsl_mc_device *dpio_dev) | |||
145 | desc.cpu = possible_next_cpu; | 187 | desc.cpu = possible_next_cpu; |
146 | cpumask_clear_cpu(possible_next_cpu, cpus_unused_mask); | 188 | cpumask_clear_cpu(possible_next_cpu, cpus_unused_mask); |
147 | 189 | ||
190 | sdest = dpaa2_dpio_get_cluster_sdest(dpio_dev, desc.cpu); | ||
191 | if (sdest >= 0) { | ||
192 | err = dpio_set_stashing_destination(dpio_dev->mc_io, 0, | ||
193 | dpio_dev->mc_handle, | ||
194 | sdest); | ||
195 | if (err) | ||
196 | dev_err(dev, "dpio_set_stashing_destination failed for cpu%d\n", | ||
197 | desc.cpu); | ||
198 | } | ||
199 | |||
148 | /* | 200 | /* |
149 | * Set the CENA regs to be the cache inhibited area of the portal to | 201 | * Set the CENA regs to be the cache inhibited area of the portal to |
150 | * avoid coherency issues if a user migrates to another core. | 202 | * avoid coherency issues if a user migrates to another core. |
@@ -220,12 +272,12 @@ static int dpaa2_dpio_remove(struct fsl_mc_device *dpio_dev) | |||
220 | 272 | ||
221 | dev = &dpio_dev->dev; | 273 | dev = &dpio_dev->dev; |
222 | priv = dev_get_drvdata(dev); | 274 | priv = dev_get_drvdata(dev); |
275 | cpu = dpaa2_io_get_cpu(priv->io); | ||
223 | 276 | ||
224 | dpaa2_io_down(priv->io); | 277 | dpaa2_io_down(priv->io); |
225 | 278 | ||
226 | dpio_teardown_irqs(dpio_dev); | 279 | dpio_teardown_irqs(dpio_dev); |
227 | 280 | ||
228 | cpu = dpaa2_io_get_cpu(priv->io); | ||
229 | cpumask_set_cpu(cpu, cpus_unused_mask); | 281 | cpumask_set_cpu(cpu, cpus_unused_mask); |
230 | 282 | ||
231 | err = dpio_open(dpio_dev->mc_io, 0, dpio_dev->obj_desc.id, | 283 | err = dpio_open(dpio_dev->mc_io, 0, dpio_dev->obj_desc.id, |
diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c index bc801934602a..b9539ef2c3cd 100644 --- a/drivers/soc/fsl/dpio/dpio-service.c +++ b/drivers/soc/fsl/dpio/dpio-service.c | |||
@@ -473,7 +473,7 @@ EXPORT_SYMBOL_GPL(dpaa2_io_service_enqueue_qd); | |||
473 | * Return 0 for success, and negative error code for failure. | 473 | * Return 0 for success, and negative error code for failure. |
474 | */ | 474 | */ |
475 | int dpaa2_io_service_release(struct dpaa2_io *d, | 475 | int dpaa2_io_service_release(struct dpaa2_io *d, |
476 | u32 bpid, | 476 | u16 bpid, |
477 | const u64 *buffers, | 477 | const u64 *buffers, |
478 | unsigned int num_buffers) | 478 | unsigned int num_buffers) |
479 | { | 479 | { |
@@ -502,7 +502,7 @@ EXPORT_SYMBOL_GPL(dpaa2_io_service_release); | |||
502 | * Eg. if the buffer pool is empty, this will return zero. | 502 | * Eg. if the buffer pool is empty, this will return zero. |
503 | */ | 503 | */ |
504 | int dpaa2_io_service_acquire(struct dpaa2_io *d, | 504 | int dpaa2_io_service_acquire(struct dpaa2_io *d, |
505 | u32 bpid, | 505 | u16 bpid, |
506 | u64 *buffers, | 506 | u64 *buffers, |
507 | unsigned int num_buffers) | 507 | unsigned int num_buffers) |
508 | { | 508 | { |
@@ -630,6 +630,7 @@ struct dpaa2_dq *dpaa2_io_store_next(struct dpaa2_io_store *s, int *is_last) | |||
630 | if (!(dpaa2_dq_flags(ret) & DPAA2_DQ_STAT_VALIDFRAME)) | 630 | if (!(dpaa2_dq_flags(ret) & DPAA2_DQ_STAT_VALIDFRAME)) |
631 | ret = NULL; | 631 | ret = NULL; |
632 | } else { | 632 | } else { |
633 | prefetch(&s->vaddr[s->idx]); | ||
633 | *is_last = 0; | 634 | *is_last = 0; |
634 | } | 635 | } |
635 | 636 | ||
diff --git a/drivers/soc/fsl/dpio/dpio.c b/drivers/soc/fsl/dpio/dpio.c index 521bc6946317..af74c597a675 100644 --- a/drivers/soc/fsl/dpio/dpio.c +++ b/drivers/soc/fsl/dpio/dpio.c | |||
@@ -166,6 +166,22 @@ int dpio_get_attributes(struct fsl_mc_io *mc_io, | |||
166 | return 0; | 166 | return 0; |
167 | } | 167 | } |
168 | 168 | ||
169 | int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, | ||
170 | u32 cmd_flags, | ||
171 | u16 token, | ||
172 | u8 sdest) | ||
173 | { | ||
174 | struct fsl_mc_command cmd = { 0 }; | ||
175 | struct dpio_stashing_dest *dpio_cmd; | ||
176 | |||
177 | cmd.header = mc_encode_cmd_header(DPIO_CMDID_SET_STASHING_DEST, | ||
178 | cmd_flags, token); | ||
179 | dpio_cmd = (struct dpio_stashing_dest *)cmd.params; | ||
180 | dpio_cmd->sdest = sdest; | ||
181 | |||
182 | return mc_send_command(mc_io, &cmd); | ||
183 | } | ||
184 | |||
169 | /** | 185 | /** |
170 | * dpio_get_api_version - Get Data Path I/O API version | 186 | * dpio_get_api_version - Get Data Path I/O API version |
171 | * @mc_io: Pointer to MC portal's DPIO object | 187 | * @mc_io: Pointer to MC portal's DPIO object |
diff --git a/drivers/soc/fsl/dpio/dpio.h b/drivers/soc/fsl/dpio/dpio.h index b2ac4ba4fb8e..da06f7258098 100644 --- a/drivers/soc/fsl/dpio/dpio.h +++ b/drivers/soc/fsl/dpio/dpio.h | |||
@@ -75,6 +75,11 @@ int dpio_get_attributes(struct fsl_mc_io *mc_io, | |||
75 | u16 token, | 75 | u16 token, |
76 | struct dpio_attr *attr); | 76 | struct dpio_attr *attr); |
77 | 77 | ||
78 | int dpio_set_stashing_destination(struct fsl_mc_io *mc_io, | ||
79 | u32 cmd_flags, | ||
80 | u16 token, | ||
81 | u8 dest); | ||
82 | |||
78 | int dpio_get_api_version(struct fsl_mc_io *mc_io, | 83 | int dpio_get_api_version(struct fsl_mc_io *mc_io, |
79 | u32 cmd_flags, | 84 | u32 cmd_flags, |
80 | u16 *major_ver, | 85 | u16 *major_ver, |
diff --git a/drivers/soc/fsl/dpio/qbman-portal.c b/drivers/soc/fsl/dpio/qbman-portal.c index 0bddb85c0ae5..d02013556a1b 100644 --- a/drivers/soc/fsl/dpio/qbman-portal.c +++ b/drivers/soc/fsl/dpio/qbman-portal.c | |||
@@ -169,9 +169,9 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) | |||
169 | 3, /* RPM: Valid bit mode, RCR in array mode */ | 169 | 3, /* RPM: Valid bit mode, RCR in array mode */ |
170 | 2, /* DCM: Discrete consumption ack mode */ | 170 | 2, /* DCM: Discrete consumption ack mode */ |
171 | 3, /* EPM: Valid bit mode, EQCR in array mode */ | 171 | 3, /* EPM: Valid bit mode, EQCR in array mode */ |
172 | 0, /* mem stashing drop enable == FALSE */ | 172 | 1, /* mem stashing drop enable == TRUE */ |
173 | 1, /* mem stashing priority == TRUE */ | 173 | 1, /* mem stashing priority == TRUE */ |
174 | 0, /* mem stashing enable == FALSE */ | 174 | 1, /* mem stashing enable == TRUE */ |
175 | 1, /* dequeue stashing priority == TRUE */ | 175 | 1, /* dequeue stashing priority == TRUE */ |
176 | 0, /* dequeue stashing enable == FALSE */ | 176 | 0, /* dequeue stashing enable == FALSE */ |
177 | 0); /* EQCR_CI stashing priority == FALSE */ | 177 | 0); /* EQCR_CI stashing priority == FALSE */ |
@@ -180,6 +180,7 @@ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) | |||
180 | reg = qbman_read_register(p, QBMAN_CINH_SWP_CFG); | 180 | reg = qbman_read_register(p, QBMAN_CINH_SWP_CFG); |
181 | if (!reg) { | 181 | if (!reg) { |
182 | pr_err("qbman: the portal is not enabled!\n"); | 182 | pr_err("qbman: the portal is not enabled!\n"); |
183 | kfree(p); | ||
183 | return NULL; | 184 | return NULL; |
184 | } | 185 | } |
185 | 186 | ||
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 302e0c8d69d9..63f6df86f9e5 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c | |||
@@ -32,6 +32,7 @@ struct fsl_soc_die_attr { | |||
32 | static struct guts *guts; | 32 | static struct guts *guts; |
33 | static struct soc_device_attribute soc_dev_attr; | 33 | static struct soc_device_attribute soc_dev_attr; |
34 | static struct soc_device *soc_dev; | 34 | static struct soc_device *soc_dev; |
35 | static struct device_node *root; | ||
35 | 36 | ||
36 | 37 | ||
37 | /* SoC die attribute definition for QorIQ platform */ | 38 | /* SoC die attribute definition for QorIQ platform */ |
@@ -114,7 +115,7 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match( | |||
114 | return NULL; | 115 | return NULL; |
115 | } | 116 | } |
116 | 117 | ||
117 | u32 fsl_guts_get_svr(void) | 118 | static u32 fsl_guts_get_svr(void) |
118 | { | 119 | { |
119 | u32 svr = 0; | 120 | u32 svr = 0; |
120 | 121 | ||
@@ -128,11 +129,10 @@ u32 fsl_guts_get_svr(void) | |||
128 | 129 | ||
129 | return svr; | 130 | return svr; |
130 | } | 131 | } |
131 | EXPORT_SYMBOL(fsl_guts_get_svr); | ||
132 | 132 | ||
133 | static int fsl_guts_probe(struct platform_device *pdev) | 133 | static int fsl_guts_probe(struct platform_device *pdev) |
134 | { | 134 | { |
135 | struct device_node *root, *np = pdev->dev.of_node; | 135 | struct device_node *np = pdev->dev.of_node; |
136 | struct device *dev = &pdev->dev; | 136 | struct device *dev = &pdev->dev; |
137 | struct resource *res; | 137 | struct resource *res; |
138 | const struct fsl_soc_die_attr *soc_die; | 138 | const struct fsl_soc_die_attr *soc_die; |
@@ -155,9 +155,8 @@ static int fsl_guts_probe(struct platform_device *pdev) | |||
155 | root = of_find_node_by_path("/"); | 155 | root = of_find_node_by_path("/"); |
156 | if (of_property_read_string(root, "model", &machine)) | 156 | if (of_property_read_string(root, "model", &machine)) |
157 | of_property_read_string_index(root, "compatible", 0, &machine); | 157 | of_property_read_string_index(root, "compatible", 0, &machine); |
158 | of_node_put(root); | ||
159 | if (machine) | 158 | if (machine) |
160 | soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL); | 159 | soc_dev_attr.machine = machine; |
161 | 160 | ||
162 | svr = fsl_guts_get_svr(); | 161 | svr = fsl_guts_get_svr(); |
163 | soc_die = fsl_soc_die_match(svr, fsl_soc_die); | 162 | soc_die = fsl_soc_die_match(svr, fsl_soc_die); |
@@ -192,6 +191,7 @@ static int fsl_guts_probe(struct platform_device *pdev) | |||
192 | static int fsl_guts_remove(struct platform_device *dev) | 191 | static int fsl_guts_remove(struct platform_device *dev) |
193 | { | 192 | { |
194 | soc_device_unregister(soc_dev); | 193 | soc_device_unregister(soc_dev); |
194 | of_node_put(root); | ||
195 | return 0; | 195 | return 0; |
196 | } | 196 | } |
197 | 197 | ||
diff --git a/drivers/soc/imx/Kconfig b/drivers/soc/imx/Kconfig index 2112d18dbb7b..d80f899d22f9 100644 --- a/drivers/soc/imx/Kconfig +++ b/drivers/soc/imx/Kconfig | |||
@@ -2,7 +2,7 @@ menu "i.MX SoC drivers" | |||
2 | 2 | ||
3 | config IMX_GPCV2_PM_DOMAINS | 3 | config IMX_GPCV2_PM_DOMAINS |
4 | bool "i.MX GPCv2 PM domains" | 4 | bool "i.MX GPCv2 PM domains" |
5 | depends on SOC_IMX7D || SOC_IMX8MQ || (COMPILE_TEST && OF) | 5 | depends on ARCH_MXC || (COMPILE_TEST && OF) |
6 | depends on PM | 6 | depends on PM |
7 | select PM_GENERIC_DOMAINS | 7 | select PM_GENERIC_DOMAINS |
8 | default y if SOC_IMX7D | 8 | default y if SOC_IMX7D |
diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 8b4f48a2ca57..176f473127b6 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de> | 8 | * Copyright 2015-2017 Pengutronix, Lucas Stach <kernel@pengutronix.de> |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/clk.h> | ||
11 | #include <linux/of_device.h> | 12 | #include <linux/of_device.h> |
12 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
13 | #include <linux/pm_domain.h> | 14 | #include <linux/pm_domain.h> |
@@ -65,6 +66,12 @@ | |||
65 | 66 | ||
66 | #define GPC_M4_PU_PDN_FLG 0x1bc | 67 | #define GPC_M4_PU_PDN_FLG 0x1bc |
67 | 68 | ||
69 | #define GPC_PU_PWRHSK 0x1fc | ||
70 | |||
71 | #define IMX8M_GPU_HSK_PWRDNREQN BIT(6) | ||
72 | #define IMX8M_VPU_HSK_PWRDNREQN BIT(5) | ||
73 | #define IMX8M_DISP_HSK_PWRDNREQN BIT(4) | ||
74 | |||
68 | /* | 75 | /* |
69 | * The PGC offset values in Reference Manual | 76 | * The PGC offset values in Reference Manual |
70 | * (Rev. 1, 01/2018 and the older ones) GPC chapter's | 77 | * (Rev. 1, 01/2018 and the older ones) GPC chapter's |
@@ -92,16 +99,21 @@ | |||
92 | 99 | ||
93 | #define GPC_PGC_CTRL_PCR BIT(0) | 100 | #define GPC_PGC_CTRL_PCR BIT(0) |
94 | 101 | ||
102 | #define GPC_CLK_MAX 6 | ||
103 | |||
95 | struct imx_pgc_domain { | 104 | struct imx_pgc_domain { |
96 | struct generic_pm_domain genpd; | 105 | struct generic_pm_domain genpd; |
97 | struct regmap *regmap; | 106 | struct regmap *regmap; |
98 | struct regulator *regulator; | 107 | struct regulator *regulator; |
108 | struct clk *clk[GPC_CLK_MAX]; | ||
109 | int num_clks; | ||
99 | 110 | ||
100 | unsigned int pgc; | 111 | unsigned int pgc; |
101 | 112 | ||
102 | const struct { | 113 | const struct { |
103 | u32 pxx; | 114 | u32 pxx; |
104 | u32 map; | 115 | u32 map; |
116 | u32 hsk; | ||
105 | } bits; | 117 | } bits; |
106 | 118 | ||
107 | const int voltage; | 119 | const int voltage; |
@@ -125,7 +137,7 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, | |||
125 | const bool enable_power_control = !on; | 137 | const bool enable_power_control = !on; |
126 | const bool has_regulator = !IS_ERR(domain->regulator); | 138 | const bool has_regulator = !IS_ERR(domain->regulator); |
127 | unsigned long deadline; | 139 | unsigned long deadline; |
128 | int ret = 0; | 140 | int i, ret = 0; |
129 | 141 | ||
130 | regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, | 142 | regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, |
131 | domain->bits.map, domain->bits.map); | 143 | domain->bits.map, domain->bits.map); |
@@ -138,10 +150,18 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, | |||
138 | } | 150 | } |
139 | } | 151 | } |
140 | 152 | ||
153 | /* Enable reset clocks for all devices in the domain */ | ||
154 | for (i = 0; i < domain->num_clks; i++) | ||
155 | clk_prepare_enable(domain->clk[i]); | ||
156 | |||
141 | if (enable_power_control) | 157 | if (enable_power_control) |
142 | regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), | 158 | regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), |
143 | GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); | 159 | GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); |
144 | 160 | ||
161 | if (domain->bits.hsk) | ||
162 | regmap_update_bits(domain->regmap, GPC_PU_PWRHSK, | ||
163 | domain->bits.hsk, on ? domain->bits.hsk : 0); | ||
164 | |||
145 | regmap_update_bits(domain->regmap, offset, | 165 | regmap_update_bits(domain->regmap, offset, |
146 | domain->bits.pxx, domain->bits.pxx); | 166 | domain->bits.pxx, domain->bits.pxx); |
147 | 167 | ||
@@ -179,6 +199,10 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, | |||
179 | regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), | 199 | regmap_update_bits(domain->regmap, GPC_PGC_CTRL(domain->pgc), |
180 | GPC_PGC_CTRL_PCR, 0); | 200 | GPC_PGC_CTRL_PCR, 0); |
181 | 201 | ||
202 | /* Disable reset clocks for all devices in the domain */ | ||
203 | for (i = 0; i < domain->num_clks; i++) | ||
204 | clk_disable_unprepare(domain->clk[i]); | ||
205 | |||
182 | if (has_regulator && !on) { | 206 | if (has_regulator && !on) { |
183 | int err; | 207 | int err; |
184 | 208 | ||
@@ -328,6 +352,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { | |||
328 | .bits = { | 352 | .bits = { |
329 | .pxx = IMX8M_GPU_SW_Pxx_REQ, | 353 | .pxx = IMX8M_GPU_SW_Pxx_REQ, |
330 | .map = IMX8M_GPU_A53_DOMAIN, | 354 | .map = IMX8M_GPU_A53_DOMAIN, |
355 | .hsk = IMX8M_GPU_HSK_PWRDNREQN, | ||
331 | }, | 356 | }, |
332 | .pgc = IMX8M_PGC_GPU, | 357 | .pgc = IMX8M_PGC_GPU, |
333 | }, | 358 | }, |
@@ -339,6 +364,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { | |||
339 | .bits = { | 364 | .bits = { |
340 | .pxx = IMX8M_VPU_SW_Pxx_REQ, | 365 | .pxx = IMX8M_VPU_SW_Pxx_REQ, |
341 | .map = IMX8M_VPU_A53_DOMAIN, | 366 | .map = IMX8M_VPU_A53_DOMAIN, |
367 | .hsk = IMX8M_VPU_HSK_PWRDNREQN, | ||
342 | }, | 368 | }, |
343 | .pgc = IMX8M_PGC_VPU, | 369 | .pgc = IMX8M_PGC_VPU, |
344 | }, | 370 | }, |
@@ -350,6 +376,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { | |||
350 | .bits = { | 376 | .bits = { |
351 | .pxx = IMX8M_DISP_SW_Pxx_REQ, | 377 | .pxx = IMX8M_DISP_SW_Pxx_REQ, |
352 | .map = IMX8M_DISP_A53_DOMAIN, | 378 | .map = IMX8M_DISP_A53_DOMAIN, |
379 | .hsk = IMX8M_DISP_HSK_PWRDNREQN, | ||
353 | }, | 380 | }, |
354 | .pgc = IMX8M_PGC_DISP, | 381 | .pgc = IMX8M_PGC_DISP, |
355 | }, | 382 | }, |
@@ -390,7 +417,7 @@ static const struct imx_pgc_domain imx8m_pgc_domains[] = { | |||
390 | 417 | ||
391 | static const struct regmap_range imx8m_yes_ranges[] = { | 418 | static const struct regmap_range imx8m_yes_ranges[] = { |
392 | regmap_reg_range(GPC_LPCR_A_CORE_BSC, | 419 | regmap_reg_range(GPC_LPCR_A_CORE_BSC, |
393 | GPC_M4_PU_PDN_FLG), | 420 | GPC_PU_PWRHSK), |
394 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI), | 421 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI), |
395 | GPC_PGC_SR(IMX8M_PGC_MIPI)), | 422 | GPC_PGC_SR(IMX8M_PGC_MIPI)), |
396 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1), | 423 | regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1), |
@@ -426,6 +453,41 @@ static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { | |||
426 | .reg_access_table = &imx8m_access_table, | 453 | .reg_access_table = &imx8m_access_table, |
427 | }; | 454 | }; |
428 | 455 | ||
456 | static int imx_pgc_get_clocks(struct imx_pgc_domain *domain) | ||
457 | { | ||
458 | int i, ret; | ||
459 | |||
460 | for (i = 0; ; i++) { | ||
461 | struct clk *clk = of_clk_get(domain->dev->of_node, i); | ||
462 | if (IS_ERR(clk)) | ||
463 | break; | ||
464 | if (i >= GPC_CLK_MAX) { | ||
465 | dev_err(domain->dev, "more than %d clocks\n", | ||
466 | GPC_CLK_MAX); | ||
467 | ret = -EINVAL; | ||
468 | goto clk_err; | ||
469 | } | ||
470 | domain->clk[i] = clk; | ||
471 | } | ||
472 | domain->num_clks = i; | ||
473 | |||
474 | return 0; | ||
475 | |||
476 | clk_err: | ||
477 | while (i--) | ||
478 | clk_put(domain->clk[i]); | ||
479 | |||
480 | return ret; | ||
481 | } | ||
482 | |||
483 | static void imx_pgc_put_clocks(struct imx_pgc_domain *domain) | ||
484 | { | ||
485 | int i; | ||
486 | |||
487 | for (i = domain->num_clks - 1; i >= 0; i--) | ||
488 | clk_put(domain->clk[i]); | ||
489 | } | ||
490 | |||
429 | static int imx_pgc_domain_probe(struct platform_device *pdev) | 491 | static int imx_pgc_domain_probe(struct platform_device *pdev) |
430 | { | 492 | { |
431 | struct imx_pgc_domain *domain = pdev->dev.platform_data; | 493 | struct imx_pgc_domain *domain = pdev->dev.platform_data; |
@@ -445,9 +507,17 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) | |||
445 | domain->voltage, domain->voltage); | 507 | domain->voltage, domain->voltage); |
446 | } | 508 | } |
447 | 509 | ||
510 | ret = imx_pgc_get_clocks(domain); | ||
511 | if (ret) { | ||
512 | if (ret != -EPROBE_DEFER) | ||
513 | dev_err(domain->dev, "Failed to get domain's clocks\n"); | ||
514 | return ret; | ||
515 | } | ||
516 | |||
448 | ret = pm_genpd_init(&domain->genpd, NULL, true); | 517 | ret = pm_genpd_init(&domain->genpd, NULL, true); |
449 | if (ret) { | 518 | if (ret) { |
450 | dev_err(domain->dev, "Failed to init power domain\n"); | 519 | dev_err(domain->dev, "Failed to init power domain\n"); |
520 | imx_pgc_put_clocks(domain); | ||
451 | return ret; | 521 | return ret; |
452 | } | 522 | } |
453 | 523 | ||
@@ -456,6 +526,7 @@ static int imx_pgc_domain_probe(struct platform_device *pdev) | |||
456 | if (ret) { | 526 | if (ret) { |
457 | dev_err(domain->dev, "Failed to add genpd provider\n"); | 527 | dev_err(domain->dev, "Failed to add genpd provider\n"); |
458 | pm_genpd_remove(&domain->genpd); | 528 | pm_genpd_remove(&domain->genpd); |
529 | imx_pgc_put_clocks(domain); | ||
459 | } | 530 | } |
460 | 531 | ||
461 | return ret; | 532 | return ret; |
@@ -467,6 +538,7 @@ static int imx_pgc_domain_remove(struct platform_device *pdev) | |||
467 | 538 | ||
468 | of_genpd_del_provider(domain->dev->of_node); | 539 | of_genpd_del_provider(domain->dev->of_node); |
469 | pm_genpd_remove(&domain->genpd); | 540 | pm_genpd_remove(&domain->genpd); |
541 | imx_pgc_put_clocks(domain); | ||
470 | 542 | ||
471 | return 0; | 543 | return 0; |
472 | } | 544 | } |
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index fcbf8a2e4080..1ee298f6bf17 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig | |||
@@ -98,6 +98,24 @@ config QCOM_RPMH | |||
98 | of hardware components aggregate requests for these resources and | 98 | of hardware components aggregate requests for these resources and |
99 | help apply the aggregated state on the resource. | 99 | help apply the aggregated state on the resource. |
100 | 100 | ||
101 | config QCOM_RPMHPD | ||
102 | bool "Qualcomm RPMh Power domain driver" | ||
103 | depends on QCOM_RPMH && QCOM_COMMAND_DB | ||
104 | help | ||
105 | QCOM RPMh Power domain driver to support power-domains with | ||
106 | performance states. The driver communicates a performance state | ||
107 | value to RPMh which then translates it into corresponding voltage | ||
108 | for the voltage rail. | ||
109 | |||
110 | config QCOM_RPMPD | ||
111 | bool "Qualcomm RPM Power domain driver" | ||
112 | depends on QCOM_SMD_RPM=y | ||
113 | help | ||
114 | QCOM RPM Power domain driver to support power-domains with | ||
115 | performance states. The driver communicates a performance state | ||
116 | value to RPM which then translates it into corresponding voltage | ||
117 | for the voltage rail. | ||
118 | |||
101 | config QCOM_SMEM | 119 | config QCOM_SMEM |
102 | tristate "Qualcomm Shared Memory Manager (SMEM)" | 120 | tristate "Qualcomm Shared Memory Manager (SMEM)" |
103 | depends on ARCH_QCOM || COMPILE_TEST | 121 | depends on ARCH_QCOM || COMPILE_TEST |
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index f25b54cd6cf8..ffe519b0cb66 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile | |||
@@ -21,3 +21,5 @@ obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o | |||
21 | obj-$(CONFIG_QCOM_APR) += apr.o | 21 | obj-$(CONFIG_QCOM_APR) += apr.o |
22 | obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o | 22 | obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o |
23 | obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o | 23 | obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o |
24 | obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o | ||
25 | obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o | ||
diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c index 2e1e4f0a5db8..86600d97c36d 100644 --- a/drivers/soc/qcom/llcc-sdm845.c +++ b/drivers/soc/qcom/llcc-sdm845.c | |||
@@ -71,6 +71,11 @@ static struct llcc_slice_config sdm845_data[] = { | |||
71 | SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), | 71 | SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), |
72 | }; | 72 | }; |
73 | 73 | ||
74 | static int sdm845_qcom_llcc_remove(struct platform_device *pdev) | ||
75 | { | ||
76 | return qcom_llcc_remove(pdev); | ||
77 | } | ||
78 | |||
74 | static int sdm845_qcom_llcc_probe(struct platform_device *pdev) | 79 | static int sdm845_qcom_llcc_probe(struct platform_device *pdev) |
75 | { | 80 | { |
76 | return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); | 81 | return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); |
@@ -87,6 +92,7 @@ static struct platform_driver sdm845_qcom_llcc_driver = { | |||
87 | .of_match_table = sdm845_qcom_llcc_of_match, | 92 | .of_match_table = sdm845_qcom_llcc_of_match, |
88 | }, | 93 | }, |
89 | .probe = sdm845_qcom_llcc_probe, | 94 | .probe = sdm845_qcom_llcc_probe, |
95 | .remove = sdm845_qcom_llcc_remove, | ||
90 | }; | 96 | }; |
91 | module_platform_driver(sdm845_qcom_llcc_driver); | 97 | module_platform_driver(sdm845_qcom_llcc_driver); |
92 | 98 | ||
diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-slice.c index 80667f7be52c..9090ea12eaf3 100644 --- a/drivers/soc/qcom/llcc-slice.c +++ b/drivers/soc/qcom/llcc-slice.c | |||
@@ -46,7 +46,7 @@ | |||
46 | 46 | ||
47 | #define BANK_OFFSET_STRIDE 0x80000 | 47 | #define BANK_OFFSET_STRIDE 0x80000 |
48 | 48 | ||
49 | static struct llcc_drv_data *drv_data; | 49 | static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; |
50 | 50 | ||
51 | static const struct regmap_config llcc_regmap_config = { | 51 | static const struct regmap_config llcc_regmap_config = { |
52 | .reg_bits = 32, | 52 | .reg_bits = 32, |
@@ -68,6 +68,9 @@ struct llcc_slice_desc *llcc_slice_getd(u32 uid) | |||
68 | struct llcc_slice_desc *desc; | 68 | struct llcc_slice_desc *desc; |
69 | u32 sz, count; | 69 | u32 sz, count; |
70 | 70 | ||
71 | if (IS_ERR(drv_data)) | ||
72 | return ERR_CAST(drv_data); | ||
73 | |||
71 | cfg = drv_data->cfg; | 74 | cfg = drv_data->cfg; |
72 | sz = drv_data->cfg_size; | 75 | sz = drv_data->cfg_size; |
73 | 76 | ||
@@ -108,6 +111,9 @@ static int llcc_update_act_ctrl(u32 sid, | |||
108 | u32 slice_status; | 111 | u32 slice_status; |
109 | int ret; | 112 | int ret; |
110 | 113 | ||
114 | if (IS_ERR(drv_data)) | ||
115 | return PTR_ERR(drv_data); | ||
116 | |||
111 | act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid); | 117 | act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid); |
112 | status_reg = LLCC_TRP_STATUSn(sid); | 118 | status_reg = LLCC_TRP_STATUSn(sid); |
113 | 119 | ||
@@ -143,6 +149,9 @@ int llcc_slice_activate(struct llcc_slice_desc *desc) | |||
143 | int ret; | 149 | int ret; |
144 | u32 act_ctrl_val; | 150 | u32 act_ctrl_val; |
145 | 151 | ||
152 | if (IS_ERR(drv_data)) | ||
153 | return PTR_ERR(drv_data); | ||
154 | |||
146 | if (IS_ERR_OR_NULL(desc)) | 155 | if (IS_ERR_OR_NULL(desc)) |
147 | return -EINVAL; | 156 | return -EINVAL; |
148 | 157 | ||
@@ -180,6 +189,9 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc) | |||
180 | u32 act_ctrl_val; | 189 | u32 act_ctrl_val; |
181 | int ret; | 190 | int ret; |
182 | 191 | ||
192 | if (IS_ERR(drv_data)) | ||
193 | return PTR_ERR(drv_data); | ||
194 | |||
183 | if (IS_ERR_OR_NULL(desc)) | 195 | if (IS_ERR_OR_NULL(desc)) |
184 | return -EINVAL; | 196 | return -EINVAL; |
185 | 197 | ||
@@ -289,46 +301,62 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev) | |||
289 | return ret; | 301 | return ret; |
290 | } | 302 | } |
291 | 303 | ||
304 | int qcom_llcc_remove(struct platform_device *pdev) | ||
305 | { | ||
306 | /* Set the global pointer to a error code to avoid referencing it */ | ||
307 | drv_data = ERR_PTR(-ENODEV); | ||
308 | return 0; | ||
309 | } | ||
310 | EXPORT_SYMBOL_GPL(qcom_llcc_remove); | ||
311 | |||
312 | static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, | ||
313 | const char *name) | ||
314 | { | ||
315 | struct resource *res; | ||
316 | void __iomem *base; | ||
317 | |||
318 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | ||
319 | if (!res) | ||
320 | return ERR_PTR(-ENODEV); | ||
321 | |||
322 | base = devm_ioremap_resource(&pdev->dev, res); | ||
323 | if (IS_ERR(base)) | ||
324 | return ERR_CAST(base); | ||
325 | |||
326 | return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); | ||
327 | } | ||
328 | |||
292 | int qcom_llcc_probe(struct platform_device *pdev, | 329 | int qcom_llcc_probe(struct platform_device *pdev, |
293 | const struct llcc_slice_config *llcc_cfg, u32 sz) | 330 | const struct llcc_slice_config *llcc_cfg, u32 sz) |
294 | { | 331 | { |
295 | u32 num_banks; | 332 | u32 num_banks; |
296 | struct device *dev = &pdev->dev; | 333 | struct device *dev = &pdev->dev; |
297 | struct resource *llcc_banks_res, *llcc_bcast_res; | ||
298 | void __iomem *llcc_banks_base, *llcc_bcast_base; | ||
299 | int ret, i; | 334 | int ret, i; |
300 | struct platform_device *llcc_edac; | 335 | struct platform_device *llcc_edac; |
301 | 336 | ||
302 | drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); | 337 | drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); |
303 | if (!drv_data) | 338 | if (!drv_data) { |
304 | return -ENOMEM; | 339 | ret = -ENOMEM; |
305 | 340 | goto err; | |
306 | llcc_banks_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | 341 | } |
307 | "llcc_base"); | 342 | |
308 | llcc_banks_base = devm_ioremap_resource(&pdev->dev, llcc_banks_res); | 343 | drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base"); |
309 | if (IS_ERR(llcc_banks_base)) | 344 | if (IS_ERR(drv_data->regmap)) { |
310 | return PTR_ERR(llcc_banks_base); | 345 | ret = PTR_ERR(drv_data->regmap); |
311 | 346 | goto err; | |
312 | drv_data->regmap = devm_regmap_init_mmio(dev, llcc_banks_base, | 347 | } |
313 | &llcc_regmap_config); | 348 | |
314 | if (IS_ERR(drv_data->regmap)) | 349 | drv_data->bcast_regmap = |
315 | return PTR_ERR(drv_data->regmap); | 350 | qcom_llcc_init_mmio(pdev, "llcc_broadcast_base"); |
316 | 351 | if (IS_ERR(drv_data->bcast_regmap)) { | |
317 | llcc_bcast_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | 352 | ret = PTR_ERR(drv_data->bcast_regmap); |
318 | "llcc_broadcast_base"); | 353 | goto err; |
319 | llcc_bcast_base = devm_ioremap_resource(&pdev->dev, llcc_bcast_res); | 354 | } |
320 | if (IS_ERR(llcc_bcast_base)) | ||
321 | return PTR_ERR(llcc_bcast_base); | ||
322 | |||
323 | drv_data->bcast_regmap = devm_regmap_init_mmio(dev, llcc_bcast_base, | ||
324 | &llcc_regmap_config); | ||
325 | if (IS_ERR(drv_data->bcast_regmap)) | ||
326 | return PTR_ERR(drv_data->bcast_regmap); | ||
327 | 355 | ||
328 | ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0, | 356 | ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0, |
329 | &num_banks); | 357 | &num_banks); |
330 | if (ret) | 358 | if (ret) |
331 | return ret; | 359 | goto err; |
332 | 360 | ||
333 | num_banks &= LLCC_LB_CNT_MASK; | 361 | num_banks &= LLCC_LB_CNT_MASK; |
334 | num_banks >>= LLCC_LB_CNT_SHIFT; | 362 | num_banks >>= LLCC_LB_CNT_SHIFT; |
@@ -340,8 +368,10 @@ int qcom_llcc_probe(struct platform_device *pdev, | |||
340 | 368 | ||
341 | drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), | 369 | drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32), |
342 | GFP_KERNEL); | 370 | GFP_KERNEL); |
343 | if (!drv_data->offsets) | 371 | if (!drv_data->offsets) { |
344 | return -ENOMEM; | 372 | ret = -ENOMEM; |
373 | goto err; | ||
374 | } | ||
345 | 375 | ||
346 | for (i = 0; i < num_banks; i++) | 376 | for (i = 0; i < num_banks; i++) |
347 | drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; | 377 | drv_data->offsets[i] = i * BANK_OFFSET_STRIDE; |
@@ -349,8 +379,10 @@ int qcom_llcc_probe(struct platform_device *pdev, | |||
349 | drv_data->bitmap = devm_kcalloc(dev, | 379 | drv_data->bitmap = devm_kcalloc(dev, |
350 | BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long), | 380 | BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long), |
351 | GFP_KERNEL); | 381 | GFP_KERNEL); |
352 | if (!drv_data->bitmap) | 382 | if (!drv_data->bitmap) { |
353 | return -ENOMEM; | 383 | ret = -ENOMEM; |
384 | goto err; | ||
385 | } | ||
354 | 386 | ||
355 | drv_data->cfg = llcc_cfg; | 387 | drv_data->cfg = llcc_cfg; |
356 | drv_data->cfg_size = sz; | 388 | drv_data->cfg_size = sz; |
@@ -359,7 +391,7 @@ int qcom_llcc_probe(struct platform_device *pdev, | |||
359 | 391 | ||
360 | ret = qcom_llcc_cfg_program(pdev); | 392 | ret = qcom_llcc_cfg_program(pdev); |
361 | if (ret) | 393 | if (ret) |
362 | return ret; | 394 | goto err; |
363 | 395 | ||
364 | drv_data->ecc_irq = platform_get_irq(pdev, 0); | 396 | drv_data->ecc_irq = platform_get_irq(pdev, 0); |
365 | if (drv_data->ecc_irq >= 0) { | 397 | if (drv_data->ecc_irq >= 0) { |
@@ -370,6 +402,9 @@ int qcom_llcc_probe(struct platform_device *pdev, | |||
370 | dev_err(dev, "Failed to register llcc edac driver\n"); | 402 | dev_err(dev, "Failed to register llcc edac driver\n"); |
371 | } | 403 | } |
372 | 404 | ||
405 | return 0; | ||
406 | err: | ||
407 | drv_data = ERR_PTR(-ENODEV); | ||
373 | return ret; | 408 | return ret; |
374 | } | 409 | } |
375 | EXPORT_SYMBOL_GPL(qcom_llcc_probe); | 410 | EXPORT_SYMBOL_GPL(qcom_llcc_probe); |
diff --git a/drivers/soc/qcom/qcom_gsbi.c b/drivers/soc/qcom/qcom_gsbi.c index 09c669e70d63..038abc377fdb 100644 --- a/drivers/soc/qcom/qcom_gsbi.c +++ b/drivers/soc/qcom/qcom_gsbi.c | |||
@@ -138,7 +138,7 @@ static int gsbi_probe(struct platform_device *pdev) | |||
138 | struct resource *res; | 138 | struct resource *res; |
139 | void __iomem *base; | 139 | void __iomem *base; |
140 | struct gsbi_info *gsbi; | 140 | struct gsbi_info *gsbi; |
141 | int i; | 141 | int i, ret; |
142 | u32 mask, gsbi_num; | 142 | u32 mask, gsbi_num; |
143 | const struct crci_config *config = NULL; | 143 | const struct crci_config *config = NULL; |
144 | 144 | ||
@@ -221,7 +221,10 @@ static int gsbi_probe(struct platform_device *pdev) | |||
221 | 221 | ||
222 | platform_set_drvdata(pdev, gsbi); | 222 | platform_set_drvdata(pdev, gsbi); |
223 | 223 | ||
224 | return of_platform_populate(node, NULL, NULL, &pdev->dev); | 224 | ret = of_platform_populate(node, NULL, NULL, &pdev->dev); |
225 | if (ret) | ||
226 | clk_disable_unprepare(gsbi->hclk); | ||
227 | return ret; | ||
225 | } | 228 | } |
226 | 229 | ||
227 | static int gsbi_remove(struct platform_device *pdev) | 230 | static int gsbi_remove(struct platform_device *pdev) |
diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index 97bb5989aa21..7200d762a951 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c | |||
@@ -45,9 +45,9 @@ static ssize_t qcom_rmtfs_mem_show(struct device *dev, | |||
45 | struct device_attribute *attr, | 45 | struct device_attribute *attr, |
46 | char *buf); | 46 | char *buf); |
47 | 47 | ||
48 | static DEVICE_ATTR(phys_addr, 0400, qcom_rmtfs_mem_show, NULL); | 48 | static DEVICE_ATTR(phys_addr, 0444, qcom_rmtfs_mem_show, NULL); |
49 | static DEVICE_ATTR(size, 0400, qcom_rmtfs_mem_show, NULL); | 49 | static DEVICE_ATTR(size, 0444, qcom_rmtfs_mem_show, NULL); |
50 | static DEVICE_ATTR(client_id, 0400, qcom_rmtfs_mem_show, NULL); | 50 | static DEVICE_ATTR(client_id, 0444, qcom_rmtfs_mem_show, NULL); |
51 | 51 | ||
52 | static ssize_t qcom_rmtfs_mem_show(struct device *dev, | 52 | static ssize_t qcom_rmtfs_mem_show(struct device *dev, |
53 | struct device_attribute *attr, | 53 | struct device_attribute *attr, |
@@ -132,6 +132,11 @@ static int qcom_rmtfs_mem_release(struct inode *inode, struct file *filp) | |||
132 | return 0; | 132 | return 0; |
133 | } | 133 | } |
134 | 134 | ||
135 | static struct class rmtfs_class = { | ||
136 | .owner = THIS_MODULE, | ||
137 | .name = "rmtfs", | ||
138 | }; | ||
139 | |||
135 | static const struct file_operations qcom_rmtfs_mem_fops = { | 140 | static const struct file_operations qcom_rmtfs_mem_fops = { |
136 | .owner = THIS_MODULE, | 141 | .owner = THIS_MODULE, |
137 | .open = qcom_rmtfs_mem_open, | 142 | .open = qcom_rmtfs_mem_open, |
@@ -199,6 +204,7 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) | |||
199 | 204 | ||
200 | dev_set_name(&rmtfs_mem->dev, "qcom_rmtfs_mem%d", client_id); | 205 | dev_set_name(&rmtfs_mem->dev, "qcom_rmtfs_mem%d", client_id); |
201 | rmtfs_mem->dev.id = client_id; | 206 | rmtfs_mem->dev.id = client_id; |
207 | rmtfs_mem->dev.class = &rmtfs_class; | ||
202 | rmtfs_mem->dev.devt = MKDEV(MAJOR(qcom_rmtfs_mem_major), client_id); | 208 | rmtfs_mem->dev.devt = MKDEV(MAJOR(qcom_rmtfs_mem_major), client_id); |
203 | 209 | ||
204 | ret = cdev_device_add(&rmtfs_mem->cdev, &rmtfs_mem->dev); | 210 | ret = cdev_device_add(&rmtfs_mem->cdev, &rmtfs_mem->dev); |
@@ -277,32 +283,42 @@ static struct platform_driver qcom_rmtfs_mem_driver = { | |||
277 | }, | 283 | }, |
278 | }; | 284 | }; |
279 | 285 | ||
280 | static int qcom_rmtfs_mem_init(void) | 286 | static int __init qcom_rmtfs_mem_init(void) |
281 | { | 287 | { |
282 | int ret; | 288 | int ret; |
283 | 289 | ||
290 | ret = class_register(&rmtfs_class); | ||
291 | if (ret) | ||
292 | return ret; | ||
293 | |||
284 | ret = alloc_chrdev_region(&qcom_rmtfs_mem_major, 0, | 294 | ret = alloc_chrdev_region(&qcom_rmtfs_mem_major, 0, |
285 | QCOM_RMTFS_MEM_DEV_MAX, "qcom_rmtfs_mem"); | 295 | QCOM_RMTFS_MEM_DEV_MAX, "qcom_rmtfs_mem"); |
286 | if (ret < 0) { | 296 | if (ret < 0) { |
287 | pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n"); | 297 | pr_err("qcom_rmtfs_mem: failed to allocate char dev region\n"); |
288 | return ret; | 298 | goto unregister_class; |
289 | } | 299 | } |
290 | 300 | ||
291 | ret = platform_driver_register(&qcom_rmtfs_mem_driver); | 301 | ret = platform_driver_register(&qcom_rmtfs_mem_driver); |
292 | if (ret < 0) { | 302 | if (ret < 0) { |
293 | pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n"); | 303 | pr_err("qcom_rmtfs_mem: failed to register rmtfs_mem driver\n"); |
294 | unregister_chrdev_region(qcom_rmtfs_mem_major, | 304 | goto unregister_chrdev; |
295 | QCOM_RMTFS_MEM_DEV_MAX); | ||
296 | } | 305 | } |
297 | 306 | ||
307 | return 0; | ||
308 | |||
309 | unregister_chrdev: | ||
310 | unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX); | ||
311 | unregister_class: | ||
312 | class_unregister(&rmtfs_class); | ||
298 | return ret; | 313 | return ret; |
299 | } | 314 | } |
300 | module_init(qcom_rmtfs_mem_init); | 315 | module_init(qcom_rmtfs_mem_init); |
301 | 316 | ||
302 | static void qcom_rmtfs_mem_exit(void) | 317 | static void __exit qcom_rmtfs_mem_exit(void) |
303 | { | 318 | { |
304 | platform_driver_unregister(&qcom_rmtfs_mem_driver); | 319 | platform_driver_unregister(&qcom_rmtfs_mem_driver); |
305 | unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX); | 320 | unregister_chrdev_region(qcom_rmtfs_mem_major, QCOM_RMTFS_MEM_DEV_MAX); |
321 | class_unregister(&rmtfs_class); | ||
306 | } | 322 | } |
307 | module_exit(qcom_rmtfs_mem_exit); | 323 | module_exit(qcom_rmtfs_mem_exit); |
308 | 324 | ||
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index c7beb6841289..035091fd44b8 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c | |||
@@ -80,6 +80,7 @@ void rpmh_tx_done(const struct tcs_request *msg, int r) | |||
80 | struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request, | 80 | struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request, |
81 | msg); | 81 | msg); |
82 | struct completion *compl = rpm_msg->completion; | 82 | struct completion *compl = rpm_msg->completion; |
83 | bool free = rpm_msg->needs_free; | ||
83 | 84 | ||
84 | rpm_msg->err = r; | 85 | rpm_msg->err = r; |
85 | 86 | ||
@@ -94,7 +95,7 @@ void rpmh_tx_done(const struct tcs_request *msg, int r) | |||
94 | complete(compl); | 95 | complete(compl); |
95 | 96 | ||
96 | exit: | 97 | exit: |
97 | if (rpm_msg->needs_free) | 98 | if (free) |
98 | kfree(rpm_msg); | 99 | kfree(rpm_msg); |
99 | } | 100 | } |
100 | 101 | ||
@@ -192,9 +193,8 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state, | |||
192 | WARN_ON(irqs_disabled()); | 193 | WARN_ON(irqs_disabled()); |
193 | ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); | 194 | ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); |
194 | } else { | 195 | } 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 */ | 196 | /* Clean up our call by spoofing tx_done */ |
197 | ret = 0; | ||
198 | rpmh_tx_done(&rpm_msg->msg, ret); | 198 | rpmh_tx_done(&rpm_msg->msg, ret); |
199 | } | 199 | } |
200 | 200 | ||
@@ -348,11 +348,12 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, | |||
348 | { | 348 | { |
349 | struct batch_cache_req *req; | 349 | struct batch_cache_req *req; |
350 | struct rpmh_request *rpm_msgs; | 350 | struct rpmh_request *rpm_msgs; |
351 | DECLARE_COMPLETION_ONSTACK(compl); | 351 | struct completion *compls; |
352 | struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); | 352 | struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); |
353 | unsigned long time_left; | 353 | unsigned long time_left; |
354 | int count = 0; | 354 | int count = 0; |
355 | int ret, i, j; | 355 | int ret, i; |
356 | void *ptr; | ||
356 | 357 | ||
357 | if (!cmd || !n) | 358 | if (!cmd || !n) |
358 | return -EINVAL; | 359 | return -EINVAL; |
@@ -362,10 +363,15 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, | |||
362 | if (!count) | 363 | if (!count) |
363 | return -EINVAL; | 364 | return -EINVAL; |
364 | 365 | ||
365 | req = kzalloc(sizeof(*req) + count * sizeof(req->rpm_msgs[0]), | 366 | ptr = kzalloc(sizeof(*req) + |
367 | count * (sizeof(req->rpm_msgs[0]) + sizeof(*compls)), | ||
366 | GFP_ATOMIC); | 368 | GFP_ATOMIC); |
367 | if (!req) | 369 | if (!ptr) |
368 | return -ENOMEM; | 370 | return -ENOMEM; |
371 | |||
372 | req = ptr; | ||
373 | compls = ptr + sizeof(*req) + count * sizeof(*rpm_msgs); | ||
374 | |||
369 | req->count = count; | 375 | req->count = count; |
370 | rpm_msgs = req->rpm_msgs; | 376 | rpm_msgs = req->rpm_msgs; |
371 | 377 | ||
@@ -380,25 +386,26 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, | |||
380 | } | 386 | } |
381 | 387 | ||
382 | for (i = 0; i < count; i++) { | 388 | for (i = 0; i < count; i++) { |
383 | rpm_msgs[i].completion = &compl; | 389 | struct completion *compl = &compls[i]; |
390 | |||
391 | init_completion(compl); | ||
392 | rpm_msgs[i].completion = compl; | ||
384 | ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msgs[i].msg); | 393 | ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msgs[i].msg); |
385 | if (ret) { | 394 | if (ret) { |
386 | pr_err("Error(%d) sending RPMH message addr=%#x\n", | 395 | pr_err("Error(%d) sending RPMH message addr=%#x\n", |
387 | ret, rpm_msgs[i].msg.cmds[0].addr); | 396 | 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; | 397 | break; |
391 | } | 398 | } |
392 | } | 399 | } |
393 | 400 | ||
394 | time_left = RPMH_TIMEOUT_MS; | 401 | time_left = RPMH_TIMEOUT_MS; |
395 | for (i = 0; i < count; i++) { | 402 | while (i--) { |
396 | time_left = wait_for_completion_timeout(&compl, time_left); | 403 | time_left = wait_for_completion_timeout(&compls[i], time_left); |
397 | if (!time_left) { | 404 | if (!time_left) { |
398 | /* | 405 | /* |
399 | * Better hope they never finish because they'll signal | 406 | * Better hope they never finish because they'll signal |
400 | * the completion on our stack and that's bad once | 407 | * the completion that we're going to free once |
401 | * we've returned from the function. | 408 | * we've returned from this function. |
402 | */ | 409 | */ |
403 | WARN_ON(1); | 410 | WARN_ON(1); |
404 | ret = -ETIMEDOUT; | 411 | ret = -ETIMEDOUT; |
@@ -407,7 +414,7 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, | |||
407 | } | 414 | } |
408 | 415 | ||
409 | exit: | 416 | exit: |
410 | kfree(req); | 417 | kfree(ptr); |
411 | 418 | ||
412 | return ret; | 419 | return ret; |
413 | } | 420 | } |
diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c new file mode 100644 index 000000000000..5741ec3fa814 --- /dev/null +++ b/drivers/soc/qcom/rpmhpd.c | |||
@@ -0,0 +1,406 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ | ||
3 | |||
4 | #include <linux/err.h> | ||
5 | #include <linux/init.h> | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/mutex.h> | ||
8 | #include <linux/pm_domain.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/of.h> | ||
11 | #include <linux/of_device.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/pm_opp.h> | ||
14 | #include <soc/qcom/cmd-db.h> | ||
15 | #include <soc/qcom/rpmh.h> | ||
16 | #include <dt-bindings/power/qcom-rpmpd.h> | ||
17 | |||
18 | #define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd) | ||
19 | |||
20 | #define RPMH_ARC_MAX_LEVELS 16 | ||
21 | |||
22 | /** | ||
23 | * struct rpmhpd - top level RPMh power domain resource data structure | ||
24 | * @dev: rpmh power domain controller device | ||
25 | * @pd: generic_pm_domain corrresponding to the power domain | ||
26 | * @peer: A peer power domain in case Active only Voting is | ||
27 | * supported | ||
28 | * @active_only: True if it represents an Active only peer | ||
29 | * @level: An array of level (vlvl) to corner (hlvl) mappings | ||
30 | * derived from cmd-db | ||
31 | * @level_count: Number of levels supported by the power domain. max | ||
32 | * being 16 (0 - 15) | ||
33 | * @enabled: true if the power domain is enabled | ||
34 | * @res_name: Resource name used for cmd-db lookup | ||
35 | * @addr: Resource address as looped up using resource name from | ||
36 | * cmd-db | ||
37 | */ | ||
38 | struct rpmhpd { | ||
39 | struct device *dev; | ||
40 | struct generic_pm_domain pd; | ||
41 | struct generic_pm_domain *parent; | ||
42 | struct rpmhpd *peer; | ||
43 | const bool active_only; | ||
44 | unsigned int corner; | ||
45 | unsigned int active_corner; | ||
46 | u32 level[RPMH_ARC_MAX_LEVELS]; | ||
47 | size_t level_count; | ||
48 | bool enabled; | ||
49 | const char *res_name; | ||
50 | u32 addr; | ||
51 | }; | ||
52 | |||
53 | struct rpmhpd_desc { | ||
54 | struct rpmhpd **rpmhpds; | ||
55 | size_t num_pds; | ||
56 | }; | ||
57 | |||
58 | static DEFINE_MUTEX(rpmhpd_lock); | ||
59 | |||
60 | /* SDM845 RPMH powerdomains */ | ||
61 | |||
62 | static struct rpmhpd sdm845_ebi = { | ||
63 | .pd = { .name = "ebi", }, | ||
64 | .res_name = "ebi.lvl", | ||
65 | }; | ||
66 | |||
67 | static struct rpmhpd sdm845_lmx = { | ||
68 | .pd = { .name = "lmx", }, | ||
69 | .res_name = "lmx.lvl", | ||
70 | }; | ||
71 | |||
72 | static struct rpmhpd sdm845_lcx = { | ||
73 | .pd = { .name = "lcx", }, | ||
74 | .res_name = "lcx.lvl", | ||
75 | }; | ||
76 | |||
77 | static struct rpmhpd sdm845_gfx = { | ||
78 | .pd = { .name = "gfx", }, | ||
79 | .res_name = "gfx.lvl", | ||
80 | }; | ||
81 | |||
82 | static struct rpmhpd sdm845_mss = { | ||
83 | .pd = { .name = "mss", }, | ||
84 | .res_name = "mss.lvl", | ||
85 | }; | ||
86 | |||
87 | static struct rpmhpd sdm845_mx_ao; | ||
88 | static struct rpmhpd sdm845_mx = { | ||
89 | .pd = { .name = "mx", }, | ||
90 | .peer = &sdm845_mx_ao, | ||
91 | .res_name = "mx.lvl", | ||
92 | }; | ||
93 | |||
94 | static struct rpmhpd sdm845_mx_ao = { | ||
95 | .pd = { .name = "mx_ao", }, | ||
96 | .peer = &sdm845_mx, | ||
97 | .res_name = "mx.lvl", | ||
98 | }; | ||
99 | |||
100 | static struct rpmhpd sdm845_cx_ao; | ||
101 | static struct rpmhpd sdm845_cx = { | ||
102 | .pd = { .name = "cx", }, | ||
103 | .peer = &sdm845_cx_ao, | ||
104 | .parent = &sdm845_mx.pd, | ||
105 | .res_name = "cx.lvl", | ||
106 | }; | ||
107 | |||
108 | static struct rpmhpd sdm845_cx_ao = { | ||
109 | .pd = { .name = "cx_ao", }, | ||
110 | .peer = &sdm845_cx, | ||
111 | .parent = &sdm845_mx_ao.pd, | ||
112 | .res_name = "cx.lvl", | ||
113 | }; | ||
114 | |||
115 | static struct rpmhpd *sdm845_rpmhpds[] = { | ||
116 | [SDM845_EBI] = &sdm845_ebi, | ||
117 | [SDM845_MX] = &sdm845_mx, | ||
118 | [SDM845_MX_AO] = &sdm845_mx_ao, | ||
119 | [SDM845_CX] = &sdm845_cx, | ||
120 | [SDM845_CX_AO] = &sdm845_cx_ao, | ||
121 | [SDM845_LMX] = &sdm845_lmx, | ||
122 | [SDM845_LCX] = &sdm845_lcx, | ||
123 | [SDM845_GFX] = &sdm845_gfx, | ||
124 | [SDM845_MSS] = &sdm845_mss, | ||
125 | }; | ||
126 | |||
127 | static const struct rpmhpd_desc sdm845_desc = { | ||
128 | .rpmhpds = sdm845_rpmhpds, | ||
129 | .num_pds = ARRAY_SIZE(sdm845_rpmhpds), | ||
130 | }; | ||
131 | |||
132 | static const struct of_device_id rpmhpd_match_table[] = { | ||
133 | { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, | ||
134 | { } | ||
135 | }; | ||
136 | |||
137 | static int rpmhpd_send_corner(struct rpmhpd *pd, int state, | ||
138 | unsigned int corner, bool sync) | ||
139 | { | ||
140 | struct tcs_cmd cmd = { | ||
141 | .addr = pd->addr, | ||
142 | .data = corner, | ||
143 | }; | ||
144 | |||
145 | /* | ||
146 | * Wait for an ack only when we are increasing the | ||
147 | * perf state of the power domain | ||
148 | */ | ||
149 | if (sync) | ||
150 | return rpmh_write(pd->dev, state, &cmd, 1); | ||
151 | else | ||
152 | return rpmh_write_async(pd->dev, state, &cmd, 1); | ||
153 | } | ||
154 | |||
155 | static void to_active_sleep(struct rpmhpd *pd, unsigned int corner, | ||
156 | unsigned int *active, unsigned int *sleep) | ||
157 | { | ||
158 | *active = corner; | ||
159 | |||
160 | if (pd->active_only) | ||
161 | *sleep = 0; | ||
162 | else | ||
163 | *sleep = *active; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * This function is used to aggregate the votes across the active only | ||
168 | * resources and its peers. The aggregated votes are sent to RPMh as | ||
169 | * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes | ||
170 | * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh | ||
171 | * on system sleep). | ||
172 | * We send ACTIVE_ONLY votes for resources without any peers. For others, | ||
173 | * which have an active only peer, all 3 votes are sent. | ||
174 | */ | ||
175 | static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) | ||
176 | { | ||
177 | int ret; | ||
178 | struct rpmhpd *peer = pd->peer; | ||
179 | unsigned int active_corner, sleep_corner; | ||
180 | unsigned int this_active_corner = 0, this_sleep_corner = 0; | ||
181 | unsigned int peer_active_corner = 0, peer_sleep_corner = 0; | ||
182 | |||
183 | to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); | ||
184 | |||
185 | if (peer && peer->enabled) | ||
186 | to_active_sleep(peer, peer->corner, &peer_active_corner, | ||
187 | &peer_sleep_corner); | ||
188 | |||
189 | active_corner = max(this_active_corner, peer_active_corner); | ||
190 | |||
191 | ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner, | ||
192 | active_corner > pd->active_corner); | ||
193 | if (ret) | ||
194 | return ret; | ||
195 | |||
196 | pd->active_corner = active_corner; | ||
197 | |||
198 | if (peer) { | ||
199 | peer->active_corner = active_corner; | ||
200 | |||
201 | ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE, | ||
202 | active_corner, false); | ||
203 | if (ret) | ||
204 | return ret; | ||
205 | |||
206 | sleep_corner = max(this_sleep_corner, peer_sleep_corner); | ||
207 | |||
208 | return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner, | ||
209 | false); | ||
210 | } | ||
211 | |||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static int rpmhpd_power_on(struct generic_pm_domain *domain) | ||
216 | { | ||
217 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | ||
218 | int ret = 0; | ||
219 | |||
220 | mutex_lock(&rpmhpd_lock); | ||
221 | |||
222 | if (pd->corner) | ||
223 | ret = rpmhpd_aggregate_corner(pd, pd->corner); | ||
224 | |||
225 | if (!ret) | ||
226 | pd->enabled = true; | ||
227 | |||
228 | mutex_unlock(&rpmhpd_lock); | ||
229 | |||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | static int rpmhpd_power_off(struct generic_pm_domain *domain) | ||
234 | { | ||
235 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | ||
236 | int ret = 0; | ||
237 | |||
238 | mutex_lock(&rpmhpd_lock); | ||
239 | |||
240 | ret = rpmhpd_aggregate_corner(pd, pd->level[0]); | ||
241 | |||
242 | if (!ret) | ||
243 | pd->enabled = false; | ||
244 | |||
245 | mutex_unlock(&rpmhpd_lock); | ||
246 | |||
247 | return ret; | ||
248 | } | ||
249 | |||
250 | static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, | ||
251 | unsigned int level) | ||
252 | { | ||
253 | struct rpmhpd *pd = domain_to_rpmhpd(domain); | ||
254 | int ret = 0, i; | ||
255 | |||
256 | mutex_lock(&rpmhpd_lock); | ||
257 | |||
258 | for (i = 0; i < pd->level_count; i++) | ||
259 | if (level <= pd->level[i]) | ||
260 | break; | ||
261 | |||
262 | /* | ||
263 | * If the level requested is more than that supported by the | ||
264 | * max corner, just set it to max anyway. | ||
265 | */ | ||
266 | if (i == pd->level_count) | ||
267 | i--; | ||
268 | |||
269 | if (pd->enabled) { | ||
270 | ret = rpmhpd_aggregate_corner(pd, i); | ||
271 | if (ret) | ||
272 | goto out; | ||
273 | } | ||
274 | |||
275 | pd->corner = i; | ||
276 | out: | ||
277 | mutex_unlock(&rpmhpd_lock); | ||
278 | |||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | static unsigned int rpmhpd_get_performance_state(struct generic_pm_domain *genpd, | ||
283 | struct dev_pm_opp *opp) | ||
284 | { | ||
285 | return dev_pm_opp_get_level(opp); | ||
286 | } | ||
287 | |||
288 | static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) | ||
289 | { | ||
290 | int i; | ||
291 | const u16 *buf; | ||
292 | |||
293 | buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count); | ||
294 | if (IS_ERR(buf)) | ||
295 | return PTR_ERR(buf); | ||
296 | |||
297 | /* 2 bytes used for each command DB aux data entry */ | ||
298 | rpmhpd->level_count >>= 1; | ||
299 | |||
300 | if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS) | ||
301 | return -EINVAL; | ||
302 | |||
303 | for (i = 0; i < rpmhpd->level_count; i++) { | ||
304 | rpmhpd->level[i] = buf[i]; | ||
305 | |||
306 | /* | ||
307 | * The AUX data may be zero padded. These 0 valued entries at | ||
308 | * the end of the map must be ignored. | ||
309 | */ | ||
310 | if (i > 0 && rpmhpd->level[i] == 0) { | ||
311 | rpmhpd->level_count = i; | ||
312 | break; | ||
313 | } | ||
314 | pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i, | ||
315 | rpmhpd->level[i]); | ||
316 | } | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int rpmhpd_probe(struct platform_device *pdev) | ||
322 | { | ||
323 | int i, ret; | ||
324 | size_t num_pds; | ||
325 | struct device *dev = &pdev->dev; | ||
326 | struct genpd_onecell_data *data; | ||
327 | struct rpmhpd **rpmhpds; | ||
328 | const struct rpmhpd_desc *desc; | ||
329 | |||
330 | desc = of_device_get_match_data(dev); | ||
331 | if (!desc) | ||
332 | return -EINVAL; | ||
333 | |||
334 | rpmhpds = desc->rpmhpds; | ||
335 | num_pds = desc->num_pds; | ||
336 | |||
337 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | ||
338 | if (!data) | ||
339 | return -ENOMEM; | ||
340 | |||
341 | data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains), | ||
342 | GFP_KERNEL); | ||
343 | if (!data->domains) | ||
344 | return -ENOMEM; | ||
345 | |||
346 | data->num_domains = num_pds; | ||
347 | |||
348 | for (i = 0; i < num_pds; i++) { | ||
349 | if (!rpmhpds[i]) { | ||
350 | dev_warn(dev, "rpmhpds[%d] is empty\n", i); | ||
351 | continue; | ||
352 | } | ||
353 | |||
354 | rpmhpds[i]->dev = dev; | ||
355 | rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); | ||
356 | if (!rpmhpds[i]->addr) { | ||
357 | dev_err(dev, "Could not find RPMh address for resource %s\n", | ||
358 | rpmhpds[i]->res_name); | ||
359 | return -ENODEV; | ||
360 | } | ||
361 | |||
362 | ret = cmd_db_read_slave_id(rpmhpds[i]->res_name); | ||
363 | if (ret != CMD_DB_HW_ARC) { | ||
364 | dev_err(dev, "RPMh slave ID mismatch\n"); | ||
365 | return -EINVAL; | ||
366 | } | ||
367 | |||
368 | ret = rpmhpd_update_level_mapping(rpmhpds[i]); | ||
369 | if (ret) | ||
370 | return ret; | ||
371 | |||
372 | rpmhpds[i]->pd.power_off = rpmhpd_power_off; | ||
373 | rpmhpds[i]->pd.power_on = rpmhpd_power_on; | ||
374 | rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state; | ||
375 | rpmhpds[i]->pd.opp_to_performance_state = rpmhpd_get_performance_state; | ||
376 | pm_genpd_init(&rpmhpds[i]->pd, NULL, true); | ||
377 | |||
378 | data->domains[i] = &rpmhpds[i]->pd; | ||
379 | } | ||
380 | |||
381 | /* Add subdomains */ | ||
382 | for (i = 0; i < num_pds; i++) { | ||
383 | if (!rpmhpds[i]) | ||
384 | continue; | ||
385 | if (rpmhpds[i]->parent) | ||
386 | pm_genpd_add_subdomain(rpmhpds[i]->parent, | ||
387 | &rpmhpds[i]->pd); | ||
388 | } | ||
389 | |||
390 | return of_genpd_add_provider_onecell(pdev->dev.of_node, data); | ||
391 | } | ||
392 | |||
393 | static struct platform_driver rpmhpd_driver = { | ||
394 | .driver = { | ||
395 | .name = "qcom-rpmhpd", | ||
396 | .of_match_table = rpmhpd_match_table, | ||
397 | .suppress_bind_attrs = true, | ||
398 | }, | ||
399 | .probe = rpmhpd_probe, | ||
400 | }; | ||
401 | |||
402 | static int __init rpmhpd_init(void) | ||
403 | { | ||
404 | return platform_driver_register(&rpmhpd_driver); | ||
405 | } | ||
406 | core_initcall(rpmhpd_init); | ||
diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c new file mode 100644 index 000000000000..005326050c23 --- /dev/null +++ b/drivers/soc/qcom/rpmpd.c | |||
@@ -0,0 +1,315 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. */ | ||
3 | |||
4 | #include <linux/err.h> | ||
5 | #include <linux/init.h> | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/mutex.h> | ||
8 | #include <linux/pm_domain.h> | ||
9 | #include <linux/of.h> | ||
10 | #include <linux/of_device.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/pm_opp.h> | ||
13 | #include <linux/soc/qcom/smd-rpm.h> | ||
14 | |||
15 | #include <dt-bindings/power/qcom-rpmpd.h> | ||
16 | |||
17 | #define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd) | ||
18 | |||
19 | /* Resource types */ | ||
20 | #define RPMPD_SMPA 0x61706d73 | ||
21 | #define RPMPD_LDOA 0x616f646c | ||
22 | |||
23 | /* Operation Keys */ | ||
24 | #define KEY_CORNER 0x6e726f63 /* corn */ | ||
25 | #define KEY_ENABLE 0x6e657773 /* swen */ | ||
26 | #define KEY_FLOOR_CORNER 0x636676 /* vfc */ | ||
27 | |||
28 | #define MAX_RPMPD_STATE 6 | ||
29 | |||
30 | #define DEFINE_RPMPD_CORNER_SMPA(_platform, _name, _active, r_id) \ | ||
31 | static struct rpmpd _platform##_##_active; \ | ||
32 | static struct rpmpd _platform##_##_name = { \ | ||
33 | .pd = { .name = #_name, }, \ | ||
34 | .peer = &_platform##_##_active, \ | ||
35 | .res_type = RPMPD_SMPA, \ | ||
36 | .res_id = r_id, \ | ||
37 | .key = KEY_CORNER, \ | ||
38 | }; \ | ||
39 | static struct rpmpd _platform##_##_active = { \ | ||
40 | .pd = { .name = #_active, }, \ | ||
41 | .peer = &_platform##_##_name, \ | ||
42 | .active_only = true, \ | ||
43 | .res_type = RPMPD_SMPA, \ | ||
44 | .res_id = r_id, \ | ||
45 | .key = KEY_CORNER, \ | ||
46 | } | ||
47 | |||
48 | #define DEFINE_RPMPD_CORNER_LDOA(_platform, _name, r_id) \ | ||
49 | static struct rpmpd _platform##_##_name = { \ | ||
50 | .pd = { .name = #_name, }, \ | ||
51 | .res_type = RPMPD_LDOA, \ | ||
52 | .res_id = r_id, \ | ||
53 | .key = KEY_CORNER, \ | ||
54 | } | ||
55 | |||
56 | #define DEFINE_RPMPD_VFC(_platform, _name, r_id, r_type) \ | ||
57 | static struct rpmpd _platform##_##_name = { \ | ||
58 | .pd = { .name = #_name, }, \ | ||
59 | .res_type = r_type, \ | ||
60 | .res_id = r_id, \ | ||
61 | .key = KEY_FLOOR_CORNER, \ | ||
62 | } | ||
63 | |||
64 | #define DEFINE_RPMPD_VFC_SMPA(_platform, _name, r_id) \ | ||
65 | DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_SMPA) | ||
66 | |||
67 | #define DEFINE_RPMPD_VFC_LDOA(_platform, _name, r_id) \ | ||
68 | DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_LDOA) | ||
69 | |||
70 | struct rpmpd_req { | ||
71 | __le32 key; | ||
72 | __le32 nbytes; | ||
73 | __le32 value; | ||
74 | }; | ||
75 | |||
76 | struct rpmpd { | ||
77 | struct generic_pm_domain pd; | ||
78 | struct rpmpd *peer; | ||
79 | const bool active_only; | ||
80 | unsigned int corner; | ||
81 | bool enabled; | ||
82 | const char *res_name; | ||
83 | const int res_type; | ||
84 | const int res_id; | ||
85 | struct qcom_smd_rpm *rpm; | ||
86 | __le32 key; | ||
87 | }; | ||
88 | |||
89 | struct rpmpd_desc { | ||
90 | struct rpmpd **rpmpds; | ||
91 | size_t num_pds; | ||
92 | }; | ||
93 | |||
94 | static DEFINE_MUTEX(rpmpd_lock); | ||
95 | |||
96 | /* msm8996 RPM Power domains */ | ||
97 | DEFINE_RPMPD_CORNER_SMPA(msm8996, vddcx, vddcx_ao, 1); | ||
98 | DEFINE_RPMPD_CORNER_SMPA(msm8996, vddmx, vddmx_ao, 2); | ||
99 | DEFINE_RPMPD_CORNER_LDOA(msm8996, vddsscx, 26); | ||
100 | |||
101 | DEFINE_RPMPD_VFC_SMPA(msm8996, vddcx_vfc, 1); | ||
102 | DEFINE_RPMPD_VFC_LDOA(msm8996, vddsscx_vfc, 26); | ||
103 | |||
104 | static struct rpmpd *msm8996_rpmpds[] = { | ||
105 | [MSM8996_VDDCX] = &msm8996_vddcx, | ||
106 | [MSM8996_VDDCX_AO] = &msm8996_vddcx_ao, | ||
107 | [MSM8996_VDDCX_VFC] = &msm8996_vddcx_vfc, | ||
108 | [MSM8996_VDDMX] = &msm8996_vddmx, | ||
109 | [MSM8996_VDDMX_AO] = &msm8996_vddmx_ao, | ||
110 | [MSM8996_VDDSSCX] = &msm8996_vddsscx, | ||
111 | [MSM8996_VDDSSCX_VFC] = &msm8996_vddsscx_vfc, | ||
112 | }; | ||
113 | |||
114 | static const struct rpmpd_desc msm8996_desc = { | ||
115 | .rpmpds = msm8996_rpmpds, | ||
116 | .num_pds = ARRAY_SIZE(msm8996_rpmpds), | ||
117 | }; | ||
118 | |||
119 | static const struct of_device_id rpmpd_match_table[] = { | ||
120 | { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, | ||
121 | { } | ||
122 | }; | ||
123 | |||
124 | static int rpmpd_send_enable(struct rpmpd *pd, bool enable) | ||
125 | { | ||
126 | struct rpmpd_req req = { | ||
127 | .key = KEY_ENABLE, | ||
128 | .nbytes = cpu_to_le32(sizeof(u32)), | ||
129 | .value = cpu_to_le32(enable), | ||
130 | }; | ||
131 | |||
132 | return qcom_rpm_smd_write(pd->rpm, QCOM_SMD_RPM_ACTIVE_STATE, | ||
133 | pd->res_type, pd->res_id, &req, sizeof(req)); | ||
134 | } | ||
135 | |||
136 | static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner) | ||
137 | { | ||
138 | struct rpmpd_req req = { | ||
139 | .key = pd->key, | ||
140 | .nbytes = cpu_to_le32(sizeof(u32)), | ||
141 | .value = cpu_to_le32(corner), | ||
142 | }; | ||
143 | |||
144 | return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id, | ||
145 | &req, sizeof(req)); | ||
146 | }; | ||
147 | |||
148 | static void to_active_sleep(struct rpmpd *pd, unsigned int corner, | ||
149 | unsigned int *active, unsigned int *sleep) | ||
150 | { | ||
151 | *active = corner; | ||
152 | |||
153 | if (pd->active_only) | ||
154 | *sleep = 0; | ||
155 | else | ||
156 | *sleep = *active; | ||
157 | } | ||
158 | |||
159 | static int rpmpd_aggregate_corner(struct rpmpd *pd) | ||
160 | { | ||
161 | int ret; | ||
162 | struct rpmpd *peer = pd->peer; | ||
163 | unsigned int active_corner, sleep_corner; | ||
164 | unsigned int this_active_corner = 0, this_sleep_corner = 0; | ||
165 | unsigned int peer_active_corner = 0, peer_sleep_corner = 0; | ||
166 | |||
167 | to_active_sleep(pd, pd->corner, &this_active_corner, &this_sleep_corner); | ||
168 | |||
169 | if (peer && peer->enabled) | ||
170 | to_active_sleep(peer, peer->corner, &peer_active_corner, | ||
171 | &peer_sleep_corner); | ||
172 | |||
173 | active_corner = max(this_active_corner, peer_active_corner); | ||
174 | |||
175 | ret = rpmpd_send_corner(pd, QCOM_SMD_RPM_ACTIVE_STATE, active_corner); | ||
176 | if (ret) | ||
177 | return ret; | ||
178 | |||
179 | sleep_corner = max(this_sleep_corner, peer_sleep_corner); | ||
180 | |||
181 | return rpmpd_send_corner(pd, QCOM_SMD_RPM_SLEEP_STATE, sleep_corner); | ||
182 | } | ||
183 | |||
184 | static int rpmpd_power_on(struct generic_pm_domain *domain) | ||
185 | { | ||
186 | int ret; | ||
187 | struct rpmpd *pd = domain_to_rpmpd(domain); | ||
188 | |||
189 | mutex_lock(&rpmpd_lock); | ||
190 | |||
191 | ret = rpmpd_send_enable(pd, true); | ||
192 | if (ret) | ||
193 | goto out; | ||
194 | |||
195 | pd->enabled = true; | ||
196 | |||
197 | if (pd->corner) | ||
198 | ret = rpmpd_aggregate_corner(pd); | ||
199 | |||
200 | out: | ||
201 | mutex_unlock(&rpmpd_lock); | ||
202 | |||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | static int rpmpd_power_off(struct generic_pm_domain *domain) | ||
207 | { | ||
208 | int ret; | ||
209 | struct rpmpd *pd = domain_to_rpmpd(domain); | ||
210 | |||
211 | mutex_lock(&rpmpd_lock); | ||
212 | |||
213 | ret = rpmpd_send_enable(pd, false); | ||
214 | if (!ret) | ||
215 | pd->enabled = false; | ||
216 | |||
217 | mutex_unlock(&rpmpd_lock); | ||
218 | |||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static int rpmpd_set_performance(struct generic_pm_domain *domain, | ||
223 | unsigned int state) | ||
224 | { | ||
225 | int ret = 0; | ||
226 | struct rpmpd *pd = domain_to_rpmpd(domain); | ||
227 | |||
228 | if (state > MAX_RPMPD_STATE) | ||
229 | goto out; | ||
230 | |||
231 | mutex_lock(&rpmpd_lock); | ||
232 | |||
233 | pd->corner = state; | ||
234 | |||
235 | if (!pd->enabled && pd->key != KEY_FLOOR_CORNER) | ||
236 | goto out; | ||
237 | |||
238 | ret = rpmpd_aggregate_corner(pd); | ||
239 | |||
240 | out: | ||
241 | mutex_unlock(&rpmpd_lock); | ||
242 | |||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | static unsigned int rpmpd_get_performance(struct generic_pm_domain *genpd, | ||
247 | struct dev_pm_opp *opp) | ||
248 | { | ||
249 | return dev_pm_opp_get_level(opp); | ||
250 | } | ||
251 | |||
252 | static int rpmpd_probe(struct platform_device *pdev) | ||
253 | { | ||
254 | int i; | ||
255 | size_t num; | ||
256 | struct genpd_onecell_data *data; | ||
257 | struct qcom_smd_rpm *rpm; | ||
258 | struct rpmpd **rpmpds; | ||
259 | const struct rpmpd_desc *desc; | ||
260 | |||
261 | rpm = dev_get_drvdata(pdev->dev.parent); | ||
262 | if (!rpm) { | ||
263 | dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); | ||
264 | return -ENODEV; | ||
265 | } | ||
266 | |||
267 | desc = of_device_get_match_data(&pdev->dev); | ||
268 | if (!desc) | ||
269 | return -EINVAL; | ||
270 | |||
271 | rpmpds = desc->rpmpds; | ||
272 | num = desc->num_pds; | ||
273 | |||
274 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
275 | if (!data) | ||
276 | return -ENOMEM; | ||
277 | |||
278 | data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains), | ||
279 | GFP_KERNEL); | ||
280 | data->num_domains = num; | ||
281 | |||
282 | for (i = 0; i < num; i++) { | ||
283 | if (!rpmpds[i]) { | ||
284 | dev_warn(&pdev->dev, "rpmpds[] with empty entry at index=%d\n", | ||
285 | i); | ||
286 | continue; | ||
287 | } | ||
288 | |||
289 | rpmpds[i]->rpm = rpm; | ||
290 | rpmpds[i]->pd.power_off = rpmpd_power_off; | ||
291 | rpmpds[i]->pd.power_on = rpmpd_power_on; | ||
292 | rpmpds[i]->pd.set_performance_state = rpmpd_set_performance; | ||
293 | rpmpds[i]->pd.opp_to_performance_state = rpmpd_get_performance; | ||
294 | pm_genpd_init(&rpmpds[i]->pd, NULL, true); | ||
295 | |||
296 | data->domains[i] = &rpmpds[i]->pd; | ||
297 | } | ||
298 | |||
299 | return of_genpd_add_provider_onecell(pdev->dev.of_node, data); | ||
300 | } | ||
301 | |||
302 | static struct platform_driver rpmpd_driver = { | ||
303 | .driver = { | ||
304 | .name = "qcom-rpmpd", | ||
305 | .of_match_table = rpmpd_match_table, | ||
306 | .suppress_bind_attrs = true, | ||
307 | }, | ||
308 | .probe = rpmpd_probe, | ||
309 | }; | ||
310 | |||
311 | static int __init rpmpd_init(void) | ||
312 | { | ||
313 | return platform_driver_register(&rpmpd_driver); | ||
314 | } | ||
315 | core_initcall(rpmpd_init); | ||
diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index b8e63724a49d..9956bb2c63f2 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c | |||
@@ -227,6 +227,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { | |||
227 | { .compatible = "qcom,rpm-msm8974" }, | 227 | { .compatible = "qcom,rpm-msm8974" }, |
228 | { .compatible = "qcom,rpm-msm8996" }, | 228 | { .compatible = "qcom,rpm-msm8996" }, |
229 | { .compatible = "qcom,rpm-msm8998" }, | 229 | { .compatible = "qcom,rpm-msm8998" }, |
230 | { .compatible = "qcom,rpm-sdm660" }, | ||
230 | { .compatible = "qcom,rpm-qcs404" }, | 231 | { .compatible = "qcom,rpm-qcs404" }, |
231 | {} | 232 | {} |
232 | }; | 233 | }; |
diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index a33ee8ef8b6b..51625703399e 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c | |||
@@ -137,13 +137,17 @@ static int tegra_fuse_probe(struct platform_device *pdev) | |||
137 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 137 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
138 | fuse->phys = res->start; | 138 | fuse->phys = res->start; |
139 | fuse->base = devm_ioremap_resource(&pdev->dev, res); | 139 | fuse->base = devm_ioremap_resource(&pdev->dev, res); |
140 | if (IS_ERR(fuse->base)) | 140 | if (IS_ERR(fuse->base)) { |
141 | return PTR_ERR(fuse->base); | 141 | err = PTR_ERR(fuse->base); |
142 | fuse->base = base; | ||
143 | return err; | ||
144 | } | ||
142 | 145 | ||
143 | fuse->clk = devm_clk_get(&pdev->dev, "fuse"); | 146 | fuse->clk = devm_clk_get(&pdev->dev, "fuse"); |
144 | if (IS_ERR(fuse->clk)) { | 147 | if (IS_ERR(fuse->clk)) { |
145 | dev_err(&pdev->dev, "failed to get FUSE clock: %ld", | 148 | dev_err(&pdev->dev, "failed to get FUSE clock: %ld", |
146 | PTR_ERR(fuse->clk)); | 149 | PTR_ERR(fuse->clk)); |
150 | fuse->base = base; | ||
147 | return PTR_ERR(fuse->clk); | 151 | return PTR_ERR(fuse->clk); |
148 | } | 152 | } |
149 | 153 | ||
@@ -152,8 +156,10 @@ static int tegra_fuse_probe(struct platform_device *pdev) | |||
152 | 156 | ||
153 | if (fuse->soc->probe) { | 157 | if (fuse->soc->probe) { |
154 | err = fuse->soc->probe(fuse); | 158 | err = fuse->soc->probe(fuse); |
155 | if (err < 0) | 159 | if (err < 0) { |
160 | fuse->base = base; | ||
156 | return err; | 161 | return err; |
162 | } | ||
157 | } | 163 | } |
158 | 164 | ||
159 | if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size, | 165 | if (tegra_fuse_create_sysfs(&pdev->dev, fuse->soc->info->size, |
diff --git a/drivers/soc/tegra/fuse/speedo-tegra210.c b/drivers/soc/tegra/fuse/speedo-tegra210.c index 5373f4c16b54..8ed35d9851f8 100644 --- a/drivers/soc/tegra/fuse/speedo-tegra210.c +++ b/drivers/soc/tegra/fuse/speedo-tegra210.c | |||
@@ -131,7 +131,7 @@ void __init tegra210_init_speedo_data(struct tegra_sku_info *sku_info) | |||
131 | 131 | ||
132 | soc_speedo[0] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_0); | 132 | soc_speedo[0] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_0); |
133 | soc_speedo[1] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_1); | 133 | soc_speedo[1] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_1); |
134 | soc_speedo[2] = tegra_fuse_read_early(FUSE_CPU_SPEEDO_2); | 134 | soc_speedo[2] = tegra_fuse_read_early(FUSE_SOC_SPEEDO_2); |
135 | 135 | ||
136 | cpu_iddq = tegra_fuse_read_early(FUSE_CPU_IDDQ) * 4; | 136 | cpu_iddq = tegra_fuse_read_early(FUSE_CPU_IDDQ) * 4; |
137 | soc_iddq = tegra_fuse_read_early(FUSE_SOC_IDDQ) * 4; | 137 | soc_iddq = tegra_fuse_read_early(FUSE_SOC_IDDQ) * 4; |
diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 7ea3280279ff..0df258518693 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c | |||
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | #define pr_fmt(fmt) "tegra-pmc: " fmt | 21 | #define pr_fmt(fmt) "tegra-pmc: " fmt |
22 | 22 | ||
23 | #include <linux/kernel.h> | 23 | #include <linux/arm-smccc.h> |
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/clk/tegra.h> | 25 | #include <linux/clk/tegra.h> |
26 | #include <linux/debugfs.h> | 26 | #include <linux/debugfs.h> |
@@ -30,16 +30,17 @@ | |||
30 | #include <linux/init.h> | 30 | #include <linux/init.h> |
31 | #include <linux/io.h> | 31 | #include <linux/io.h> |
32 | #include <linux/iopoll.h> | 32 | #include <linux/iopoll.h> |
33 | #include <linux/irq.h> | ||
34 | #include <linux/irqdomain.h> | 33 | #include <linux/irqdomain.h> |
35 | #include <linux/of.h> | 34 | #include <linux/irq.h> |
35 | #include <linux/kernel.h> | ||
36 | #include <linux/of_address.h> | 36 | #include <linux/of_address.h> |
37 | #include <linux/of_clk.h> | 37 | #include <linux/of_clk.h> |
38 | #include <linux/of.h> | ||
38 | #include <linux/of_irq.h> | 39 | #include <linux/of_irq.h> |
39 | #include <linux/of_platform.h> | 40 | #include <linux/of_platform.h> |
40 | #include <linux/pinctrl/pinctrl.h> | ||
41 | #include <linux/pinctrl/pinconf.h> | ||
42 | #include <linux/pinctrl/pinconf-generic.h> | 41 | #include <linux/pinctrl/pinconf-generic.h> |
42 | #include <linux/pinctrl/pinconf.h> | ||
43 | #include <linux/pinctrl/pinctrl.h> | ||
43 | #include <linux/platform_device.h> | 44 | #include <linux/platform_device.h> |
44 | #include <linux/pm_domain.h> | 45 | #include <linux/pm_domain.h> |
45 | #include <linux/reboot.h> | 46 | #include <linux/reboot.h> |
@@ -145,6 +146,11 @@ | |||
145 | #define WAKE_AOWAKE_CTRL 0x4f4 | 146 | #define WAKE_AOWAKE_CTRL 0x4f4 |
146 | #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) | 147 | #define WAKE_AOWAKE_CTRL_INTR_POLARITY BIT(0) |
147 | 148 | ||
149 | /* for secure PMC */ | ||
150 | #define TEGRA_SMC_PMC 0xc2fffe00 | ||
151 | #define TEGRA_SMC_PMC_READ 0xaa | ||
152 | #define TEGRA_SMC_PMC_WRITE 0xbb | ||
153 | |||
148 | struct tegra_powergate { | 154 | struct tegra_powergate { |
149 | struct generic_pm_domain genpd; | 155 | struct generic_pm_domain genpd; |
150 | struct tegra_pmc *pmc; | 156 | struct tegra_pmc *pmc; |
@@ -216,6 +222,7 @@ struct tegra_pmc_soc { | |||
216 | bool has_gpu_clamps; | 222 | bool has_gpu_clamps; |
217 | bool needs_mbist_war; | 223 | bool needs_mbist_war; |
218 | bool has_impl_33v_pwr; | 224 | bool has_impl_33v_pwr; |
225 | bool maybe_tz_only; | ||
219 | 226 | ||
220 | const struct tegra_io_pad_soc *io_pads; | 227 | const struct tegra_io_pad_soc *io_pads; |
221 | unsigned int num_io_pads; | 228 | unsigned int num_io_pads; |
@@ -273,8 +280,12 @@ static const char * const tegra30_reset_sources[] = { | |||
273 | * struct tegra_pmc - NVIDIA Tegra PMC | 280 | * struct tegra_pmc - NVIDIA Tegra PMC |
274 | * @dev: pointer to PMC device structure | 281 | * @dev: pointer to PMC device structure |
275 | * @base: pointer to I/O remapped register region | 282 | * @base: pointer to I/O remapped register region |
283 | * @wake: pointer to I/O remapped region for WAKE registers | ||
284 | * @aotag: pointer to I/O remapped region for AOTAG registers | ||
285 | * @scratch: pointer to I/O remapped region for scratch registers | ||
276 | * @clk: pointer to pclk clock | 286 | * @clk: pointer to pclk clock |
277 | * @soc: pointer to SoC data structure | 287 | * @soc: pointer to SoC data structure |
288 | * @tz_only: flag specifying if the PMC can only be accessed via TrustZone | ||
278 | * @debugfs: pointer to debugfs entry | 289 | * @debugfs: pointer to debugfs entry |
279 | * @rate: currently configured rate of pclk | 290 | * @rate: currently configured rate of pclk |
280 | * @suspend_mode: lowest suspend mode available | 291 | * @suspend_mode: lowest suspend mode available |
@@ -291,6 +302,9 @@ static const char * const tegra30_reset_sources[] = { | |||
291 | * @lp0_vec_size: size of the LP0 warm boot code | 302 | * @lp0_vec_size: size of the LP0 warm boot code |
292 | * @powergates_available: Bitmap of available power gates | 303 | * @powergates_available: Bitmap of available power gates |
293 | * @powergates_lock: mutex for power gate register access | 304 | * @powergates_lock: mutex for power gate register access |
305 | * @pctl_dev: pin controller exposed by the PMC | ||
306 | * @domain: IRQ domain provided by the PMC | ||
307 | * @irq: chip implementation for the IRQ domain | ||
294 | */ | 308 | */ |
295 | struct tegra_pmc { | 309 | struct tegra_pmc { |
296 | struct device *dev; | 310 | struct device *dev; |
@@ -302,6 +316,7 @@ struct tegra_pmc { | |||
302 | struct dentry *debugfs; | 316 | struct dentry *debugfs; |
303 | 317 | ||
304 | const struct tegra_pmc_soc *soc; | 318 | const struct tegra_pmc_soc *soc; |
319 | bool tz_only; | ||
305 | 320 | ||
306 | unsigned long rate; | 321 | unsigned long rate; |
307 | 322 | ||
@@ -338,30 +353,85 @@ to_powergate(struct generic_pm_domain *domain) | |||
338 | return container_of(domain, struct tegra_powergate, genpd); | 353 | return container_of(domain, struct tegra_powergate, genpd); |
339 | } | 354 | } |
340 | 355 | ||
341 | static u32 tegra_pmc_readl(unsigned long offset) | 356 | static u32 tegra_pmc_readl(struct tegra_pmc *pmc, unsigned long offset) |
342 | { | 357 | { |
358 | struct arm_smccc_res res; | ||
359 | |||
360 | if (pmc->tz_only) { | ||
361 | arm_smccc_smc(TEGRA_SMC_PMC, TEGRA_SMC_PMC_READ, offset, 0, 0, | ||
362 | 0, 0, 0, &res); | ||
363 | if (res.a0) { | ||
364 | if (pmc->dev) | ||
365 | dev_warn(pmc->dev, "%s(): SMC failed: %lu\n", | ||
366 | __func__, res.a0); | ||
367 | else | ||
368 | pr_warn("%s(): SMC failed: %lu\n", __func__, | ||
369 | res.a0); | ||
370 | } | ||
371 | |||
372 | return res.a1; | ||
373 | } | ||
374 | |||
343 | return readl(pmc->base + offset); | 375 | return readl(pmc->base + offset); |
344 | } | 376 | } |
345 | 377 | ||
346 | static void tegra_pmc_writel(u32 value, unsigned long offset) | 378 | static void tegra_pmc_writel(struct tegra_pmc *pmc, u32 value, |
379 | unsigned long offset) | ||
347 | { | 380 | { |
348 | writel(value, pmc->base + offset); | 381 | struct arm_smccc_res res; |
382 | |||
383 | if (pmc->tz_only) { | ||
384 | arm_smccc_smc(TEGRA_SMC_PMC, TEGRA_SMC_PMC_WRITE, offset, | ||
385 | value, 0, 0, 0, 0, &res); | ||
386 | if (res.a0) { | ||
387 | if (pmc->dev) | ||
388 | dev_warn(pmc->dev, "%s(): SMC failed: %lu\n", | ||
389 | __func__, res.a0); | ||
390 | else | ||
391 | pr_warn("%s(): SMC failed: %lu\n", __func__, | ||
392 | res.a0); | ||
393 | } | ||
394 | } else { | ||
395 | writel(value, pmc->base + offset); | ||
396 | } | ||
349 | } | 397 | } |
350 | 398 | ||
399 | static u32 tegra_pmc_scratch_readl(struct tegra_pmc *pmc, unsigned long offset) | ||
400 | { | ||
401 | if (pmc->tz_only) | ||
402 | return tegra_pmc_readl(pmc, offset); | ||
403 | |||
404 | return readl(pmc->scratch + offset); | ||
405 | } | ||
406 | |||
407 | static void tegra_pmc_scratch_writel(struct tegra_pmc *pmc, u32 value, | ||
408 | unsigned long offset) | ||
409 | { | ||
410 | if (pmc->tz_only) | ||
411 | tegra_pmc_writel(pmc, value, offset); | ||
412 | else | ||
413 | writel(value, pmc->scratch + offset); | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * TODO Figure out a way to call this with the struct tegra_pmc * passed in. | ||
418 | * This currently doesn't work because readx_poll_timeout() can only operate | ||
419 | * on functions that take a single argument. | ||
420 | */ | ||
351 | static inline bool tegra_powergate_state(int id) | 421 | static inline bool tegra_powergate_state(int id) |
352 | { | 422 | { |
353 | if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps) | 423 | if (id == TEGRA_POWERGATE_3D && pmc->soc->has_gpu_clamps) |
354 | return (tegra_pmc_readl(GPU_RG_CNTRL) & 0x1) == 0; | 424 | return (tegra_pmc_readl(pmc, GPU_RG_CNTRL) & 0x1) == 0; |
355 | else | 425 | else |
356 | return (tegra_pmc_readl(PWRGATE_STATUS) & BIT(id)) != 0; | 426 | return (tegra_pmc_readl(pmc, PWRGATE_STATUS) & BIT(id)) != 0; |
357 | } | 427 | } |
358 | 428 | ||
359 | static inline bool tegra_powergate_is_valid(int id) | 429 | static inline bool tegra_powergate_is_valid(struct tegra_pmc *pmc, int id) |
360 | { | 430 | { |
361 | return (pmc->soc && pmc->soc->powergates[id]); | 431 | return (pmc->soc && pmc->soc->powergates[id]); |
362 | } | 432 | } |
363 | 433 | ||
364 | static inline bool tegra_powergate_is_available(int id) | 434 | static inline bool tegra_powergate_is_available(struct tegra_pmc *pmc, int id) |
365 | { | 435 | { |
366 | return test_bit(id, pmc->powergates_available); | 436 | return test_bit(id, pmc->powergates_available); |
367 | } | 437 | } |
@@ -374,7 +444,7 @@ static int tegra_powergate_lookup(struct tegra_pmc *pmc, const char *name) | |||
374 | return -EINVAL; | 444 | return -EINVAL; |
375 | 445 | ||
376 | for (i = 0; i < pmc->soc->num_powergates; i++) { | 446 | for (i = 0; i < pmc->soc->num_powergates; i++) { |
377 | if (!tegra_powergate_is_valid(i)) | 447 | if (!tegra_powergate_is_valid(pmc, i)) |
378 | continue; | 448 | continue; |
379 | 449 | ||
380 | if (!strcmp(name, pmc->soc->powergates[i])) | 450 | if (!strcmp(name, pmc->soc->powergates[i])) |
@@ -386,10 +456,12 @@ static int tegra_powergate_lookup(struct tegra_pmc *pmc, const char *name) | |||
386 | 456 | ||
387 | /** | 457 | /** |
388 | * tegra_powergate_set() - set the state of a partition | 458 | * tegra_powergate_set() - set the state of a partition |
459 | * @pmc: power management controller | ||
389 | * @id: partition ID | 460 | * @id: partition ID |
390 | * @new_state: new state of the partition | 461 | * @new_state: new state of the partition |
391 | */ | 462 | */ |
392 | static int tegra_powergate_set(unsigned int id, bool new_state) | 463 | static int tegra_powergate_set(struct tegra_pmc *pmc, unsigned int id, |
464 | bool new_state) | ||
393 | { | 465 | { |
394 | bool status; | 466 | bool status; |
395 | int err; | 467 | int err; |
@@ -404,7 +476,7 @@ static int tegra_powergate_set(unsigned int id, bool new_state) | |||
404 | return 0; | 476 | return 0; |
405 | } | 477 | } |
406 | 478 | ||
407 | tegra_pmc_writel(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); | 479 | tegra_pmc_writel(pmc, PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); |
408 | 480 | ||
409 | err = readx_poll_timeout(tegra_powergate_state, id, status, | 481 | err = readx_poll_timeout(tegra_powergate_state, id, status, |
410 | status == new_state, 10, 100000); | 482 | status == new_state, 10, 100000); |
@@ -414,7 +486,8 @@ static int tegra_powergate_set(unsigned int id, bool new_state) | |||
414 | return err; | 486 | return err; |
415 | } | 487 | } |
416 | 488 | ||
417 | static int __tegra_powergate_remove_clamping(unsigned int id) | 489 | static int __tegra_powergate_remove_clamping(struct tegra_pmc *pmc, |
490 | unsigned int id) | ||
418 | { | 491 | { |
419 | u32 mask; | 492 | u32 mask; |
420 | 493 | ||
@@ -426,7 +499,7 @@ static int __tegra_powergate_remove_clamping(unsigned int id) | |||
426 | */ | 499 | */ |
427 | if (id == TEGRA_POWERGATE_3D) { | 500 | if (id == TEGRA_POWERGATE_3D) { |
428 | if (pmc->soc->has_gpu_clamps) { | 501 | if (pmc->soc->has_gpu_clamps) { |
429 | tegra_pmc_writel(0, GPU_RG_CNTRL); | 502 | tegra_pmc_writel(pmc, 0, GPU_RG_CNTRL); |
430 | goto out; | 503 | goto out; |
431 | } | 504 | } |
432 | } | 505 | } |
@@ -442,7 +515,7 @@ static int __tegra_powergate_remove_clamping(unsigned int id) | |||
442 | else | 515 | else |
443 | mask = (1 << id); | 516 | mask = (1 << id); |
444 | 517 | ||
445 | tegra_pmc_writel(mask, REMOVE_CLAMPING); | 518 | tegra_pmc_writel(pmc, mask, REMOVE_CLAMPING); |
446 | 519 | ||
447 | out: | 520 | out: |
448 | mutex_unlock(&pmc->powergates_lock); | 521 | mutex_unlock(&pmc->powergates_lock); |
@@ -494,7 +567,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg, | |||
494 | 567 | ||
495 | usleep_range(10, 20); | 568 | usleep_range(10, 20); |
496 | 569 | ||
497 | err = tegra_powergate_set(pg->id, true); | 570 | err = tegra_powergate_set(pg->pmc, pg->id, true); |
498 | if (err < 0) | 571 | if (err < 0) |
499 | return err; | 572 | return err; |
500 | 573 | ||
@@ -506,7 +579,7 @@ static int tegra_powergate_power_up(struct tegra_powergate *pg, | |||
506 | 579 | ||
507 | usleep_range(10, 20); | 580 | usleep_range(10, 20); |
508 | 581 | ||
509 | err = __tegra_powergate_remove_clamping(pg->id); | 582 | err = __tegra_powergate_remove_clamping(pg->pmc, pg->id); |
510 | if (err) | 583 | if (err) |
511 | goto disable_clks; | 584 | goto disable_clks; |
512 | 585 | ||
@@ -533,7 +606,7 @@ disable_clks: | |||
533 | usleep_range(10, 20); | 606 | usleep_range(10, 20); |
534 | 607 | ||
535 | powergate_off: | 608 | powergate_off: |
536 | tegra_powergate_set(pg->id, false); | 609 | tegra_powergate_set(pg->pmc, pg->id, false); |
537 | 610 | ||
538 | return err; | 611 | return err; |
539 | } | 612 | } |
@@ -558,7 +631,7 @@ static int tegra_powergate_power_down(struct tegra_powergate *pg) | |||
558 | 631 | ||
559 | usleep_range(10, 20); | 632 | usleep_range(10, 20); |
560 | 633 | ||
561 | err = tegra_powergate_set(pg->id, false); | 634 | err = tegra_powergate_set(pg->pmc, pg->id, false); |
562 | if (err) | 635 | if (err) |
563 | goto assert_resets; | 636 | goto assert_resets; |
564 | 637 | ||
@@ -579,12 +652,13 @@ disable_clks: | |||
579 | static int tegra_genpd_power_on(struct generic_pm_domain *domain) | 652 | static int tegra_genpd_power_on(struct generic_pm_domain *domain) |
580 | { | 653 | { |
581 | struct tegra_powergate *pg = to_powergate(domain); | 654 | struct tegra_powergate *pg = to_powergate(domain); |
655 | struct device *dev = pg->pmc->dev; | ||
582 | int err; | 656 | int err; |
583 | 657 | ||
584 | err = tegra_powergate_power_up(pg, true); | 658 | err = tegra_powergate_power_up(pg, true); |
585 | if (err) | 659 | if (err) |
586 | pr_err("failed to turn on PM domain %s: %d\n", pg->genpd.name, | 660 | dev_err(dev, "failed to turn on PM domain %s: %d\n", |
587 | err); | 661 | pg->genpd.name, err); |
588 | 662 | ||
589 | return err; | 663 | return err; |
590 | } | 664 | } |
@@ -592,12 +666,13 @@ static int tegra_genpd_power_on(struct generic_pm_domain *domain) | |||
592 | static int tegra_genpd_power_off(struct generic_pm_domain *domain) | 666 | static int tegra_genpd_power_off(struct generic_pm_domain *domain) |
593 | { | 667 | { |
594 | struct tegra_powergate *pg = to_powergate(domain); | 668 | struct tegra_powergate *pg = to_powergate(domain); |
669 | struct device *dev = pg->pmc->dev; | ||
595 | int err; | 670 | int err; |
596 | 671 | ||
597 | err = tegra_powergate_power_down(pg); | 672 | err = tegra_powergate_power_down(pg); |
598 | if (err) | 673 | if (err) |
599 | pr_err("failed to turn off PM domain %s: %d\n", | 674 | dev_err(dev, "failed to turn off PM domain %s: %d\n", |
600 | pg->genpd.name, err); | 675 | pg->genpd.name, err); |
601 | 676 | ||
602 | return err; | 677 | return err; |
603 | } | 678 | } |
@@ -608,10 +683,10 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain) | |||
608 | */ | 683 | */ |
609 | int tegra_powergate_power_on(unsigned int id) | 684 | int tegra_powergate_power_on(unsigned int id) |
610 | { | 685 | { |
611 | if (!tegra_powergate_is_available(id)) | 686 | if (!tegra_powergate_is_available(pmc, id)) |
612 | return -EINVAL; | 687 | return -EINVAL; |
613 | 688 | ||
614 | return tegra_powergate_set(id, true); | 689 | return tegra_powergate_set(pmc, id, true); |
615 | } | 690 | } |
616 | 691 | ||
617 | /** | 692 | /** |
@@ -620,20 +695,21 @@ int tegra_powergate_power_on(unsigned int id) | |||
620 | */ | 695 | */ |
621 | int tegra_powergate_power_off(unsigned int id) | 696 | int tegra_powergate_power_off(unsigned int id) |
622 | { | 697 | { |
623 | if (!tegra_powergate_is_available(id)) | 698 | if (!tegra_powergate_is_available(pmc, id)) |
624 | return -EINVAL; | 699 | return -EINVAL; |
625 | 700 | ||
626 | return tegra_powergate_set(id, false); | 701 | return tegra_powergate_set(pmc, id, false); |
627 | } | 702 | } |
628 | EXPORT_SYMBOL(tegra_powergate_power_off); | 703 | EXPORT_SYMBOL(tegra_powergate_power_off); |
629 | 704 | ||
630 | /** | 705 | /** |
631 | * tegra_powergate_is_powered() - check if partition is powered | 706 | * tegra_powergate_is_powered() - check if partition is powered |
707 | * @pmc: power management controller | ||
632 | * @id: partition ID | 708 | * @id: partition ID |
633 | */ | 709 | */ |
634 | int tegra_powergate_is_powered(unsigned int id) | 710 | static int tegra_powergate_is_powered(struct tegra_pmc *pmc, unsigned int id) |
635 | { | 711 | { |
636 | if (!tegra_powergate_is_valid(id)) | 712 | if (!tegra_powergate_is_valid(pmc, id)) |
637 | return -EINVAL; | 713 | return -EINVAL; |
638 | 714 | ||
639 | return tegra_powergate_state(id); | 715 | return tegra_powergate_state(id); |
@@ -645,10 +721,10 @@ int tegra_powergate_is_powered(unsigned int id) | |||
645 | */ | 721 | */ |
646 | int tegra_powergate_remove_clamping(unsigned int id) | 722 | int tegra_powergate_remove_clamping(unsigned int id) |
647 | { | 723 | { |
648 | if (!tegra_powergate_is_available(id)) | 724 | if (!tegra_powergate_is_available(pmc, id)) |
649 | return -EINVAL; | 725 | return -EINVAL; |
650 | 726 | ||
651 | return __tegra_powergate_remove_clamping(id); | 727 | return __tegra_powergate_remove_clamping(pmc, id); |
652 | } | 728 | } |
653 | EXPORT_SYMBOL(tegra_powergate_remove_clamping); | 729 | EXPORT_SYMBOL(tegra_powergate_remove_clamping); |
654 | 730 | ||
@@ -666,7 +742,7 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, | |||
666 | struct tegra_powergate *pg; | 742 | struct tegra_powergate *pg; |
667 | int err; | 743 | int err; |
668 | 744 | ||
669 | if (!tegra_powergate_is_available(id)) | 745 | if (!tegra_powergate_is_available(pmc, id)) |
670 | return -EINVAL; | 746 | return -EINVAL; |
671 | 747 | ||
672 | pg = kzalloc(sizeof(*pg), GFP_KERNEL); | 748 | pg = kzalloc(sizeof(*pg), GFP_KERNEL); |
@@ -681,7 +757,8 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, | |||
681 | 757 | ||
682 | err = tegra_powergate_power_up(pg, false); | 758 | err = tegra_powergate_power_up(pg, false); |
683 | if (err) | 759 | if (err) |
684 | pr_err("failed to turn on partition %d: %d\n", id, err); | 760 | dev_err(pmc->dev, "failed to turn on partition %d: %d\n", id, |
761 | err); | ||
685 | 762 | ||
686 | kfree(pg); | 763 | kfree(pg); |
687 | 764 | ||
@@ -691,12 +768,14 @@ EXPORT_SYMBOL(tegra_powergate_sequence_power_up); | |||
691 | 768 | ||
692 | /** | 769 | /** |
693 | * tegra_get_cpu_powergate_id() - convert from CPU ID to partition ID | 770 | * tegra_get_cpu_powergate_id() - convert from CPU ID to partition ID |
771 | * @pmc: power management controller | ||
694 | * @cpuid: CPU partition ID | 772 | * @cpuid: CPU partition ID |
695 | * | 773 | * |
696 | * Returns the partition ID corresponding to the CPU partition ID or a | 774 | * Returns the partition ID corresponding to the CPU partition ID or a |
697 | * negative error code on failure. | 775 | * negative error code on failure. |
698 | */ | 776 | */ |
699 | static int tegra_get_cpu_powergate_id(unsigned int cpuid) | 777 | static int tegra_get_cpu_powergate_id(struct tegra_pmc *pmc, |
778 | unsigned int cpuid) | ||
700 | { | 779 | { |
701 | if (pmc->soc && cpuid < pmc->soc->num_cpu_powergates) | 780 | if (pmc->soc && cpuid < pmc->soc->num_cpu_powergates) |
702 | return pmc->soc->cpu_powergates[cpuid]; | 781 | return pmc->soc->cpu_powergates[cpuid]; |
@@ -712,11 +791,11 @@ bool tegra_pmc_cpu_is_powered(unsigned int cpuid) | |||
712 | { | 791 | { |
713 | int id; | 792 | int id; |
714 | 793 | ||
715 | id = tegra_get_cpu_powergate_id(cpuid); | 794 | id = tegra_get_cpu_powergate_id(pmc, cpuid); |
716 | if (id < 0) | 795 | if (id < 0) |
717 | return false; | 796 | return false; |
718 | 797 | ||
719 | return tegra_powergate_is_powered(id); | 798 | return tegra_powergate_is_powered(pmc, id); |
720 | } | 799 | } |
721 | 800 | ||
722 | /** | 801 | /** |
@@ -727,11 +806,11 @@ int tegra_pmc_cpu_power_on(unsigned int cpuid) | |||
727 | { | 806 | { |
728 | int id; | 807 | int id; |
729 | 808 | ||
730 | id = tegra_get_cpu_powergate_id(cpuid); | 809 | id = tegra_get_cpu_powergate_id(pmc, cpuid); |
731 | if (id < 0) | 810 | if (id < 0) |
732 | return id; | 811 | return id; |
733 | 812 | ||
734 | return tegra_powergate_set(id, true); | 813 | return tegra_powergate_set(pmc, id, true); |
735 | } | 814 | } |
736 | 815 | ||
737 | /** | 816 | /** |
@@ -742,7 +821,7 @@ int tegra_pmc_cpu_remove_clamping(unsigned int cpuid) | |||
742 | { | 821 | { |
743 | int id; | 822 | int id; |
744 | 823 | ||
745 | id = tegra_get_cpu_powergate_id(cpuid); | 824 | id = tegra_get_cpu_powergate_id(pmc, cpuid); |
746 | if (id < 0) | 825 | if (id < 0) |
747 | return id; | 826 | return id; |
748 | 827 | ||
@@ -755,7 +834,7 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, | |||
755 | const char *cmd = data; | 834 | const char *cmd = data; |
756 | u32 value; | 835 | u32 value; |
757 | 836 | ||
758 | value = readl(pmc->scratch + pmc->soc->regs->scratch0); | 837 | value = tegra_pmc_scratch_readl(pmc, pmc->soc->regs->scratch0); |
759 | value &= ~PMC_SCRATCH0_MODE_MASK; | 838 | value &= ~PMC_SCRATCH0_MODE_MASK; |
760 | 839 | ||
761 | if (cmd) { | 840 | if (cmd) { |
@@ -769,12 +848,12 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, | |||
769 | value |= PMC_SCRATCH0_MODE_RCM; | 848 | value |= PMC_SCRATCH0_MODE_RCM; |
770 | } | 849 | } |
771 | 850 | ||
772 | writel(value, pmc->scratch + pmc->soc->regs->scratch0); | 851 | tegra_pmc_scratch_writel(pmc, value, pmc->soc->regs->scratch0); |
773 | 852 | ||
774 | /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */ | 853 | /* reset everything but PMC_SCRATCH0 and PMC_RST_STATUS */ |
775 | value = tegra_pmc_readl(PMC_CNTRL); | 854 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
776 | value |= PMC_CNTRL_MAIN_RST; | 855 | value |= PMC_CNTRL_MAIN_RST; |
777 | tegra_pmc_writel(value, PMC_CNTRL); | 856 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
778 | 857 | ||
779 | return NOTIFY_DONE; | 858 | return NOTIFY_DONE; |
780 | } | 859 | } |
@@ -793,7 +872,7 @@ static int powergate_show(struct seq_file *s, void *data) | |||
793 | seq_printf(s, "------------------\n"); | 872 | seq_printf(s, "------------------\n"); |
794 | 873 | ||
795 | for (i = 0; i < pmc->soc->num_powergates; i++) { | 874 | for (i = 0; i < pmc->soc->num_powergates; i++) { |
796 | status = tegra_powergate_is_powered(i); | 875 | status = tegra_powergate_is_powered(pmc, i); |
797 | if (status < 0) | 876 | if (status < 0) |
798 | continue; | 877 | continue; |
799 | 878 | ||
@@ -855,12 +934,13 @@ err: | |||
855 | static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, | 934 | static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, |
856 | struct device_node *np, bool off) | 935 | struct device_node *np, bool off) |
857 | { | 936 | { |
937 | struct device *dev = pg->pmc->dev; | ||
858 | int err; | 938 | int err; |
859 | 939 | ||
860 | pg->reset = of_reset_control_array_get_exclusive(np); | 940 | pg->reset = of_reset_control_array_get_exclusive(np); |
861 | if (IS_ERR(pg->reset)) { | 941 | if (IS_ERR(pg->reset)) { |
862 | err = PTR_ERR(pg->reset); | 942 | err = PTR_ERR(pg->reset); |
863 | pr_err("failed to get device resets: %d\n", err); | 943 | dev_err(dev, "failed to get device resets: %d\n", err); |
864 | return err; | 944 | return err; |
865 | } | 945 | } |
866 | 946 | ||
@@ -877,6 +957,7 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, | |||
877 | 957 | ||
878 | static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) | 958 | static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) |
879 | { | 959 | { |
960 | struct device *dev = pmc->dev; | ||
880 | struct tegra_powergate *pg; | 961 | struct tegra_powergate *pg; |
881 | int id, err; | 962 | int id, err; |
882 | bool off; | 963 | bool off; |
@@ -887,7 +968,7 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) | |||
887 | 968 | ||
888 | id = tegra_powergate_lookup(pmc, np->name); | 969 | id = tegra_powergate_lookup(pmc, np->name); |
889 | if (id < 0) { | 970 | if (id < 0) { |
890 | pr_err("powergate lookup failed for %pOFn: %d\n", np, id); | 971 | dev_err(dev, "powergate lookup failed for %pOFn: %d\n", np, id); |
891 | goto free_mem; | 972 | goto free_mem; |
892 | } | 973 | } |
893 | 974 | ||
@@ -903,17 +984,17 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) | |||
903 | pg->genpd.power_on = tegra_genpd_power_on; | 984 | pg->genpd.power_on = tegra_genpd_power_on; |
904 | pg->pmc = pmc; | 985 | pg->pmc = pmc; |
905 | 986 | ||
906 | off = !tegra_powergate_is_powered(pg->id); | 987 | off = !tegra_powergate_is_powered(pmc, pg->id); |
907 | 988 | ||
908 | err = tegra_powergate_of_get_clks(pg, np); | 989 | err = tegra_powergate_of_get_clks(pg, np); |
909 | if (err < 0) { | 990 | if (err < 0) { |
910 | pr_err("failed to get clocks for %pOFn: %d\n", np, err); | 991 | dev_err(dev, "failed to get clocks for %pOFn: %d\n", np, err); |
911 | goto set_available; | 992 | goto set_available; |
912 | } | 993 | } |
913 | 994 | ||
914 | err = tegra_powergate_of_get_resets(pg, np, off); | 995 | err = tegra_powergate_of_get_resets(pg, np, off); |
915 | if (err < 0) { | 996 | if (err < 0) { |
916 | pr_err("failed to get resets for %pOFn: %d\n", np, err); | 997 | dev_err(dev, "failed to get resets for %pOFn: %d\n", np, err); |
917 | goto remove_clks; | 998 | goto remove_clks; |
918 | } | 999 | } |
919 | 1000 | ||
@@ -926,19 +1007,19 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) | |||
926 | 1007 | ||
927 | err = pm_genpd_init(&pg->genpd, NULL, off); | 1008 | err = pm_genpd_init(&pg->genpd, NULL, off); |
928 | if (err < 0) { | 1009 | if (err < 0) { |
929 | pr_err("failed to initialise PM domain %pOFn: %d\n", np, | 1010 | dev_err(dev, "failed to initialise PM domain %pOFn: %d\n", np, |
930 | err); | 1011 | err); |
931 | goto remove_resets; | 1012 | goto remove_resets; |
932 | } | 1013 | } |
933 | 1014 | ||
934 | err = of_genpd_add_provider_simple(np, &pg->genpd); | 1015 | err = of_genpd_add_provider_simple(np, &pg->genpd); |
935 | if (err < 0) { | 1016 | if (err < 0) { |
936 | pr_err("failed to add PM domain provider for %pOFn: %d\n", | 1017 | dev_err(dev, "failed to add PM domain provider for %pOFn: %d\n", |
937 | np, err); | 1018 | np, err); |
938 | goto remove_genpd; | 1019 | goto remove_genpd; |
939 | } | 1020 | } |
940 | 1021 | ||
941 | pr_debug("added PM domain %s\n", pg->genpd.name); | 1022 | dev_dbg(dev, "added PM domain %s\n", pg->genpd.name); |
942 | 1023 | ||
943 | return; | 1024 | return; |
944 | 1025 | ||
@@ -994,7 +1075,8 @@ tegra_io_pad_find(struct tegra_pmc *pmc, enum tegra_io_pad id) | |||
994 | return NULL; | 1075 | return NULL; |
995 | } | 1076 | } |
996 | 1077 | ||
997 | static int tegra_io_pad_get_dpd_register_bit(enum tegra_io_pad id, | 1078 | static int tegra_io_pad_get_dpd_register_bit(struct tegra_pmc *pmc, |
1079 | enum tegra_io_pad id, | ||
998 | unsigned long *request, | 1080 | unsigned long *request, |
999 | unsigned long *status, | 1081 | unsigned long *status, |
1000 | u32 *mask) | 1082 | u32 *mask) |
@@ -1003,7 +1085,7 @@ static int tegra_io_pad_get_dpd_register_bit(enum tegra_io_pad id, | |||
1003 | 1085 | ||
1004 | pad = tegra_io_pad_find(pmc, id); | 1086 | pad = tegra_io_pad_find(pmc, id); |
1005 | if (!pad) { | 1087 | if (!pad) { |
1006 | pr_err("invalid I/O pad ID %u\n", id); | 1088 | dev_err(pmc->dev, "invalid I/O pad ID %u\n", id); |
1007 | return -ENOENT; | 1089 | return -ENOENT; |
1008 | } | 1090 | } |
1009 | 1091 | ||
@@ -1023,43 +1105,44 @@ static int tegra_io_pad_get_dpd_register_bit(enum tegra_io_pad id, | |||
1023 | return 0; | 1105 | return 0; |
1024 | } | 1106 | } |
1025 | 1107 | ||
1026 | static int tegra_io_pad_prepare(enum tegra_io_pad id, unsigned long *request, | 1108 | static int tegra_io_pad_prepare(struct tegra_pmc *pmc, enum tegra_io_pad id, |
1027 | unsigned long *status, u32 *mask) | 1109 | unsigned long *request, unsigned long *status, |
1110 | u32 *mask) | ||
1028 | { | 1111 | { |
1029 | unsigned long rate, value; | 1112 | unsigned long rate, value; |
1030 | int err; | 1113 | int err; |
1031 | 1114 | ||
1032 | err = tegra_io_pad_get_dpd_register_bit(id, request, status, mask); | 1115 | err = tegra_io_pad_get_dpd_register_bit(pmc, id, request, status, mask); |
1033 | if (err) | 1116 | if (err) |
1034 | return err; | 1117 | return err; |
1035 | 1118 | ||
1036 | if (pmc->clk) { | 1119 | if (pmc->clk) { |
1037 | rate = clk_get_rate(pmc->clk); | 1120 | rate = clk_get_rate(pmc->clk); |
1038 | if (!rate) { | 1121 | if (!rate) { |
1039 | pr_err("failed to get clock rate\n"); | 1122 | dev_err(pmc->dev, "failed to get clock rate\n"); |
1040 | return -ENODEV; | 1123 | return -ENODEV; |
1041 | } | 1124 | } |
1042 | 1125 | ||
1043 | tegra_pmc_writel(DPD_SAMPLE_ENABLE, DPD_SAMPLE); | 1126 | tegra_pmc_writel(pmc, DPD_SAMPLE_ENABLE, DPD_SAMPLE); |
1044 | 1127 | ||
1045 | /* must be at least 200 ns, in APB (PCLK) clock cycles */ | 1128 | /* must be at least 200 ns, in APB (PCLK) clock cycles */ |
1046 | value = DIV_ROUND_UP(1000000000, rate); | 1129 | value = DIV_ROUND_UP(1000000000, rate); |
1047 | value = DIV_ROUND_UP(200, value); | 1130 | value = DIV_ROUND_UP(200, value); |
1048 | tegra_pmc_writel(value, SEL_DPD_TIM); | 1131 | tegra_pmc_writel(pmc, value, SEL_DPD_TIM); |
1049 | } | 1132 | } |
1050 | 1133 | ||
1051 | return 0; | 1134 | return 0; |
1052 | } | 1135 | } |
1053 | 1136 | ||
1054 | static int tegra_io_pad_poll(unsigned long offset, u32 mask, | 1137 | static int tegra_io_pad_poll(struct tegra_pmc *pmc, unsigned long offset, |
1055 | u32 val, unsigned long timeout) | 1138 | u32 mask, u32 val, unsigned long timeout) |
1056 | { | 1139 | { |
1057 | u32 value; | 1140 | u32 value; |
1058 | 1141 | ||
1059 | timeout = jiffies + msecs_to_jiffies(timeout); | 1142 | timeout = jiffies + msecs_to_jiffies(timeout); |
1060 | 1143 | ||
1061 | while (time_after(timeout, jiffies)) { | 1144 | while (time_after(timeout, jiffies)) { |
1062 | value = tegra_pmc_readl(offset); | 1145 | value = tegra_pmc_readl(pmc, offset); |
1063 | if ((value & mask) == val) | 1146 | if ((value & mask) == val) |
1064 | return 0; | 1147 | return 0; |
1065 | 1148 | ||
@@ -1069,10 +1152,10 @@ static int tegra_io_pad_poll(unsigned long offset, u32 mask, | |||
1069 | return -ETIMEDOUT; | 1152 | return -ETIMEDOUT; |
1070 | } | 1153 | } |
1071 | 1154 | ||
1072 | static void tegra_io_pad_unprepare(void) | 1155 | static void tegra_io_pad_unprepare(struct tegra_pmc *pmc) |
1073 | { | 1156 | { |
1074 | if (pmc->clk) | 1157 | if (pmc->clk) |
1075 | tegra_pmc_writel(DPD_SAMPLE_DISABLE, DPD_SAMPLE); | 1158 | tegra_pmc_writel(pmc, DPD_SAMPLE_DISABLE, DPD_SAMPLE); |
1076 | } | 1159 | } |
1077 | 1160 | ||
1078 | /** | 1161 | /** |
@@ -1089,21 +1172,21 @@ int tegra_io_pad_power_enable(enum tegra_io_pad id) | |||
1089 | 1172 | ||
1090 | mutex_lock(&pmc->powergates_lock); | 1173 | mutex_lock(&pmc->powergates_lock); |
1091 | 1174 | ||
1092 | err = tegra_io_pad_prepare(id, &request, &status, &mask); | 1175 | err = tegra_io_pad_prepare(pmc, id, &request, &status, &mask); |
1093 | if (err < 0) { | 1176 | if (err < 0) { |
1094 | pr_err("failed to prepare I/O pad: %d\n", err); | 1177 | dev_err(pmc->dev, "failed to prepare I/O pad: %d\n", err); |
1095 | goto unlock; | 1178 | goto unlock; |
1096 | } | 1179 | } |
1097 | 1180 | ||
1098 | tegra_pmc_writel(IO_DPD_REQ_CODE_OFF | mask, request); | 1181 | tegra_pmc_writel(pmc, IO_DPD_REQ_CODE_OFF | mask, request); |
1099 | 1182 | ||
1100 | err = tegra_io_pad_poll(status, mask, 0, 250); | 1183 | err = tegra_io_pad_poll(pmc, status, mask, 0, 250); |
1101 | if (err < 0) { | 1184 | if (err < 0) { |
1102 | pr_err("failed to enable I/O pad: %d\n", err); | 1185 | dev_err(pmc->dev, "failed to enable I/O pad: %d\n", err); |
1103 | goto unlock; | 1186 | goto unlock; |
1104 | } | 1187 | } |
1105 | 1188 | ||
1106 | tegra_io_pad_unprepare(); | 1189 | tegra_io_pad_unprepare(pmc); |
1107 | 1190 | ||
1108 | unlock: | 1191 | unlock: |
1109 | mutex_unlock(&pmc->powergates_lock); | 1192 | mutex_unlock(&pmc->powergates_lock); |
@@ -1125,21 +1208,21 @@ int tegra_io_pad_power_disable(enum tegra_io_pad id) | |||
1125 | 1208 | ||
1126 | mutex_lock(&pmc->powergates_lock); | 1209 | mutex_lock(&pmc->powergates_lock); |
1127 | 1210 | ||
1128 | err = tegra_io_pad_prepare(id, &request, &status, &mask); | 1211 | err = tegra_io_pad_prepare(pmc, id, &request, &status, &mask); |
1129 | if (err < 0) { | 1212 | if (err < 0) { |
1130 | pr_err("failed to prepare I/O pad: %d\n", err); | 1213 | dev_err(pmc->dev, "failed to prepare I/O pad: %d\n", err); |
1131 | goto unlock; | 1214 | goto unlock; |
1132 | } | 1215 | } |
1133 | 1216 | ||
1134 | tegra_pmc_writel(IO_DPD_REQ_CODE_ON | mask, request); | 1217 | tegra_pmc_writel(pmc, IO_DPD_REQ_CODE_ON | mask, request); |
1135 | 1218 | ||
1136 | err = tegra_io_pad_poll(status, mask, mask, 250); | 1219 | err = tegra_io_pad_poll(pmc, status, mask, mask, 250); |
1137 | if (err < 0) { | 1220 | if (err < 0) { |
1138 | pr_err("failed to disable I/O pad: %d\n", err); | 1221 | dev_err(pmc->dev, "failed to disable I/O pad: %d\n", err); |
1139 | goto unlock; | 1222 | goto unlock; |
1140 | } | 1223 | } |
1141 | 1224 | ||
1142 | tegra_io_pad_unprepare(); | 1225 | tegra_io_pad_unprepare(pmc); |
1143 | 1226 | ||
1144 | unlock: | 1227 | unlock: |
1145 | mutex_unlock(&pmc->powergates_lock); | 1228 | mutex_unlock(&pmc->powergates_lock); |
@@ -1147,22 +1230,24 @@ unlock: | |||
1147 | } | 1230 | } |
1148 | EXPORT_SYMBOL(tegra_io_pad_power_disable); | 1231 | EXPORT_SYMBOL(tegra_io_pad_power_disable); |
1149 | 1232 | ||
1150 | static int tegra_io_pad_is_powered(enum tegra_io_pad id) | 1233 | static int tegra_io_pad_is_powered(struct tegra_pmc *pmc, enum tegra_io_pad id) |
1151 | { | 1234 | { |
1152 | unsigned long request, status; | 1235 | unsigned long request, status; |
1153 | u32 mask, value; | 1236 | u32 mask, value; |
1154 | int err; | 1237 | int err; |
1155 | 1238 | ||
1156 | err = tegra_io_pad_get_dpd_register_bit(id, &request, &status, &mask); | 1239 | err = tegra_io_pad_get_dpd_register_bit(pmc, id, &request, &status, |
1240 | &mask); | ||
1157 | if (err) | 1241 | if (err) |
1158 | return err; | 1242 | return err; |
1159 | 1243 | ||
1160 | value = tegra_pmc_readl(status); | 1244 | value = tegra_pmc_readl(pmc, status); |
1161 | 1245 | ||
1162 | return !(value & mask); | 1246 | return !(value & mask); |
1163 | } | 1247 | } |
1164 | 1248 | ||
1165 | static int tegra_io_pad_set_voltage(enum tegra_io_pad id, int voltage) | 1249 | static int tegra_io_pad_set_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id, |
1250 | int voltage) | ||
1166 | { | 1251 | { |
1167 | const struct tegra_io_pad_soc *pad; | 1252 | const struct tegra_io_pad_soc *pad; |
1168 | u32 value; | 1253 | u32 value; |
@@ -1177,29 +1262,29 @@ static int tegra_io_pad_set_voltage(enum tegra_io_pad id, int voltage) | |||
1177 | mutex_lock(&pmc->powergates_lock); | 1262 | mutex_lock(&pmc->powergates_lock); |
1178 | 1263 | ||
1179 | if (pmc->soc->has_impl_33v_pwr) { | 1264 | if (pmc->soc->has_impl_33v_pwr) { |
1180 | value = tegra_pmc_readl(PMC_IMPL_E_33V_PWR); | 1265 | value = tegra_pmc_readl(pmc, PMC_IMPL_E_33V_PWR); |
1181 | 1266 | ||
1182 | if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) | 1267 | if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) |
1183 | value &= ~BIT(pad->voltage); | 1268 | value &= ~BIT(pad->voltage); |
1184 | else | 1269 | else |
1185 | value |= BIT(pad->voltage); | 1270 | value |= BIT(pad->voltage); |
1186 | 1271 | ||
1187 | tegra_pmc_writel(value, PMC_IMPL_E_33V_PWR); | 1272 | tegra_pmc_writel(pmc, value, PMC_IMPL_E_33V_PWR); |
1188 | } else { | 1273 | } else { |
1189 | /* write-enable PMC_PWR_DET_VALUE[pad->voltage] */ | 1274 | /* write-enable PMC_PWR_DET_VALUE[pad->voltage] */ |
1190 | value = tegra_pmc_readl(PMC_PWR_DET); | 1275 | value = tegra_pmc_readl(pmc, PMC_PWR_DET); |
1191 | value |= BIT(pad->voltage); | 1276 | value |= BIT(pad->voltage); |
1192 | tegra_pmc_writel(value, PMC_PWR_DET); | 1277 | tegra_pmc_writel(pmc, value, PMC_PWR_DET); |
1193 | 1278 | ||
1194 | /* update I/O voltage */ | 1279 | /* update I/O voltage */ |
1195 | value = tegra_pmc_readl(PMC_PWR_DET_VALUE); | 1280 | value = tegra_pmc_readl(pmc, PMC_PWR_DET_VALUE); |
1196 | 1281 | ||
1197 | if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) | 1282 | if (voltage == TEGRA_IO_PAD_VOLTAGE_1V8) |
1198 | value &= ~BIT(pad->voltage); | 1283 | value &= ~BIT(pad->voltage); |
1199 | else | 1284 | else |
1200 | value |= BIT(pad->voltage); | 1285 | value |= BIT(pad->voltage); |
1201 | 1286 | ||
1202 | tegra_pmc_writel(value, PMC_PWR_DET_VALUE); | 1287 | tegra_pmc_writel(pmc, value, PMC_PWR_DET_VALUE); |
1203 | } | 1288 | } |
1204 | 1289 | ||
1205 | mutex_unlock(&pmc->powergates_lock); | 1290 | mutex_unlock(&pmc->powergates_lock); |
@@ -1209,7 +1294,7 @@ static int tegra_io_pad_set_voltage(enum tegra_io_pad id, int voltage) | |||
1209 | return 0; | 1294 | return 0; |
1210 | } | 1295 | } |
1211 | 1296 | ||
1212 | static int tegra_io_pad_get_voltage(enum tegra_io_pad id) | 1297 | static int tegra_io_pad_get_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id) |
1213 | { | 1298 | { |
1214 | const struct tegra_io_pad_soc *pad; | 1299 | const struct tegra_io_pad_soc *pad; |
1215 | u32 value; | 1300 | u32 value; |
@@ -1222,9 +1307,9 @@ static int tegra_io_pad_get_voltage(enum tegra_io_pad id) | |||
1222 | return -ENOTSUPP; | 1307 | return -ENOTSUPP; |
1223 | 1308 | ||
1224 | if (pmc->soc->has_impl_33v_pwr) | 1309 | if (pmc->soc->has_impl_33v_pwr) |
1225 | value = tegra_pmc_readl(PMC_IMPL_E_33V_PWR); | 1310 | value = tegra_pmc_readl(pmc, PMC_IMPL_E_33V_PWR); |
1226 | else | 1311 | else |
1227 | value = tegra_pmc_readl(PMC_PWR_DET_VALUE); | 1312 | value = tegra_pmc_readl(pmc, PMC_PWR_DET_VALUE); |
1228 | 1313 | ||
1229 | if ((value & BIT(pad->voltage)) == 0) | 1314 | if ((value & BIT(pad->voltage)) == 0) |
1230 | return TEGRA_IO_PAD_VOLTAGE_1V8; | 1315 | return TEGRA_IO_PAD_VOLTAGE_1V8; |
@@ -1296,21 +1381,21 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) | |||
1296 | 1381 | ||
1297 | ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1; | 1382 | ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1; |
1298 | do_div(ticks, USEC_PER_SEC); | 1383 | do_div(ticks, USEC_PER_SEC); |
1299 | tegra_pmc_writel(ticks, PMC_CPUPWRGOOD_TIMER); | 1384 | tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER); |
1300 | 1385 | ||
1301 | ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1; | 1386 | ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1; |
1302 | do_div(ticks, USEC_PER_SEC); | 1387 | do_div(ticks, USEC_PER_SEC); |
1303 | tegra_pmc_writel(ticks, PMC_CPUPWROFF_TIMER); | 1388 | tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER); |
1304 | 1389 | ||
1305 | wmb(); | 1390 | wmb(); |
1306 | 1391 | ||
1307 | pmc->rate = rate; | 1392 | pmc->rate = rate; |
1308 | } | 1393 | } |
1309 | 1394 | ||
1310 | value = tegra_pmc_readl(PMC_CNTRL); | 1395 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
1311 | value &= ~PMC_CNTRL_SIDE_EFFECT_LP0; | 1396 | value &= ~PMC_CNTRL_SIDE_EFFECT_LP0; |
1312 | value |= PMC_CNTRL_CPU_PWRREQ_OE; | 1397 | value |= PMC_CNTRL_CPU_PWRREQ_OE; |
1313 | tegra_pmc_writel(value, PMC_CNTRL); | 1398 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
1314 | } | 1399 | } |
1315 | #endif | 1400 | #endif |
1316 | 1401 | ||
@@ -1432,13 +1517,13 @@ static void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc) | |||
1432 | if (of_property_read_u32(np, "nvidia,pinmux-id", &pinmux)) | 1517 | if (of_property_read_u32(np, "nvidia,pinmux-id", &pinmux)) |
1433 | pinmux = 0; | 1518 | pinmux = 0; |
1434 | 1519 | ||
1435 | value = tegra_pmc_readl(PMC_SENSOR_CTRL); | 1520 | value = tegra_pmc_readl(pmc, PMC_SENSOR_CTRL); |
1436 | value |= PMC_SENSOR_CTRL_SCRATCH_WRITE; | 1521 | value |= PMC_SENSOR_CTRL_SCRATCH_WRITE; |
1437 | tegra_pmc_writel(value, PMC_SENSOR_CTRL); | 1522 | tegra_pmc_writel(pmc, value, PMC_SENSOR_CTRL); |
1438 | 1523 | ||
1439 | value = (reg_data << PMC_SCRATCH54_DATA_SHIFT) | | 1524 | value = (reg_data << PMC_SCRATCH54_DATA_SHIFT) | |
1440 | (reg_addr << PMC_SCRATCH54_ADDR_SHIFT); | 1525 | (reg_addr << PMC_SCRATCH54_ADDR_SHIFT); |
1441 | tegra_pmc_writel(value, PMC_SCRATCH54); | 1526 | tegra_pmc_writel(pmc, value, PMC_SCRATCH54); |
1442 | 1527 | ||
1443 | value = PMC_SCRATCH55_RESET_TEGRA; | 1528 | value = PMC_SCRATCH55_RESET_TEGRA; |
1444 | value |= ctrl_id << PMC_SCRATCH55_CNTRL_ID_SHIFT; | 1529 | value |= ctrl_id << PMC_SCRATCH55_CNTRL_ID_SHIFT; |
@@ -1456,11 +1541,11 @@ static void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc) | |||
1456 | 1541 | ||
1457 | value |= checksum << PMC_SCRATCH55_CHECKSUM_SHIFT; | 1542 | value |= checksum << PMC_SCRATCH55_CHECKSUM_SHIFT; |
1458 | 1543 | ||
1459 | tegra_pmc_writel(value, PMC_SCRATCH55); | 1544 | tegra_pmc_writel(pmc, value, PMC_SCRATCH55); |
1460 | 1545 | ||
1461 | value = tegra_pmc_readl(PMC_SENSOR_CTRL); | 1546 | value = tegra_pmc_readl(pmc, PMC_SENSOR_CTRL); |
1462 | value |= PMC_SENSOR_CTRL_ENABLE_RST; | 1547 | value |= PMC_SENSOR_CTRL_ENABLE_RST; |
1463 | tegra_pmc_writel(value, PMC_SENSOR_CTRL); | 1548 | tegra_pmc_writel(pmc, value, PMC_SENSOR_CTRL); |
1464 | 1549 | ||
1465 | dev_info(pmc->dev, "emergency thermal reset enabled\n"); | 1550 | dev_info(pmc->dev, "emergency thermal reset enabled\n"); |
1466 | 1551 | ||
@@ -1470,12 +1555,16 @@ out: | |||
1470 | 1555 | ||
1471 | static int tegra_io_pad_pinctrl_get_groups_count(struct pinctrl_dev *pctl_dev) | 1556 | static int tegra_io_pad_pinctrl_get_groups_count(struct pinctrl_dev *pctl_dev) |
1472 | { | 1557 | { |
1558 | struct tegra_pmc *pmc = pinctrl_dev_get_drvdata(pctl_dev); | ||
1559 | |||
1473 | return pmc->soc->num_io_pads; | 1560 | return pmc->soc->num_io_pads; |
1474 | } | 1561 | } |
1475 | 1562 | ||
1476 | static const char *tegra_io_pad_pinctrl_get_group_name( | 1563 | static const char *tegra_io_pad_pinctrl_get_group_name(struct pinctrl_dev *pctl, |
1477 | struct pinctrl_dev *pctl, unsigned int group) | 1564 | unsigned int group) |
1478 | { | 1565 | { |
1566 | struct tegra_pmc *pmc = pinctrl_dev_get_drvdata(pctl); | ||
1567 | |||
1479 | return pmc->soc->io_pads[group].name; | 1568 | return pmc->soc->io_pads[group].name; |
1480 | } | 1569 | } |
1481 | 1570 | ||
@@ -1484,8 +1573,11 @@ static int tegra_io_pad_pinctrl_get_group_pins(struct pinctrl_dev *pctl_dev, | |||
1484 | const unsigned int **pins, | 1573 | const unsigned int **pins, |
1485 | unsigned int *num_pins) | 1574 | unsigned int *num_pins) |
1486 | { | 1575 | { |
1576 | struct tegra_pmc *pmc = pinctrl_dev_get_drvdata(pctl_dev); | ||
1577 | |||
1487 | *pins = &pmc->soc->io_pads[group].id; | 1578 | *pins = &pmc->soc->io_pads[group].id; |
1488 | *num_pins = 1; | 1579 | *num_pins = 1; |
1580 | |||
1489 | return 0; | 1581 | return 0; |
1490 | } | 1582 | } |
1491 | 1583 | ||
@@ -1500,27 +1592,33 @@ static const struct pinctrl_ops tegra_io_pad_pinctrl_ops = { | |||
1500 | static int tegra_io_pad_pinconf_get(struct pinctrl_dev *pctl_dev, | 1592 | static int tegra_io_pad_pinconf_get(struct pinctrl_dev *pctl_dev, |
1501 | unsigned int pin, unsigned long *config) | 1593 | unsigned int pin, unsigned long *config) |
1502 | { | 1594 | { |
1503 | const struct tegra_io_pad_soc *pad = tegra_io_pad_find(pmc, pin); | ||
1504 | enum pin_config_param param = pinconf_to_config_param(*config); | 1595 | enum pin_config_param param = pinconf_to_config_param(*config); |
1596 | struct tegra_pmc *pmc = pinctrl_dev_get_drvdata(pctl_dev); | ||
1597 | const struct tegra_io_pad_soc *pad; | ||
1505 | int ret; | 1598 | int ret; |
1506 | u32 arg; | 1599 | u32 arg; |
1507 | 1600 | ||
1601 | pad = tegra_io_pad_find(pmc, pin); | ||
1508 | if (!pad) | 1602 | if (!pad) |
1509 | return -EINVAL; | 1603 | return -EINVAL; |
1510 | 1604 | ||
1511 | switch (param) { | 1605 | switch (param) { |
1512 | case PIN_CONFIG_POWER_SOURCE: | 1606 | case PIN_CONFIG_POWER_SOURCE: |
1513 | ret = tegra_io_pad_get_voltage(pad->id); | 1607 | ret = tegra_io_pad_get_voltage(pmc, pad->id); |
1514 | if (ret < 0) | 1608 | if (ret < 0) |
1515 | return ret; | 1609 | return ret; |
1610 | |||
1516 | arg = ret; | 1611 | arg = ret; |
1517 | break; | 1612 | break; |
1613 | |||
1518 | case PIN_CONFIG_LOW_POWER_MODE: | 1614 | case PIN_CONFIG_LOW_POWER_MODE: |
1519 | ret = tegra_io_pad_is_powered(pad->id); | 1615 | ret = tegra_io_pad_is_powered(pmc, pad->id); |
1520 | if (ret < 0) | 1616 | if (ret < 0) |
1521 | return ret; | 1617 | return ret; |
1618 | |||
1522 | arg = !ret; | 1619 | arg = !ret; |
1523 | break; | 1620 | break; |
1621 | |||
1524 | default: | 1622 | default: |
1525 | return -EINVAL; | 1623 | return -EINVAL; |
1526 | } | 1624 | } |
@@ -1534,12 +1632,14 @@ static int tegra_io_pad_pinconf_set(struct pinctrl_dev *pctl_dev, | |||
1534 | unsigned int pin, unsigned long *configs, | 1632 | unsigned int pin, unsigned long *configs, |
1535 | unsigned int num_configs) | 1633 | unsigned int num_configs) |
1536 | { | 1634 | { |
1537 | const struct tegra_io_pad_soc *pad = tegra_io_pad_find(pmc, pin); | 1635 | struct tegra_pmc *pmc = pinctrl_dev_get_drvdata(pctl_dev); |
1636 | const struct tegra_io_pad_soc *pad; | ||
1538 | enum pin_config_param param; | 1637 | enum pin_config_param param; |
1539 | unsigned int i; | 1638 | unsigned int i; |
1540 | int err; | 1639 | int err; |
1541 | u32 arg; | 1640 | u32 arg; |
1542 | 1641 | ||
1642 | pad = tegra_io_pad_find(pmc, pin); | ||
1543 | if (!pad) | 1643 | if (!pad) |
1544 | return -EINVAL; | 1644 | return -EINVAL; |
1545 | 1645 | ||
@@ -1560,7 +1660,7 @@ static int tegra_io_pad_pinconf_set(struct pinctrl_dev *pctl_dev, | |||
1560 | if (arg != TEGRA_IO_PAD_VOLTAGE_1V8 && | 1660 | if (arg != TEGRA_IO_PAD_VOLTAGE_1V8 && |
1561 | arg != TEGRA_IO_PAD_VOLTAGE_3V3) | 1661 | arg != TEGRA_IO_PAD_VOLTAGE_3V3) |
1562 | return -EINVAL; | 1662 | return -EINVAL; |
1563 | err = tegra_io_pad_set_voltage(pad->id, arg); | 1663 | err = tegra_io_pad_set_voltage(pmc, pad->id, arg); |
1564 | if (err) | 1664 | if (err) |
1565 | return err; | 1665 | return err; |
1566 | break; | 1666 | break; |
@@ -1585,7 +1685,7 @@ static struct pinctrl_desc tegra_pmc_pctl_desc = { | |||
1585 | 1685 | ||
1586 | static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) | 1686 | static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) |
1587 | { | 1687 | { |
1588 | int err = 0; | 1688 | int err; |
1589 | 1689 | ||
1590 | if (!pmc->soc->num_pin_descs) | 1690 | if (!pmc->soc->num_pin_descs) |
1591 | return 0; | 1691 | return 0; |
@@ -1598,18 +1698,20 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) | |||
1598 | pmc); | 1698 | pmc); |
1599 | if (IS_ERR(pmc->pctl_dev)) { | 1699 | if (IS_ERR(pmc->pctl_dev)) { |
1600 | err = PTR_ERR(pmc->pctl_dev); | 1700 | err = PTR_ERR(pmc->pctl_dev); |
1601 | dev_err(pmc->dev, "unable to register pinctrl, %d\n", err); | 1701 | dev_err(pmc->dev, "failed to register pin controller: %d\n", |
1702 | err); | ||
1703 | return err; | ||
1602 | } | 1704 | } |
1603 | 1705 | ||
1604 | return err; | 1706 | return 0; |
1605 | } | 1707 | } |
1606 | 1708 | ||
1607 | static ssize_t reset_reason_show(struct device *dev, | 1709 | static ssize_t reset_reason_show(struct device *dev, |
1608 | struct device_attribute *attr, char *buf) | 1710 | struct device_attribute *attr, char *buf) |
1609 | { | 1711 | { |
1610 | u32 value, rst_src; | 1712 | u32 value, rst_src; |
1611 | 1713 | ||
1612 | value = tegra_pmc_readl(pmc->soc->regs->rst_status); | 1714 | value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status); |
1613 | rst_src = (value & pmc->soc->regs->rst_source_mask) >> | 1715 | rst_src = (value & pmc->soc->regs->rst_source_mask) >> |
1614 | pmc->soc->regs->rst_source_shift; | 1716 | pmc->soc->regs->rst_source_shift; |
1615 | 1717 | ||
@@ -1619,11 +1721,11 @@ static ssize_t reset_reason_show(struct device *dev, | |||
1619 | static DEVICE_ATTR_RO(reset_reason); | 1721 | static DEVICE_ATTR_RO(reset_reason); |
1620 | 1722 | ||
1621 | static ssize_t reset_level_show(struct device *dev, | 1723 | static ssize_t reset_level_show(struct device *dev, |
1622 | struct device_attribute *attr, char *buf) | 1724 | struct device_attribute *attr, char *buf) |
1623 | { | 1725 | { |
1624 | u32 value, rst_lvl; | 1726 | u32 value, rst_lvl; |
1625 | 1727 | ||
1626 | value = tegra_pmc_readl(pmc->soc->regs->rst_status); | 1728 | value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status); |
1627 | rst_lvl = (value & pmc->soc->regs->rst_level_mask) >> | 1729 | rst_lvl = (value & pmc->soc->regs->rst_level_mask) >> |
1628 | pmc->soc->regs->rst_level_shift; | 1730 | pmc->soc->regs->rst_level_shift; |
1629 | 1731 | ||
@@ -1641,16 +1743,16 @@ static void tegra_pmc_reset_sysfs_init(struct tegra_pmc *pmc) | |||
1641 | err = device_create_file(dev, &dev_attr_reset_reason); | 1743 | err = device_create_file(dev, &dev_attr_reset_reason); |
1642 | if (err < 0) | 1744 | if (err < 0) |
1643 | dev_warn(dev, | 1745 | dev_warn(dev, |
1644 | "failed to create attr \"reset_reason\": %d\n", | 1746 | "failed to create attr \"reset_reason\": %d\n", |
1645 | err); | 1747 | err); |
1646 | } | 1748 | } |
1647 | 1749 | ||
1648 | if (pmc->soc->reset_levels) { | 1750 | if (pmc->soc->reset_levels) { |
1649 | err = device_create_file(dev, &dev_attr_reset_level); | 1751 | err = device_create_file(dev, &dev_attr_reset_level); |
1650 | if (err < 0) | 1752 | if (err < 0) |
1651 | dev_warn(dev, | 1753 | dev_warn(dev, |
1652 | "failed to create attr \"reset_level\": %d\n", | 1754 | "failed to create attr \"reset_level\": %d\n", |
1653 | err); | 1755 | err); |
1654 | } | 1756 | } |
1655 | } | 1757 | } |
1656 | 1758 | ||
@@ -1920,6 +2022,8 @@ static int tegra_pmc_probe(struct platform_device *pdev) | |||
1920 | pmc->base = base; | 2022 | pmc->base = base; |
1921 | mutex_unlock(&pmc->powergates_lock); | 2023 | mutex_unlock(&pmc->powergates_lock); |
1922 | 2024 | ||
2025 | platform_set_drvdata(pdev, pmc); | ||
2026 | |||
1923 | return 0; | 2027 | return 0; |
1924 | 2028 | ||
1925 | cleanup_restart_handler: | 2029 | cleanup_restart_handler: |
@@ -1932,14 +2036,18 @@ cleanup_debugfs: | |||
1932 | #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM) | 2036 | #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM) |
1933 | static int tegra_pmc_suspend(struct device *dev) | 2037 | static int tegra_pmc_suspend(struct device *dev) |
1934 | { | 2038 | { |
1935 | tegra_pmc_writel(virt_to_phys(tegra_resume), PMC_SCRATCH41); | 2039 | struct tegra_pmc *pmc = dev_get_drvdata(dev); |
2040 | |||
2041 | tegra_pmc_writel(pmc, virt_to_phys(tegra_resume), PMC_SCRATCH41); | ||
1936 | 2042 | ||
1937 | return 0; | 2043 | return 0; |
1938 | } | 2044 | } |
1939 | 2045 | ||
1940 | static int tegra_pmc_resume(struct device *dev) | 2046 | static int tegra_pmc_resume(struct device *dev) |
1941 | { | 2047 | { |
1942 | tegra_pmc_writel(0x0, PMC_SCRATCH41); | 2048 | struct tegra_pmc *pmc = dev_get_drvdata(dev); |
2049 | |||
2050 | tegra_pmc_writel(pmc, 0x0, PMC_SCRATCH41); | ||
1943 | 2051 | ||
1944 | return 0; | 2052 | return 0; |
1945 | } | 2053 | } |
@@ -1976,11 +2084,11 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc) | |||
1976 | u32 value; | 2084 | u32 value; |
1977 | 2085 | ||
1978 | /* Always enable CPU power request */ | 2086 | /* Always enable CPU power request */ |
1979 | value = tegra_pmc_readl(PMC_CNTRL); | 2087 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
1980 | value |= PMC_CNTRL_CPU_PWRREQ_OE; | 2088 | value |= PMC_CNTRL_CPU_PWRREQ_OE; |
1981 | tegra_pmc_writel(value, PMC_CNTRL); | 2089 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
1982 | 2090 | ||
1983 | value = tegra_pmc_readl(PMC_CNTRL); | 2091 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
1984 | 2092 | ||
1985 | if (pmc->sysclkreq_high) | 2093 | if (pmc->sysclkreq_high) |
1986 | value &= ~PMC_CNTRL_SYSCLK_POLARITY; | 2094 | value &= ~PMC_CNTRL_SYSCLK_POLARITY; |
@@ -1988,12 +2096,12 @@ static void tegra20_pmc_init(struct tegra_pmc *pmc) | |||
1988 | value |= PMC_CNTRL_SYSCLK_POLARITY; | 2096 | value |= PMC_CNTRL_SYSCLK_POLARITY; |
1989 | 2097 | ||
1990 | /* configure the output polarity while the request is tristated */ | 2098 | /* configure the output polarity while the request is tristated */ |
1991 | tegra_pmc_writel(value, PMC_CNTRL); | 2099 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
1992 | 2100 | ||
1993 | /* now enable the request */ | 2101 | /* now enable the request */ |
1994 | value = tegra_pmc_readl(PMC_CNTRL); | 2102 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
1995 | value |= PMC_CNTRL_SYSCLK_OE; | 2103 | value |= PMC_CNTRL_SYSCLK_OE; |
1996 | tegra_pmc_writel(value, PMC_CNTRL); | 2104 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
1997 | } | 2105 | } |
1998 | 2106 | ||
1999 | static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc, | 2107 | static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc, |
@@ -2002,14 +2110,14 @@ static void tegra20_pmc_setup_irq_polarity(struct tegra_pmc *pmc, | |||
2002 | { | 2110 | { |
2003 | u32 value; | 2111 | u32 value; |
2004 | 2112 | ||
2005 | value = tegra_pmc_readl(PMC_CNTRL); | 2113 | value = tegra_pmc_readl(pmc, PMC_CNTRL); |
2006 | 2114 | ||
2007 | if (invert) | 2115 | if (invert) |
2008 | value |= PMC_CNTRL_INTR_POLARITY; | 2116 | value |= PMC_CNTRL_INTR_POLARITY; |
2009 | else | 2117 | else |
2010 | value &= ~PMC_CNTRL_INTR_POLARITY; | 2118 | value &= ~PMC_CNTRL_INTR_POLARITY; |
2011 | 2119 | ||
2012 | tegra_pmc_writel(value, PMC_CNTRL); | 2120 | tegra_pmc_writel(pmc, value, PMC_CNTRL); |
2013 | } | 2121 | } |
2014 | 2122 | ||
2015 | static const struct tegra_pmc_soc tegra20_pmc_soc = { | 2123 | static const struct tegra_pmc_soc tegra20_pmc_soc = { |
@@ -2019,6 +2127,9 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { | |||
2019 | .cpu_powergates = NULL, | 2127 | .cpu_powergates = NULL, |
2020 | .has_tsense_reset = false, | 2128 | .has_tsense_reset = false, |
2021 | .has_gpu_clamps = false, | 2129 | .has_gpu_clamps = false, |
2130 | .needs_mbist_war = false, | ||
2131 | .has_impl_33v_pwr = false, | ||
2132 | .maybe_tz_only = false, | ||
2022 | .num_io_pads = 0, | 2133 | .num_io_pads = 0, |
2023 | .io_pads = NULL, | 2134 | .io_pads = NULL, |
2024 | .num_pin_descs = 0, | 2135 | .num_pin_descs = 0, |
@@ -2063,7 +2174,9 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { | |||
2063 | .cpu_powergates = tegra30_cpu_powergates, | 2174 | .cpu_powergates = tegra30_cpu_powergates, |
2064 | .has_tsense_reset = true, | 2175 | .has_tsense_reset = true, |
2065 | .has_gpu_clamps = false, | 2176 | .has_gpu_clamps = false, |
2177 | .needs_mbist_war = false, | ||
2066 | .has_impl_33v_pwr = false, | 2178 | .has_impl_33v_pwr = false, |
2179 | .maybe_tz_only = false, | ||
2067 | .num_io_pads = 0, | 2180 | .num_io_pads = 0, |
2068 | .io_pads = NULL, | 2181 | .io_pads = NULL, |
2069 | .num_pin_descs = 0, | 2182 | .num_pin_descs = 0, |
@@ -2112,7 +2225,9 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { | |||
2112 | .cpu_powergates = tegra114_cpu_powergates, | 2225 | .cpu_powergates = tegra114_cpu_powergates, |
2113 | .has_tsense_reset = true, | 2226 | .has_tsense_reset = true, |
2114 | .has_gpu_clamps = false, | 2227 | .has_gpu_clamps = false, |
2228 | .needs_mbist_war = false, | ||
2115 | .has_impl_33v_pwr = false, | 2229 | .has_impl_33v_pwr = false, |
2230 | .maybe_tz_only = false, | ||
2116 | .num_io_pads = 0, | 2231 | .num_io_pads = 0, |
2117 | .io_pads = NULL, | 2232 | .io_pads = NULL, |
2118 | .num_pin_descs = 0, | 2233 | .num_pin_descs = 0, |
@@ -2221,7 +2336,9 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { | |||
2221 | .cpu_powergates = tegra124_cpu_powergates, | 2336 | .cpu_powergates = tegra124_cpu_powergates, |
2222 | .has_tsense_reset = true, | 2337 | .has_tsense_reset = true, |
2223 | .has_gpu_clamps = true, | 2338 | .has_gpu_clamps = true, |
2339 | .needs_mbist_war = false, | ||
2224 | .has_impl_33v_pwr = false, | 2340 | .has_impl_33v_pwr = false, |
2341 | .maybe_tz_only = false, | ||
2225 | .num_io_pads = ARRAY_SIZE(tegra124_io_pads), | 2342 | .num_io_pads = ARRAY_SIZE(tegra124_io_pads), |
2226 | .io_pads = tegra124_io_pads, | 2343 | .io_pads = tegra124_io_pads, |
2227 | .num_pin_descs = ARRAY_SIZE(tegra124_pin_descs), | 2344 | .num_pin_descs = ARRAY_SIZE(tegra124_pin_descs), |
@@ -2325,8 +2442,9 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { | |||
2325 | .cpu_powergates = tegra210_cpu_powergates, | 2442 | .cpu_powergates = tegra210_cpu_powergates, |
2326 | .has_tsense_reset = true, | 2443 | .has_tsense_reset = true, |
2327 | .has_gpu_clamps = true, | 2444 | .has_gpu_clamps = true, |
2328 | .has_impl_33v_pwr = false, | ||
2329 | .needs_mbist_war = true, | 2445 | .needs_mbist_war = true, |
2446 | .has_impl_33v_pwr = false, | ||
2447 | .maybe_tz_only = true, | ||
2330 | .num_io_pads = ARRAY_SIZE(tegra210_io_pads), | 2448 | .num_io_pads = ARRAY_SIZE(tegra210_io_pads), |
2331 | .io_pads = tegra210_io_pads, | 2449 | .io_pads = tegra210_io_pads, |
2332 | .num_pin_descs = ARRAY_SIZE(tegra210_pin_descs), | 2450 | .num_pin_descs = ARRAY_SIZE(tegra210_pin_descs), |
@@ -2413,7 +2531,7 @@ static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, | |||
2413 | 2531 | ||
2414 | index = of_property_match_string(np, "reg-names", "wake"); | 2532 | index = of_property_match_string(np, "reg-names", "wake"); |
2415 | if (index < 0) { | 2533 | if (index < 0) { |
2416 | pr_err("failed to find PMC wake registers\n"); | 2534 | dev_err(pmc->dev, "failed to find PMC wake registers\n"); |
2417 | return; | 2535 | return; |
2418 | } | 2536 | } |
2419 | 2537 | ||
@@ -2421,7 +2539,7 @@ static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, | |||
2421 | 2539 | ||
2422 | wake = ioremap_nocache(regs.start, resource_size(®s)); | 2540 | wake = ioremap_nocache(regs.start, resource_size(®s)); |
2423 | if (!wake) { | 2541 | if (!wake) { |
2424 | pr_err("failed to map PMC wake registers\n"); | 2542 | dev_err(pmc->dev, "failed to map PMC wake registers\n"); |
2425 | return; | 2543 | return; |
2426 | } | 2544 | } |
2427 | 2545 | ||
@@ -2438,7 +2556,7 @@ static void tegra186_pmc_setup_irq_polarity(struct tegra_pmc *pmc, | |||
2438 | } | 2556 | } |
2439 | 2557 | ||
2440 | static const struct tegra_wake_event tegra186_wake_events[] = { | 2558 | static const struct tegra_wake_event tegra186_wake_events[] = { |
2441 | TEGRA_WAKE_GPIO("power", 29, 1, TEGRA_AON_GPIO(FF, 0)), | 2559 | TEGRA_WAKE_GPIO("power", 29, 1, TEGRA186_AON_GPIO(FF, 0)), |
2442 | TEGRA_WAKE_IRQ("rtc", 73, 10), | 2560 | TEGRA_WAKE_IRQ("rtc", 73, 10), |
2443 | }; | 2561 | }; |
2444 | 2562 | ||
@@ -2449,7 +2567,9 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { | |||
2449 | .cpu_powergates = NULL, | 2567 | .cpu_powergates = NULL, |
2450 | .has_tsense_reset = false, | 2568 | .has_tsense_reset = false, |
2451 | .has_gpu_clamps = false, | 2569 | .has_gpu_clamps = false, |
2570 | .needs_mbist_war = false, | ||
2452 | .has_impl_33v_pwr = true, | 2571 | .has_impl_33v_pwr = true, |
2572 | .maybe_tz_only = false, | ||
2453 | .num_io_pads = ARRAY_SIZE(tegra186_io_pads), | 2573 | .num_io_pads = ARRAY_SIZE(tegra186_io_pads), |
2454 | .io_pads = tegra186_io_pads, | 2574 | .io_pads = tegra186_io_pads, |
2455 | .num_pin_descs = ARRAY_SIZE(tegra186_pin_descs), | 2575 | .num_pin_descs = ARRAY_SIZE(tegra186_pin_descs), |
@@ -2527,6 +2647,9 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { | |||
2527 | .cpu_powergates = NULL, | 2647 | .cpu_powergates = NULL, |
2528 | .has_tsense_reset = false, | 2648 | .has_tsense_reset = false, |
2529 | .has_gpu_clamps = false, | 2649 | .has_gpu_clamps = false, |
2650 | .needs_mbist_war = false, | ||
2651 | .has_impl_33v_pwr = false, | ||
2652 | .maybe_tz_only = false, | ||
2530 | .num_io_pads = ARRAY_SIZE(tegra194_io_pads), | 2653 | .num_io_pads = ARRAY_SIZE(tegra194_io_pads), |
2531 | .io_pads = tegra194_io_pads, | 2654 | .io_pads = tegra194_io_pads, |
2532 | .regs = &tegra186_pmc_regs, | 2655 | .regs = &tegra186_pmc_regs, |
@@ -2561,6 +2684,32 @@ static struct platform_driver tegra_pmc_driver = { | |||
2561 | }; | 2684 | }; |
2562 | builtin_platform_driver(tegra_pmc_driver); | 2685 | builtin_platform_driver(tegra_pmc_driver); |
2563 | 2686 | ||
2687 | static bool __init tegra_pmc_detect_tz_only(struct tegra_pmc *pmc) | ||
2688 | { | ||
2689 | u32 value, saved; | ||
2690 | |||
2691 | saved = readl(pmc->base + pmc->soc->regs->scratch0); | ||
2692 | value = saved ^ 0xffffffff; | ||
2693 | |||
2694 | if (value == 0xffffffff) | ||
2695 | value = 0xdeadbeef; | ||
2696 | |||
2697 | /* write pattern and read it back */ | ||
2698 | writel(value, pmc->base + pmc->soc->regs->scratch0); | ||
2699 | value = readl(pmc->base + pmc->soc->regs->scratch0); | ||
2700 | |||
2701 | /* if we read all-zeroes, access is restricted to TZ only */ | ||
2702 | if (value == 0) { | ||
2703 | pr_info("access to PMC is restricted to TZ\n"); | ||
2704 | return true; | ||
2705 | } | ||
2706 | |||
2707 | /* restore original value */ | ||
2708 | writel(saved, pmc->base + pmc->soc->regs->scratch0); | ||
2709 | |||
2710 | return false; | ||
2711 | } | ||
2712 | |||
2564 | /* | 2713 | /* |
2565 | * Early initialization to allow access to registers in the very early boot | 2714 | * Early initialization to allow access to registers in the very early boot |
2566 | * process. | 2715 | * process. |
@@ -2623,6 +2772,9 @@ static int __init tegra_pmc_early_init(void) | |||
2623 | if (np) { | 2772 | if (np) { |
2624 | pmc->soc = match->data; | 2773 | pmc->soc = match->data; |
2625 | 2774 | ||
2775 | if (pmc->soc->maybe_tz_only) | ||
2776 | pmc->tz_only = tegra_pmc_detect_tz_only(pmc); | ||
2777 | |||
2626 | tegra_powergate_init(pmc, np); | 2778 | tegra_powergate_init(pmc, np); |
2627 | 2779 | ||
2628 | /* | 2780 | /* |
diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index e05ab16d9a9e..6285cd8efb21 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c | |||
@@ -598,7 +598,7 @@ static int pktdma_init_chan(struct knav_dma_device *dma, | |||
598 | 598 | ||
599 | INIT_LIST_HEAD(&chan->list); | 599 | INIT_LIST_HEAD(&chan->list); |
600 | chan->dma = dma; | 600 | chan->dma = dma; |
601 | chan->direction = DMA_NONE; | 601 | chan->direction = DMA_TRANS_NONE; |
602 | atomic_set(&chan->ref_count, 0); | 602 | atomic_set(&chan->ref_count, 0); |
603 | spin_lock_init(&chan->lock); | 603 | spin_lock_init(&chan->lock); |
604 | 604 | ||
diff --git a/drivers/soc/xilinx/Kconfig b/drivers/soc/xilinx/Kconfig index 687c8f3cd955..01e76b58dd78 100644 --- a/drivers/soc/xilinx/Kconfig +++ b/drivers/soc/xilinx/Kconfig | |||
@@ -17,4 +17,24 @@ config XILINX_VCU | |||
17 | To compile this driver as a module, choose M here: the | 17 | To compile this driver as a module, choose M here: the |
18 | module will be called xlnx_vcu. | 18 | module will be called xlnx_vcu. |
19 | 19 | ||
20 | config ZYNQMP_POWER | ||
21 | bool "Enable Xilinx Zynq MPSoC Power Management driver" | ||
22 | depends on PM && ARCH_ZYNQMP | ||
23 | default y | ||
24 | help | ||
25 | Say yes to enable power management support for ZyqnMP SoC. | ||
26 | This driver uses firmware driver as an interface for power | ||
27 | management request to firmware. It registers isr to handle | ||
28 | power management callbacks from firmware. | ||
29 | If in doubt, say N. | ||
30 | |||
31 | config ZYNQMP_PM_DOMAINS | ||
32 | bool "Enable Zynq MPSoC generic PM domains" | ||
33 | default y | ||
34 | depends on PM && ARCH_ZYNQMP && ZYNQMP_FIRMWARE | ||
35 | select PM_GENERIC_DOMAINS | ||
36 | help | ||
37 | Say yes to enable device power management through PM domains | ||
38 | If in doubt, say N. | ||
39 | |||
20 | endmenu | 40 | endmenu |
diff --git a/drivers/soc/xilinx/Makefile b/drivers/soc/xilinx/Makefile index dee8fd51e303..f66bfea5de17 100644 --- a/drivers/soc/xilinx/Makefile +++ b/drivers/soc/xilinx/Makefile | |||
@@ -1,2 +1,4 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
2 | obj-$(CONFIG_XILINX_VCU) += xlnx_vcu.o | 2 | obj-$(CONFIG_XILINX_VCU) += xlnx_vcu.o |
3 | obj-$(CONFIG_ZYNQMP_POWER) += zynqmp_power.o | ||
4 | obj-$(CONFIG_ZYNQMP_PM_DOMAINS) += zynqmp_pm_domains.o | ||
diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c new file mode 100644 index 000000000000..354d256e6e00 --- /dev/null +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c | |||
@@ -0,0 +1,321 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ZynqMP Generic PM domain support | ||
4 | * | ||
5 | * Copyright (C) 2015-2018 Xilinx, Inc. | ||
6 | * | ||
7 | * Davorin Mista <davorin.mista@aggios.com> | ||
8 | * Jolly Shah <jollys@xilinx.com> | ||
9 | * Rajan Vaja <rajan.vaja@xilinx.com> | ||
10 | */ | ||
11 | |||
12 | #include <linux/err.h> | ||
13 | #include <linux/list.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of_platform.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/pm_domain.h> | ||
18 | #include <linux/slab.h> | ||
19 | |||
20 | #include <linux/firmware/xlnx-zynqmp.h> | ||
21 | |||
22 | #define ZYNQMP_NUM_DOMAINS (100) | ||
23 | /* Flag stating if PM nodes mapped to the PM domain has been requested */ | ||
24 | #define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0) | ||
25 | |||
26 | /** | ||
27 | * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain | ||
28 | * @gpd: Generic power domain | ||
29 | * @node_id: PM node ID corresponding to device inside PM domain | ||
30 | * @flags: ZynqMP PM domain flags | ||
31 | */ | ||
32 | struct zynqmp_pm_domain { | ||
33 | struct generic_pm_domain gpd; | ||
34 | u32 node_id; | ||
35 | u8 flags; | ||
36 | }; | ||
37 | |||
38 | /** | ||
39 | * zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source | ||
40 | * path | ||
41 | * @dev: Device to check for wakeup source path | ||
42 | * @not_used: Data member (not required) | ||
43 | * | ||
44 | * This function is checks device's child hierarchy and checks if any device is | ||
45 | * set as wakeup source. | ||
46 | * | ||
47 | * Return: 1 if device is in wakeup source path else 0 | ||
48 | */ | ||
49 | static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used) | ||
50 | { | ||
51 | int may_wakeup; | ||
52 | |||
53 | may_wakeup = device_may_wakeup(dev); | ||
54 | if (may_wakeup) | ||
55 | return may_wakeup; | ||
56 | |||
57 | return device_for_each_child(dev, NULL, | ||
58 | zynqmp_gpd_is_active_wakeup_path); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * zynqmp_gpd_power_on() - Power on PM domain | ||
63 | * @domain: Generic PM domain | ||
64 | * | ||
65 | * This function is called before devices inside a PM domain are resumed, to | ||
66 | * power on PM domain. | ||
67 | * | ||
68 | * Return: 0 on success, error code otherwise | ||
69 | */ | ||
70 | static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) | ||
71 | { | ||
72 | int ret; | ||
73 | struct zynqmp_pm_domain *pd; | ||
74 | const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
75 | |||
76 | if (!eemi_ops || !eemi_ops->set_requirement) | ||
77 | return -ENXIO; | ||
78 | |||
79 | pd = container_of(domain, struct zynqmp_pm_domain, gpd); | ||
80 | ret = eemi_ops->set_requirement(pd->node_id, | ||
81 | ZYNQMP_PM_CAPABILITY_ACCESS, | ||
82 | ZYNQMP_PM_MAX_QOS, | ||
83 | ZYNQMP_PM_REQUEST_ACK_BLOCKING); | ||
84 | if (ret) { | ||
85 | pr_err("%s() %s set requirement for node %d failed: %d\n", | ||
86 | __func__, domain->name, pd->node_id, ret); | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | pr_debug("%s() Powered on %s domain\n", __func__, domain->name); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * zynqmp_gpd_power_off() - Power off PM domain | ||
96 | * @domain: Generic PM domain | ||
97 | * | ||
98 | * This function is called after devices inside a PM domain are suspended, to | ||
99 | * power off PM domain. | ||
100 | * | ||
101 | * Return: 0 on success, error code otherwise | ||
102 | */ | ||
103 | static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) | ||
104 | { | ||
105 | int ret; | ||
106 | struct pm_domain_data *pdd, *tmp; | ||
107 | struct zynqmp_pm_domain *pd; | ||
108 | u32 capabilities = 0; | ||
109 | bool may_wakeup; | ||
110 | const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
111 | |||
112 | if (!eemi_ops || !eemi_ops->set_requirement) | ||
113 | return -ENXIO; | ||
114 | |||
115 | pd = container_of(domain, struct zynqmp_pm_domain, gpd); | ||
116 | |||
117 | /* If domain is already released there is nothing to be done */ | ||
118 | if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED)) { | ||
119 | pr_debug("%s() %s domain is already released\n", | ||
120 | __func__, domain->name); | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | list_for_each_entry_safe(pdd, tmp, &domain->dev_list, list_node) { | ||
125 | /* If device is in wakeup path, set capability to WAKEUP */ | ||
126 | may_wakeup = zynqmp_gpd_is_active_wakeup_path(pdd->dev, NULL); | ||
127 | if (may_wakeup) { | ||
128 | dev_dbg(pdd->dev, "device is in wakeup path in %s\n", | ||
129 | domain->name); | ||
130 | capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP; | ||
131 | break; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | ret = eemi_ops->set_requirement(pd->node_id, capabilities, 0, | ||
136 | ZYNQMP_PM_REQUEST_ACK_NO); | ||
137 | /** | ||
138 | * If powering down of any node inside this domain fails, | ||
139 | * report and return the error | ||
140 | */ | ||
141 | if (ret) { | ||
142 | pr_err("%s() %s set requirement for node %d failed: %d\n", | ||
143 | __func__, domain->name, pd->node_id, ret); | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | pr_debug("%s() Powered off %s domain\n", __func__, domain->name); | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * zynqmp_gpd_attach_dev() - Attach device to the PM domain | ||
153 | * @domain: Generic PM domain | ||
154 | * @dev: Device to attach | ||
155 | * | ||
156 | * Return: 0 on success, error code otherwise | ||
157 | */ | ||
158 | static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, | ||
159 | struct device *dev) | ||
160 | { | ||
161 | int ret; | ||
162 | struct zynqmp_pm_domain *pd; | ||
163 | const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
164 | |||
165 | if (!eemi_ops || !eemi_ops->request_node) | ||
166 | return -ENXIO; | ||
167 | |||
168 | pd = container_of(domain, struct zynqmp_pm_domain, gpd); | ||
169 | |||
170 | /* If this is not the first device to attach there is nothing to do */ | ||
171 | if (domain->device_count) | ||
172 | return 0; | ||
173 | |||
174 | ret = eemi_ops->request_node(pd->node_id, 0, 0, | ||
175 | ZYNQMP_PM_REQUEST_ACK_BLOCKING); | ||
176 | /* If requesting a node fails print and return the error */ | ||
177 | if (ret) { | ||
178 | pr_err("%s() %s request failed for node %d: %d\n", | ||
179 | __func__, domain->name, pd->node_id, ret); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED; | ||
184 | |||
185 | pr_debug("%s() %s attached to %s domain\n", __func__, | ||
186 | dev_name(dev), domain->name); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * zynqmp_gpd_detach_dev() - Detach device from the PM domain | ||
192 | * @domain: Generic PM domain | ||
193 | * @dev: Device to detach | ||
194 | */ | ||
195 | static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, | ||
196 | struct device *dev) | ||
197 | { | ||
198 | int ret; | ||
199 | struct zynqmp_pm_domain *pd; | ||
200 | const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
201 | |||
202 | if (!eemi_ops || !eemi_ops->release_node) | ||
203 | return; | ||
204 | |||
205 | pd = container_of(domain, struct zynqmp_pm_domain, gpd); | ||
206 | |||
207 | /* If this is not the last device to detach there is nothing to do */ | ||
208 | if (domain->device_count) | ||
209 | return; | ||
210 | |||
211 | ret = eemi_ops->release_node(pd->node_id); | ||
212 | /* If releasing a node fails print the error and return */ | ||
213 | if (ret) { | ||
214 | pr_err("%s() %s release failed for node %d: %d\n", | ||
215 | __func__, domain->name, pd->node_id, ret); | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED; | ||
220 | |||
221 | pr_debug("%s() %s detached from %s domain\n", __func__, | ||
222 | dev_name(dev), domain->name); | ||
223 | } | ||
224 | |||
225 | static struct generic_pm_domain *zynqmp_gpd_xlate | ||
226 | (struct of_phandle_args *genpdspec, void *data) | ||
227 | { | ||
228 | struct genpd_onecell_data *genpd_data = data; | ||
229 | unsigned int i, idx = genpdspec->args[0]; | ||
230 | struct zynqmp_pm_domain *pd; | ||
231 | |||
232 | pd = container_of(genpd_data->domains[0], struct zynqmp_pm_domain, gpd); | ||
233 | |||
234 | if (genpdspec->args_count != 1) | ||
235 | return ERR_PTR(-EINVAL); | ||
236 | |||
237 | /* Check for existing pm domains */ | ||
238 | for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) { | ||
239 | if (pd[i].node_id == idx) | ||
240 | goto done; | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * Add index in empty node_id of power domain list as no existing | ||
245 | * power domain found for current index. | ||
246 | */ | ||
247 | for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) { | ||
248 | if (pd[i].node_id == 0) { | ||
249 | pd[i].node_id = idx; | ||
250 | break; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | done: | ||
255 | if (!genpd_data->domains[i] || i == ZYNQMP_NUM_DOMAINS) | ||
256 | return ERR_PTR(-ENOENT); | ||
257 | |||
258 | return genpd_data->domains[i]; | ||
259 | } | ||
260 | |||
261 | static int zynqmp_gpd_probe(struct platform_device *pdev) | ||
262 | { | ||
263 | int i; | ||
264 | struct genpd_onecell_data *zynqmp_pd_data; | ||
265 | struct generic_pm_domain **domains; | ||
266 | struct zynqmp_pm_domain *pd; | ||
267 | struct device *dev = &pdev->dev; | ||
268 | |||
269 | pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL); | ||
270 | if (!pd) | ||
271 | return -ENOMEM; | ||
272 | |||
273 | zynqmp_pd_data = devm_kzalloc(dev, sizeof(*zynqmp_pd_data), GFP_KERNEL); | ||
274 | if (!zynqmp_pd_data) | ||
275 | return -ENOMEM; | ||
276 | |||
277 | zynqmp_pd_data->xlate = zynqmp_gpd_xlate; | ||
278 | |||
279 | domains = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*domains), | ||
280 | GFP_KERNEL); | ||
281 | if (!domains) | ||
282 | return -ENOMEM; | ||
283 | |||
284 | for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) { | ||
285 | pd->node_id = 0; | ||
286 | pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i); | ||
287 | pd->gpd.power_off = zynqmp_gpd_power_off; | ||
288 | pd->gpd.power_on = zynqmp_gpd_power_on; | ||
289 | pd->gpd.attach_dev = zynqmp_gpd_attach_dev; | ||
290 | pd->gpd.detach_dev = zynqmp_gpd_detach_dev; | ||
291 | |||
292 | domains[i] = &pd->gpd; | ||
293 | |||
294 | /* Mark all PM domains as initially powered off */ | ||
295 | pm_genpd_init(&pd->gpd, NULL, true); | ||
296 | } | ||
297 | |||
298 | zynqmp_pd_data->domains = domains; | ||
299 | zynqmp_pd_data->num_domains = ZYNQMP_NUM_DOMAINS; | ||
300 | of_genpd_add_provider_onecell(dev->parent->of_node, zynqmp_pd_data); | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int zynqmp_gpd_remove(struct platform_device *pdev) | ||
306 | { | ||
307 | of_genpd_del_provider(pdev->dev.parent->of_node); | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static struct platform_driver zynqmp_power_domain_driver = { | ||
313 | .driver = { | ||
314 | .name = "zynqmp_power_controller", | ||
315 | }, | ||
316 | .probe = zynqmp_gpd_probe, | ||
317 | .remove = zynqmp_gpd_remove, | ||
318 | }; | ||
319 | module_platform_driver(zynqmp_power_domain_driver); | ||
320 | |||
321 | MODULE_ALIAS("platform:zynqmp_power_controller"); | ||
diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c new file mode 100644 index 000000000000..771cb59b9d22 --- /dev/null +++ b/drivers/soc/xilinx/zynqmp_power.c | |||
@@ -0,0 +1,178 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Xilinx Zynq MPSoC Power Management | ||
4 | * | ||
5 | * Copyright (C) 2014-2018 Xilinx, Inc. | ||
6 | * | ||
7 | * Davorin Mista <davorin.mista@aggios.com> | ||
8 | * Jolly Shah <jollys@xilinx.com> | ||
9 | * Rajan Vaja <rajan.vaja@xilinx.com> | ||
10 | */ | ||
11 | |||
12 | #include <linux/mailbox_client.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/reboot.h> | ||
16 | #include <linux/suspend.h> | ||
17 | |||
18 | #include <linux/firmware/xlnx-zynqmp.h> | ||
19 | |||
20 | enum pm_suspend_mode { | ||
21 | PM_SUSPEND_MODE_FIRST = 0, | ||
22 | PM_SUSPEND_MODE_STD = PM_SUSPEND_MODE_FIRST, | ||
23 | PM_SUSPEND_MODE_POWER_OFF, | ||
24 | }; | ||
25 | |||
26 | #define PM_SUSPEND_MODE_FIRST PM_SUSPEND_MODE_STD | ||
27 | |||
28 | static const char *const suspend_modes[] = { | ||
29 | [PM_SUSPEND_MODE_STD] = "standard", | ||
30 | [PM_SUSPEND_MODE_POWER_OFF] = "power-off", | ||
31 | }; | ||
32 | |||
33 | static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD; | ||
34 | |||
35 | enum pm_api_cb_id { | ||
36 | PM_INIT_SUSPEND_CB = 30, | ||
37 | PM_ACKNOWLEDGE_CB, | ||
38 | PM_NOTIFY_CB, | ||
39 | }; | ||
40 | |||
41 | static void zynqmp_pm_get_callback_data(u32 *buf) | ||
42 | { | ||
43 | zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); | ||
44 | } | ||
45 | |||
46 | static irqreturn_t zynqmp_pm_isr(int irq, void *data) | ||
47 | { | ||
48 | u32 payload[CB_PAYLOAD_SIZE]; | ||
49 | |||
50 | zynqmp_pm_get_callback_data(payload); | ||
51 | |||
52 | /* First element is callback API ID, others are callback arguments */ | ||
53 | if (payload[0] == PM_INIT_SUSPEND_CB) { | ||
54 | switch (payload[1]) { | ||
55 | case SUSPEND_SYSTEM_SHUTDOWN: | ||
56 | orderly_poweroff(true); | ||
57 | break; | ||
58 | case SUSPEND_POWER_REQUEST: | ||
59 | pm_suspend(PM_SUSPEND_MEM); | ||
60 | break; | ||
61 | default: | ||
62 | pr_err("%s Unsupported InitSuspendCb reason " | ||
63 | "code %d\n", __func__, payload[1]); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | return IRQ_HANDLED; | ||
68 | } | ||
69 | |||
70 | static ssize_t suspend_mode_show(struct device *dev, | ||
71 | struct device_attribute *attr, char *buf) | ||
72 | { | ||
73 | char *s = buf; | ||
74 | int md; | ||
75 | |||
76 | for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++) | ||
77 | if (suspend_modes[md]) { | ||
78 | if (md == suspend_mode) | ||
79 | s += sprintf(s, "[%s] ", suspend_modes[md]); | ||
80 | else | ||
81 | s += sprintf(s, "%s ", suspend_modes[md]); | ||
82 | } | ||
83 | |||
84 | /* Convert last space to newline */ | ||
85 | if (s != buf) | ||
86 | *(s - 1) = '\n'; | ||
87 | return (s - buf); | ||
88 | } | ||
89 | |||
90 | static ssize_t suspend_mode_store(struct device *dev, | ||
91 | struct device_attribute *attr, | ||
92 | const char *buf, size_t count) | ||
93 | { | ||
94 | int md, ret = -EINVAL; | ||
95 | const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
96 | |||
97 | if (!eemi_ops || !eemi_ops->set_suspend_mode) | ||
98 | return ret; | ||
99 | |||
100 | for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++) | ||
101 | if (suspend_modes[md] && | ||
102 | sysfs_streq(suspend_modes[md], buf)) { | ||
103 | ret = 0; | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | if (!ret && md != suspend_mode) { | ||
108 | ret = eemi_ops->set_suspend_mode(md); | ||
109 | if (likely(!ret)) | ||
110 | suspend_mode = md; | ||
111 | } | ||
112 | |||
113 | return ret ? ret : count; | ||
114 | } | ||
115 | |||
116 | static DEVICE_ATTR_RW(suspend_mode); | ||
117 | |||
118 | static int zynqmp_pm_probe(struct platform_device *pdev) | ||
119 | { | ||
120 | int ret, irq; | ||
121 | u32 pm_api_version; | ||
122 | |||
123 | const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); | ||
124 | |||
125 | if (!eemi_ops || !eemi_ops->get_api_version || !eemi_ops->init_finalize) | ||
126 | return -ENXIO; | ||
127 | |||
128 | eemi_ops->init_finalize(); | ||
129 | eemi_ops->get_api_version(&pm_api_version); | ||
130 | |||
131 | /* Check PM API version number */ | ||
132 | if (pm_api_version < ZYNQMP_PM_VERSION) | ||
133 | return -ENODEV; | ||
134 | |||
135 | irq = platform_get_irq(pdev, 0); | ||
136 | if (irq <= 0) | ||
137 | return -ENXIO; | ||
138 | |||
139 | ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, zynqmp_pm_isr, | ||
140 | IRQF_NO_SUSPEND | IRQF_ONESHOT, | ||
141 | dev_name(&pdev->dev), &pdev->dev); | ||
142 | if (ret) { | ||
143 | dev_err(&pdev->dev, "devm_request_threaded_irq '%d' failed " | ||
144 | "with %d\n", irq, ret); | ||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); | ||
149 | if (ret) { | ||
150 | dev_err(&pdev->dev, "unable to create sysfs interface\n"); | ||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int zynqmp_pm_remove(struct platform_device *pdev) | ||
158 | { | ||
159 | sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static const struct of_device_id pm_of_match[] = { | ||
165 | { .compatible = "xlnx,zynqmp-power", }, | ||
166 | { /* end of table */ }, | ||
167 | }; | ||
168 | MODULE_DEVICE_TABLE(of, pm_of_match); | ||
169 | |||
170 | static struct platform_driver zynqmp_pm_platform_driver = { | ||
171 | .probe = zynqmp_pm_probe, | ||
172 | .remove = zynqmp_pm_remove, | ||
173 | .driver = { | ||
174 | .name = "zynqmp_power", | ||
175 | .of_match_table = pm_of_match, | ||
176 | }, | ||
177 | }; | ||
178 | module_platform_driver(zynqmp_pm_platform_driver); | ||
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile index 48d262ae2f04..56263ae3b1d7 100644 --- a/drivers/tee/optee/Makefile +++ b/drivers/tee/optee/Makefile | |||
@@ -5,3 +5,4 @@ optee-objs += call.o | |||
5 | optee-objs += rpc.o | 5 | optee-objs += rpc.o |
6 | optee-objs += supp.o | 6 | optee-objs += supp.o |
7 | optee-objs += shm_pool.o | 7 | optee-objs += shm_pool.o |
8 | optee-objs += device.o | ||
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 947f9b28de9e..0842b6e6af82 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c | |||
@@ -634,6 +634,10 @@ static struct optee *optee_probe(struct device_node *np) | |||
634 | if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) | 634 | if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) |
635 | pr_info("dynamic shared memory is enabled\n"); | 635 | pr_info("dynamic shared memory is enabled\n"); |
636 | 636 | ||
637 | rc = optee_enumerate_devices(); | ||
638 | if (rc) | ||
639 | goto err; | ||
640 | |||
637 | pr_info("initialized driver\n"); | 641 | pr_info("initialized driver\n"); |
638 | return optee; | 642 | return optee; |
639 | err: | 643 | err: |
diff --git a/drivers/tee/optee/device.c b/drivers/tee/optee/device.c new file mode 100644 index 000000000000..e3a148521ec1 --- /dev/null +++ b/drivers/tee/optee/device.c | |||
@@ -0,0 +1,160 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2019 Linaro Ltd. | ||
4 | */ | ||
5 | |||
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
7 | |||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/tee_drv.h> | ||
11 | #include <linux/uuid.h> | ||
12 | #include "optee_private.h" | ||
13 | |||
14 | /* | ||
15 | * Get device UUIDs | ||
16 | * | ||
17 | * [out] memref[0] Array of device UUIDs | ||
18 | * | ||
19 | * Return codes: | ||
20 | * TEE_SUCCESS - Invoke command success | ||
21 | * TEE_ERROR_BAD_PARAMETERS - Incorrect input param | ||
22 | * TEE_ERROR_SHORT_BUFFER - Output buffer size less than required | ||
23 | */ | ||
24 | #define PTA_CMD_GET_DEVICES 0x0 | ||
25 | |||
26 | static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) | ||
27 | { | ||
28 | if (ver->impl_id == TEE_IMPL_ID_OPTEE) | ||
29 | return 1; | ||
30 | else | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | static int get_devices(struct tee_context *ctx, u32 session, | ||
35 | struct tee_shm *device_shm, u32 *shm_size) | ||
36 | { | ||
37 | int ret = 0; | ||
38 | struct tee_ioctl_invoke_arg inv_arg; | ||
39 | struct tee_param param[4]; | ||
40 | |||
41 | memset(&inv_arg, 0, sizeof(inv_arg)); | ||
42 | memset(¶m, 0, sizeof(param)); | ||
43 | |||
44 | /* Invoke PTA_CMD_GET_DEVICES function */ | ||
45 | inv_arg.func = PTA_CMD_GET_DEVICES; | ||
46 | inv_arg.session = session; | ||
47 | inv_arg.num_params = 4; | ||
48 | |||
49 | /* Fill invoke cmd params */ | ||
50 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; | ||
51 | param[0].u.memref.shm = device_shm; | ||
52 | param[0].u.memref.size = *shm_size; | ||
53 | param[0].u.memref.shm_offs = 0; | ||
54 | |||
55 | ret = tee_client_invoke_func(ctx, &inv_arg, param); | ||
56 | if ((ret < 0) || ((inv_arg.ret != TEEC_SUCCESS) && | ||
57 | (inv_arg.ret != TEEC_ERROR_SHORT_BUFFER))) { | ||
58 | pr_err("PTA_CMD_GET_DEVICES invoke function err: %x\n", | ||
59 | inv_arg.ret); | ||
60 | return -EINVAL; | ||
61 | } | ||
62 | |||
63 | *shm_size = param[0].u.memref.size; | ||
64 | |||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static int optee_register_device(const uuid_t *device_uuid, u32 device_id) | ||
69 | { | ||
70 | struct tee_client_device *optee_device = NULL; | ||
71 | int rc; | ||
72 | |||
73 | optee_device = kzalloc(sizeof(*optee_device), GFP_KERNEL); | ||
74 | if (!optee_device) | ||
75 | return -ENOMEM; | ||
76 | |||
77 | optee_device->dev.bus = &tee_bus_type; | ||
78 | dev_set_name(&optee_device->dev, "optee-clnt%u", device_id); | ||
79 | uuid_copy(&optee_device->id.uuid, device_uuid); | ||
80 | |||
81 | rc = device_register(&optee_device->dev); | ||
82 | if (rc) { | ||
83 | pr_err("device registration failed, err: %d\n", rc); | ||
84 | kfree(optee_device); | ||
85 | } | ||
86 | |||
87 | return rc; | ||
88 | } | ||
89 | |||
90 | int optee_enumerate_devices(void) | ||
91 | { | ||
92 | const uuid_t pta_uuid = | ||
93 | UUID_INIT(0x7011a688, 0xddde, 0x4053, | ||
94 | 0xa5, 0xa9, 0x7b, 0x3c, 0x4d, 0xdf, 0x13, 0xb8); | ||
95 | struct tee_ioctl_open_session_arg sess_arg; | ||
96 | struct tee_shm *device_shm = NULL; | ||
97 | const uuid_t *device_uuid = NULL; | ||
98 | struct tee_context *ctx = NULL; | ||
99 | u32 shm_size = 0, idx, num_devices = 0; | ||
100 | int rc; | ||
101 | |||
102 | memset(&sess_arg, 0, sizeof(sess_arg)); | ||
103 | |||
104 | /* Open context with OP-TEE driver */ | ||
105 | ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); | ||
106 | if (IS_ERR(ctx)) | ||
107 | return -ENODEV; | ||
108 | |||
109 | /* Open session with device enumeration pseudo TA */ | ||
110 | memcpy(sess_arg.uuid, pta_uuid.b, TEE_IOCTL_UUID_LEN); | ||
111 | sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; | ||
112 | sess_arg.num_params = 0; | ||
113 | |||
114 | rc = tee_client_open_session(ctx, &sess_arg, NULL); | ||
115 | if ((rc < 0) || (sess_arg.ret != TEEC_SUCCESS)) { | ||
116 | /* Device enumeration pseudo TA not found */ | ||
117 | rc = 0; | ||
118 | goto out_ctx; | ||
119 | } | ||
120 | |||
121 | rc = get_devices(ctx, sess_arg.session, NULL, &shm_size); | ||
122 | if (rc < 0 || !shm_size) | ||
123 | goto out_sess; | ||
124 | |||
125 | device_shm = tee_shm_alloc(ctx, shm_size, | ||
126 | TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); | ||
127 | if (IS_ERR(device_shm)) { | ||
128 | pr_err("tee_shm_alloc failed\n"); | ||
129 | rc = PTR_ERR(device_shm); | ||
130 | goto out_sess; | ||
131 | } | ||
132 | |||
133 | rc = get_devices(ctx, sess_arg.session, device_shm, &shm_size); | ||
134 | if (rc < 0) | ||
135 | goto out_shm; | ||
136 | |||
137 | device_uuid = tee_shm_get_va(device_shm, 0); | ||
138 | if (IS_ERR(device_uuid)) { | ||
139 | pr_err("tee_shm_get_va failed\n"); | ||
140 | rc = PTR_ERR(device_uuid); | ||
141 | goto out_shm; | ||
142 | } | ||
143 | |||
144 | num_devices = shm_size / sizeof(uuid_t); | ||
145 | |||
146 | for (idx = 0; idx < num_devices; idx++) { | ||
147 | rc = optee_register_device(&device_uuid[idx], idx); | ||
148 | if (rc) | ||
149 | goto out_shm; | ||
150 | } | ||
151 | |||
152 | out_shm: | ||
153 | tee_shm_free(device_shm); | ||
154 | out_sess: | ||
155 | tee_client_close_session(ctx, sess_arg.session); | ||
156 | out_ctx: | ||
157 | tee_client_close_context(ctx); | ||
158 | |||
159 | return rc; | ||
160 | } | ||
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 30504901be80..795bc19ae17a 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h | |||
@@ -1,28 +1,6 @@ | |||
1 | /* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ | ||
1 | /* | 2 | /* |
2 | * Copyright (c) 2015-2016, Linaro Limited | 3 | * Copyright (c) 2015-2019, Linaro Limited |
3 | * All rights reserved. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * | ||
8 | * 1. Redistributions of source code must retain the above copyright notice, | ||
9 | * this list of conditions and the following disclaimer. | ||
10 | * | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright notice, | ||
12 | * this list of conditions and the following disclaimer in the documentation | ||
13 | * and/or other materials provided with the distribution. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
25 | * POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | 4 | */ |
27 | #ifndef _OPTEE_MSG_H | 5 | #ifndef _OPTEE_MSG_H |
28 | #define _OPTEE_MSG_H | 6 | #define _OPTEE_MSG_H |
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 35e79386c556..a5e84afd5013 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h | |||
@@ -28,6 +28,7 @@ | |||
28 | #define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 | 28 | #define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 |
29 | #define TEEC_ERROR_COMMUNICATION 0xFFFF000E | 29 | #define TEEC_ERROR_COMMUNICATION 0xFFFF000E |
30 | #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C | 30 | #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C |
31 | #define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010 | ||
31 | 32 | ||
32 | #define TEEC_ORIGIN_COMMS 0x00000002 | 33 | #define TEEC_ORIGIN_COMMS 0x00000002 |
33 | 34 | ||
@@ -181,6 +182,8 @@ void optee_free_pages_list(void *array, size_t num_entries); | |||
181 | void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, | 182 | void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, |
182 | size_t page_offset); | 183 | size_t page_offset); |
183 | 184 | ||
185 | int optee_enumerate_devices(void); | ||
186 | |||
184 | /* | 187 | /* |
185 | * Small helpers | 188 | * Small helpers |
186 | */ | 189 | */ |
diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index bbf0cf028c16..c72122d9c997 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h | |||
@@ -1,28 +1,6 @@ | |||
1 | /* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ | ||
1 | /* | 2 | /* |
2 | * Copyright (c) 2015-2016, Linaro Limited | 3 | * Copyright (c) 2015-2019, Linaro Limited |
3 | * All rights reserved. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * | ||
8 | * 1. Redistributions of source code must retain the above copyright notice, | ||
9 | * this list of conditions and the following disclaimer. | ||
10 | * | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright notice, | ||
12 | * this list of conditions and the following disclaimer in the documentation | ||
13 | * and/or other materials provided with the distribution. | ||
14 | * | ||
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
20 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
21 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
24 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
25 | * POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | 4 | */ |
27 | #ifndef OPTEE_SMC_H | 5 | #ifndef OPTEE_SMC_H |
28 | #define OPTEE_SMC_H | 6 | #define OPTEE_SMC_H |
diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index 43626e15703a..92f56b8645e3 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c | |||
@@ -88,10 +88,18 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, | |||
88 | { | 88 | { |
89 | struct optee *optee = tee_get_drvdata(ctx->teedev); | 89 | struct optee *optee = tee_get_drvdata(ctx->teedev); |
90 | struct optee_supp *supp = &optee->supp; | 90 | struct optee_supp *supp = &optee->supp; |
91 | struct optee_supp_req *req = kzalloc(sizeof(*req), GFP_KERNEL); | 91 | struct optee_supp_req *req; |
92 | bool interruptable; | 92 | bool interruptable; |
93 | u32 ret; | 93 | u32 ret; |
94 | 94 | ||
95 | /* | ||
96 | * Return in case there is no supplicant available and | ||
97 | * non-blocking request. | ||
98 | */ | ||
99 | if (!supp->ctx && ctx->supp_nowait) | ||
100 | return TEEC_ERROR_COMMUNICATION; | ||
101 | |||
102 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
95 | if (!req) | 103 | if (!req) |
96 | return TEEC_ERROR_OUT_OF_MEMORY; | 104 | return TEEC_ERROR_OUT_OF_MEMORY; |
97 | 105 | ||
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 7b2bb4c50058..17c64fccbb10 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c | |||
@@ -15,7 +15,6 @@ | |||
15 | #define pr_fmt(fmt) "%s: " fmt, __func__ | 15 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
16 | 16 | ||
17 | #include <linux/cdev.h> | 17 | #include <linux/cdev.h> |
18 | #include <linux/device.h> | ||
19 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
20 | #include <linux/idr.h> | 19 | #include <linux/idr.h> |
21 | #include <linux/module.h> | 20 | #include <linux/module.h> |
@@ -106,6 +105,11 @@ static int tee_open(struct inode *inode, struct file *filp) | |||
106 | if (IS_ERR(ctx)) | 105 | if (IS_ERR(ctx)) |
107 | return PTR_ERR(ctx); | 106 | return PTR_ERR(ctx); |
108 | 107 | ||
108 | /* | ||
109 | * Default user-space behaviour is to wait for tee-supplicant | ||
110 | * if not present for any requests in this context. | ||
111 | */ | ||
112 | ctx->supp_nowait = false; | ||
109 | filp->private_data = ctx; | 113 | filp->private_data = ctx; |
110 | return 0; | 114 | return 0; |
111 | } | 115 | } |
@@ -982,6 +986,16 @@ tee_client_open_context(struct tee_context *start, | |||
982 | } while (IS_ERR(ctx) && PTR_ERR(ctx) != -ENOMEM); | 986 | } while (IS_ERR(ctx) && PTR_ERR(ctx) != -ENOMEM); |
983 | 987 | ||
984 | put_device(put_dev); | 988 | put_device(put_dev); |
989 | /* | ||
990 | * Default behaviour for in kernel client is to not wait for | ||
991 | * tee-supplicant if not present for any requests in this context. | ||
992 | * Also this flag could be configured again before call to | ||
993 | * tee_client_open_session() if any in kernel client requires | ||
994 | * different behaviour. | ||
995 | */ | ||
996 | if (!IS_ERR(ctx)) | ||
997 | ctx->supp_nowait = true; | ||
998 | |||
985 | return ctx; | 999 | return ctx; |
986 | } | 1000 | } |
987 | EXPORT_SYMBOL_GPL(tee_client_open_context); | 1001 | EXPORT_SYMBOL_GPL(tee_client_open_context); |
@@ -1027,6 +1041,48 @@ int tee_client_invoke_func(struct tee_context *ctx, | |||
1027 | } | 1041 | } |
1028 | EXPORT_SYMBOL_GPL(tee_client_invoke_func); | 1042 | EXPORT_SYMBOL_GPL(tee_client_invoke_func); |
1029 | 1043 | ||
1044 | int tee_client_cancel_req(struct tee_context *ctx, | ||
1045 | struct tee_ioctl_cancel_arg *arg) | ||
1046 | { | ||
1047 | if (!ctx->teedev->desc->ops->cancel_req) | ||
1048 | return -EINVAL; | ||
1049 | return ctx->teedev->desc->ops->cancel_req(ctx, arg->cancel_id, | ||
1050 | arg->session); | ||
1051 | } | ||
1052 | |||
1053 | static int tee_client_device_match(struct device *dev, | ||
1054 | struct device_driver *drv) | ||
1055 | { | ||
1056 | const struct tee_client_device_id *id_table; | ||
1057 | struct tee_client_device *tee_device; | ||
1058 | |||
1059 | id_table = to_tee_client_driver(drv)->id_table; | ||
1060 | tee_device = to_tee_client_device(dev); | ||
1061 | |||
1062 | while (!uuid_is_null(&id_table->uuid)) { | ||
1063 | if (uuid_equal(&tee_device->id.uuid, &id_table->uuid)) | ||
1064 | return 1; | ||
1065 | id_table++; | ||
1066 | } | ||
1067 | |||
1068 | return 0; | ||
1069 | } | ||
1070 | |||
1071 | static int tee_client_device_uevent(struct device *dev, | ||
1072 | struct kobj_uevent_env *env) | ||
1073 | { | ||
1074 | uuid_t *dev_id = &to_tee_client_device(dev)->id.uuid; | ||
1075 | |||
1076 | return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id); | ||
1077 | } | ||
1078 | |||
1079 | struct bus_type tee_bus_type = { | ||
1080 | .name = "tee", | ||
1081 | .match = tee_client_device_match, | ||
1082 | .uevent = tee_client_device_uevent, | ||
1083 | }; | ||
1084 | EXPORT_SYMBOL_GPL(tee_bus_type); | ||
1085 | |||
1030 | static int __init tee_init(void) | 1086 | static int __init tee_init(void) |
1031 | { | 1087 | { |
1032 | int rc; | 1088 | int rc; |
@@ -1040,18 +1096,32 @@ static int __init tee_init(void) | |||
1040 | rc = alloc_chrdev_region(&tee_devt, 0, TEE_NUM_DEVICES, "tee"); | 1096 | rc = alloc_chrdev_region(&tee_devt, 0, TEE_NUM_DEVICES, "tee"); |
1041 | if (rc) { | 1097 | if (rc) { |
1042 | pr_err("failed to allocate char dev region\n"); | 1098 | pr_err("failed to allocate char dev region\n"); |
1043 | class_destroy(tee_class); | 1099 | goto out_unreg_class; |
1044 | tee_class = NULL; | 1100 | } |
1101 | |||
1102 | rc = bus_register(&tee_bus_type); | ||
1103 | if (rc) { | ||
1104 | pr_err("failed to register tee bus\n"); | ||
1105 | goto out_unreg_chrdev; | ||
1045 | } | 1106 | } |
1046 | 1107 | ||
1108 | return 0; | ||
1109 | |||
1110 | out_unreg_chrdev: | ||
1111 | unregister_chrdev_region(tee_devt, TEE_NUM_DEVICES); | ||
1112 | out_unreg_class: | ||
1113 | class_destroy(tee_class); | ||
1114 | tee_class = NULL; | ||
1115 | |||
1047 | return rc; | 1116 | return rc; |
1048 | } | 1117 | } |
1049 | 1118 | ||
1050 | static void __exit tee_exit(void) | 1119 | static void __exit tee_exit(void) |
1051 | { | 1120 | { |
1121 | bus_unregister(&tee_bus_type); | ||
1122 | unregister_chrdev_region(tee_devt, TEE_NUM_DEVICES); | ||
1052 | class_destroy(tee_class); | 1123 | class_destroy(tee_class); |
1053 | tee_class = NULL; | 1124 | tee_class = NULL; |
1054 | unregister_chrdev_region(tee_devt, TEE_NUM_DEVICES); | ||
1055 | } | 1125 | } |
1056 | 1126 | ||
1057 | subsys_initcall(tee_init); | 1127 | subsys_initcall(tee_init); |
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index ed05514cc2dc..1834524ae373 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c | |||
@@ -12,6 +12,7 @@ | |||
12 | 12 | ||
13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <linux/types.h> | 14 | #include <linux/types.h> |
15 | #include <linux/mfd/bcm2835-pm.h> | ||
15 | #include <linux/module.h> | 16 | #include <linux/module.h> |
16 | #include <linux/io.h> | 17 | #include <linux/io.h> |
17 | #include <linux/watchdog.h> | 18 | #include <linux/watchdog.h> |
@@ -47,6 +48,8 @@ struct bcm2835_wdt { | |||
47 | spinlock_t lock; | 48 | spinlock_t lock; |
48 | }; | 49 | }; |
49 | 50 | ||
51 | static struct bcm2835_wdt *bcm2835_power_off_wdt; | ||
52 | |||
50 | static unsigned int heartbeat; | 53 | static unsigned int heartbeat; |
51 | static bool nowayout = WATCHDOG_NOWAYOUT; | 54 | static bool nowayout = WATCHDOG_NOWAYOUT; |
52 | 55 | ||
@@ -148,10 +151,7 @@ static struct watchdog_device bcm2835_wdt_wdd = { | |||
148 | */ | 151 | */ |
149 | static void bcm2835_power_off(void) | 152 | static void bcm2835_power_off(void) |
150 | { | 153 | { |
151 | struct device_node *np = | 154 | struct bcm2835_wdt *wdt = bcm2835_power_off_wdt; |
152 | of_find_compatible_node(NULL, NULL, "brcm,bcm2835-pm-wdt"); | ||
153 | struct platform_device *pdev = of_find_device_by_node(np); | ||
154 | struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); | ||
155 | u32 val; | 155 | u32 val; |
156 | 156 | ||
157 | /* | 157 | /* |
@@ -169,7 +169,7 @@ static void bcm2835_power_off(void) | |||
169 | 169 | ||
170 | static int bcm2835_wdt_probe(struct platform_device *pdev) | 170 | static int bcm2835_wdt_probe(struct platform_device *pdev) |
171 | { | 171 | { |
172 | struct resource *res; | 172 | struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent); |
173 | struct device *dev = &pdev->dev; | 173 | struct device *dev = &pdev->dev; |
174 | struct bcm2835_wdt *wdt; | 174 | struct bcm2835_wdt *wdt; |
175 | int err; | 175 | int err; |
@@ -181,10 +181,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) | |||
181 | 181 | ||
182 | spin_lock_init(&wdt->lock); | 182 | spin_lock_init(&wdt->lock); |
183 | 183 | ||
184 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 184 | wdt->base = pm->base; |
185 | wdt->base = devm_ioremap_resource(dev, res); | ||
186 | if (IS_ERR(wdt->base)) | ||
187 | return PTR_ERR(wdt->base); | ||
188 | 185 | ||
189 | watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt); | 186 | watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt); |
190 | watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev); | 187 | watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev); |
@@ -211,8 +208,10 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) | |||
211 | return err; | 208 | return err; |
212 | } | 209 | } |
213 | 210 | ||
214 | if (pm_power_off == NULL) | 211 | if (pm_power_off == NULL) { |
215 | pm_power_off = bcm2835_power_off; | 212 | pm_power_off = bcm2835_power_off; |
213 | bcm2835_power_off_wdt = wdt; | ||
214 | } | ||
216 | 215 | ||
217 | dev_info(dev, "Broadcom BCM2835 watchdog timer"); | 216 | dev_info(dev, "Broadcom BCM2835 watchdog timer"); |
218 | return 0; | 217 | return 0; |
@@ -226,18 +225,11 @@ static int bcm2835_wdt_remove(struct platform_device *pdev) | |||
226 | return 0; | 225 | return 0; |
227 | } | 226 | } |
228 | 227 | ||
229 | static const struct of_device_id bcm2835_wdt_of_match[] = { | ||
230 | { .compatible = "brcm,bcm2835-pm-wdt", }, | ||
231 | {}, | ||
232 | }; | ||
233 | MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match); | ||
234 | |||
235 | static struct platform_driver bcm2835_wdt_driver = { | 228 | static struct platform_driver bcm2835_wdt_driver = { |
236 | .probe = bcm2835_wdt_probe, | 229 | .probe = bcm2835_wdt_probe, |
237 | .remove = bcm2835_wdt_remove, | 230 | .remove = bcm2835_wdt_remove, |
238 | .driver = { | 231 | .driver = { |
239 | .name = "bcm2835-wdt", | 232 | .name = "bcm2835-wdt", |
240 | .of_match_table = bcm2835_wdt_of_match, | ||
241 | }, | 233 | }, |
242 | }; | 234 | }; |
243 | module_platform_driver(bcm2835_wdt_driver); | 235 | module_platform_driver(bcm2835_wdt_driver); |
diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h new file mode 100644 index 000000000000..87d9c6611682 --- /dev/null +++ b/include/dt-bindings/power/qcom-rpmpd.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* Copyright (c) 2018, The Linux Foundation. All rights reserved. */ | ||
3 | |||
4 | #ifndef _DT_BINDINGS_POWER_QCOM_RPMPD_H | ||
5 | #define _DT_BINDINGS_POWER_QCOM_RPMPD_H | ||
6 | |||
7 | /* SDM845 Power Domain Indexes */ | ||
8 | #define SDM845_EBI 0 | ||
9 | #define SDM845_MX 1 | ||
10 | #define SDM845_MX_AO 2 | ||
11 | #define SDM845_CX 3 | ||
12 | #define SDM845_CX_AO 4 | ||
13 | #define SDM845_LMX 5 | ||
14 | #define SDM845_LCX 6 | ||
15 | #define SDM845_GFX 7 | ||
16 | #define SDM845_MSS 8 | ||
17 | |||
18 | /* SDM845 Power Domain performance levels */ | ||
19 | #define RPMH_REGULATOR_LEVEL_RETENTION 16 | ||
20 | #define RPMH_REGULATOR_LEVEL_MIN_SVS 48 | ||
21 | #define RPMH_REGULATOR_LEVEL_LOW_SVS 64 | ||
22 | #define RPMH_REGULATOR_LEVEL_SVS 128 | ||
23 | #define RPMH_REGULATOR_LEVEL_SVS_L1 192 | ||
24 | #define RPMH_REGULATOR_LEVEL_NOM 256 | ||
25 | #define RPMH_REGULATOR_LEVEL_NOM_L1 320 | ||
26 | #define RPMH_REGULATOR_LEVEL_NOM_L2 336 | ||
27 | #define RPMH_REGULATOR_LEVEL_TURBO 384 | ||
28 | #define RPMH_REGULATOR_LEVEL_TURBO_L1 416 | ||
29 | |||
30 | /* MSM8996 Power Domain Indexes */ | ||
31 | #define MSM8996_VDDCX 0 | ||
32 | #define MSM8996_VDDCX_AO 1 | ||
33 | #define MSM8996_VDDCX_VFC 2 | ||
34 | #define MSM8996_VDDMX 3 | ||
35 | #define MSM8996_VDDMX_AO 4 | ||
36 | #define MSM8996_VDDSSCX 5 | ||
37 | #define MSM8996_VDDSSCX_VFC 6 | ||
38 | |||
39 | #endif | ||
diff --git a/include/dt-bindings/power/xlnx-zynqmp-power.h b/include/dt-bindings/power/xlnx-zynqmp-power.h new file mode 100644 index 000000000000..0d9a412fd5e0 --- /dev/null +++ b/include/dt-bindings/power/xlnx-zynqmp-power.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (C) 2018 Xilinx, Inc. | ||
4 | */ | ||
5 | |||
6 | #ifndef _DT_BINDINGS_ZYNQMP_POWER_H | ||
7 | #define _DT_BINDINGS_ZYNQMP_POWER_H | ||
8 | |||
9 | #define PD_USB_0 22 | ||
10 | #define PD_USB_1 23 | ||
11 | #define PD_TTC_0 24 | ||
12 | #define PD_TTC_1 25 | ||
13 | #define PD_TTC_2 26 | ||
14 | #define PD_TTC_3 27 | ||
15 | #define PD_SATA 28 | ||
16 | #define PD_ETH_0 29 | ||
17 | #define PD_ETH_1 30 | ||
18 | #define PD_ETH_2 31 | ||
19 | #define PD_ETH_3 32 | ||
20 | #define PD_UART_0 33 | ||
21 | #define PD_UART_1 34 | ||
22 | #define PD_SPI_0 35 | ||
23 | #define PD_SPI_1 36 | ||
24 | #define PD_I2C_0 37 | ||
25 | #define PD_I2C_1 38 | ||
26 | #define PD_SD_0 39 | ||
27 | #define PD_SD_1 40 | ||
28 | #define PD_DP 41 | ||
29 | #define PD_GDMA 42 | ||
30 | #define PD_ADMA 43 | ||
31 | #define PD_NAND 44 | ||
32 | #define PD_QSPI 45 | ||
33 | #define PD_GPIO 46 | ||
34 | #define PD_CAN_0 47 | ||
35 | #define PD_CAN_1 48 | ||
36 | #define PD_GPU 58 | ||
37 | #define PD_PCIE 59 | ||
38 | |||
39 | #endif | ||
diff --git a/include/dt-bindings/reset/amlogic,meson-g12a-reset.h b/include/dt-bindings/reset/amlogic,meson-g12a-reset.h new file mode 100644 index 000000000000..8063e8314eef --- /dev/null +++ b/include/dt-bindings/reset/amlogic,meson-g12a-reset.h | |||
@@ -0,0 +1,134 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ | ||
2 | /* | ||
3 | * Copyright (c) 2019 BayLibre, SAS. | ||
4 | * Author: Jerome Brunet <jbrunet@baylibre.com> | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #ifndef _DT_BINDINGS_AMLOGIC_MESON_G12A_RESET_H | ||
9 | #define _DT_BINDINGS_AMLOGIC_MESON_G12A_RESET_H | ||
10 | |||
11 | /* RESET0 */ | ||
12 | #define RESET_HIU 0 | ||
13 | /* 1 */ | ||
14 | #define RESET_DOS 2 | ||
15 | /* 3-4 */ | ||
16 | #define RESET_VIU 5 | ||
17 | #define RESET_AFIFO 6 | ||
18 | #define RESET_VID_PLL_DIV 7 | ||
19 | /* 8-9 */ | ||
20 | #define RESET_VENC 10 | ||
21 | #define RESET_ASSIST 11 | ||
22 | #define RESET_PCIE_CTRL_A 12 | ||
23 | #define RESET_VCBUS 13 | ||
24 | #define RESET_PCIE_PHY 14 | ||
25 | #define RESET_PCIE_APB 15 | ||
26 | #define RESET_GIC 16 | ||
27 | #define RESET_CAPB3_DECODE 17 | ||
28 | /* 18 */ | ||
29 | #define RESET_HDMITX_CAPB3 19 | ||
30 | #define RESET_DVALIN_CAPB3 20 | ||
31 | #define RESET_DOS_CAPB3 21 | ||
32 | /* 22 */ | ||
33 | #define RESET_CBUS_CAPB3 23 | ||
34 | #define RESET_AHB_CNTL 24 | ||
35 | #define RESET_AHB_DATA 25 | ||
36 | #define RESET_VCBUS_CLK81 26 | ||
37 | /* 27-31 */ | ||
38 | /* RESET1 */ | ||
39 | /* 32 */ | ||
40 | #define RESET_DEMUX 33 | ||
41 | #define RESET_USB 34 | ||
42 | #define RESET_DDR 35 | ||
43 | /* 36 */ | ||
44 | #define RESET_BT656 37 | ||
45 | #define RESET_AHB_SRAM 38 | ||
46 | /* 39 */ | ||
47 | #define RESET_PARSER 40 | ||
48 | /* 41 */ | ||
49 | #define RESET_ISA 42 | ||
50 | #define RESET_ETHERNET 43 | ||
51 | #define RESET_SD_EMMC_A 44 | ||
52 | #define RESET_SD_EMMC_B 45 | ||
53 | #define RESET_SD_EMMC_C 46 | ||
54 | /* 47-60 */ | ||
55 | #define RESET_AUDIO_CODEC 61 | ||
56 | /* 62-63 */ | ||
57 | /* RESET2 */ | ||
58 | /* 64 */ | ||
59 | #define RESET_AUDIO 65 | ||
60 | #define RESET_HDMITX_PHY 66 | ||
61 | /* 67 */ | ||
62 | #define RESET_MIPI_DSI_HOST 68 | ||
63 | #define RESET_ALOCKER 69 | ||
64 | #define RESET_GE2D 70 | ||
65 | #define RESET_PARSER_REG 71 | ||
66 | #define RESET_PARSER_FETCH 72 | ||
67 | #define RESET_CTL 73 | ||
68 | #define RESET_PARSER_TOP 74 | ||
69 | /* 75-77 */ | ||
70 | #define RESET_DVALIN 78 | ||
71 | #define RESET_HDMITX 79 | ||
72 | /* 80-95 */ | ||
73 | /* RESET3 */ | ||
74 | /* 96-95 */ | ||
75 | #define RESET_DEMUX_TOP 105 | ||
76 | #define RESET_DEMUX_DES_PL 106 | ||
77 | #define RESET_DEMUX_S2P_0 107 | ||
78 | #define RESET_DEMUX_S2P_1 108 | ||
79 | #define RESET_DEMUX_0 109 | ||
80 | #define RESET_DEMUX_1 110 | ||
81 | #define RESET_DEMUX_2 111 | ||
82 | /* 112-127 */ | ||
83 | /* RESET4 */ | ||
84 | /* 128-129 */ | ||
85 | #define RESET_MIPI_DSI_PHY 130 | ||
86 | /* 131-132 */ | ||
87 | #define RESET_RDMA 133 | ||
88 | #define RESET_VENCI 134 | ||
89 | #define RESET_VENCP 135 | ||
90 | /* 136 */ | ||
91 | #define RESET_VDAC 137 | ||
92 | /* 138-139 */ | ||
93 | #define RESET_VDI6 140 | ||
94 | #define RESET_VENCL 141 | ||
95 | #define RESET_I2C_M1 142 | ||
96 | #define RESET_I2C_M2 143 | ||
97 | /* 144-159 */ | ||
98 | /* RESET5 */ | ||
99 | /* 160-191 */ | ||
100 | /* RESET6 */ | ||
101 | #define RESET_GEN 192 | ||
102 | #define RESET_SPICC0 193 | ||
103 | #define RESET_SC 194 | ||
104 | #define RESET_SANA_3 195 | ||
105 | #define RESET_I2C_M0 196 | ||
106 | #define RESET_TS_PLL 197 | ||
107 | #define RESET_SPICC1 198 | ||
108 | #define RESET_STREAM 199 | ||
109 | #define RESET_TS_CPU 200 | ||
110 | #define RESET_UART0 201 | ||
111 | #define RESET_UART1_2 202 | ||
112 | #define RESET_ASYNC0 203 | ||
113 | #define RESET_ASYNC1 204 | ||
114 | #define RESET_SPIFC0 205 | ||
115 | #define RESET_I2C_M3 206 | ||
116 | /* 207-223 */ | ||
117 | /* RESET7 */ | ||
118 | #define RESET_USB_DDR_0 224 | ||
119 | #define RESET_USB_DDR_1 225 | ||
120 | #define RESET_USB_DDR_2 226 | ||
121 | #define RESET_USB_DDR_3 227 | ||
122 | #define RESET_TS_GPU 228 | ||
123 | #define RESET_DEVICE_MMC_ARB 229 | ||
124 | #define RESET_DVALIN_DMC_PIPL 230 | ||
125 | #define RESET_VID_LOCK 231 | ||
126 | #define RESET_NIC_DMC_PIPL 232 | ||
127 | #define RESET_DMC_VPU_PIPL 233 | ||
128 | #define RESET_GE2D_DMC_PIPL 234 | ||
129 | #define RESET_HCODEC_DMC_PIPL 235 | ||
130 | #define RESET_WAVE420_DMC_PIPL 236 | ||
131 | #define RESET_HEVCF_DMC_PIPL 237 | ||
132 | /* 238-255 */ | ||
133 | |||
134 | #endif | ||
diff --git a/include/dt-bindings/reset/imx8mq-reset.h b/include/dt-bindings/reset/imx8mq-reset.h new file mode 100644 index 000000000000..57c592498aa0 --- /dev/null +++ b/include/dt-bindings/reset/imx8mq-reset.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (C) 2018 Zodiac Inflight Innovations | ||
4 | * | ||
5 | * Author: Andrey Smirnov <andrew.smirnov@gmail.com> | ||
6 | */ | ||
7 | |||
8 | #ifndef DT_BINDING_RESET_IMX8MQ_H | ||
9 | #define DT_BINDING_RESET_IMX8MQ_H | ||
10 | |||
11 | #define IMX8MQ_RESET_A53_CORE_POR_RESET0 0 | ||
12 | #define IMX8MQ_RESET_A53_CORE_POR_RESET1 1 | ||
13 | #define IMX8MQ_RESET_A53_CORE_POR_RESET2 2 | ||
14 | #define IMX8MQ_RESET_A53_CORE_POR_RESET3 3 | ||
15 | #define IMX8MQ_RESET_A53_CORE_RESET0 4 | ||
16 | #define IMX8MQ_RESET_A53_CORE_RESET1 5 | ||
17 | #define IMX8MQ_RESET_A53_CORE_RESET2 6 | ||
18 | #define IMX8MQ_RESET_A53_CORE_RESET3 7 | ||
19 | #define IMX8MQ_RESET_A53_DBG_RESET0 8 | ||
20 | #define IMX8MQ_RESET_A53_DBG_RESET1 9 | ||
21 | #define IMX8MQ_RESET_A53_DBG_RESET2 10 | ||
22 | #define IMX8MQ_RESET_A53_DBG_RESET3 11 | ||
23 | #define IMX8MQ_RESET_A53_ETM_RESET0 12 | ||
24 | #define IMX8MQ_RESET_A53_ETM_RESET1 13 | ||
25 | #define IMX8MQ_RESET_A53_ETM_RESET2 14 | ||
26 | #define IMX8MQ_RESET_A53_ETM_RESET3 15 | ||
27 | #define IMX8MQ_RESET_A53_SOC_DBG_RESET 16 | ||
28 | #define IMX8MQ_RESET_A53_L2RESET 17 | ||
29 | #define IMX8MQ_RESET_SW_NON_SCLR_M4C_RST 18 | ||
30 | #define IMX8MQ_RESET_OTG1_PHY_RESET 19 | ||
31 | #define IMX8MQ_RESET_OTG2_PHY_RESET 20 | ||
32 | #define IMX8MQ_RESET_MIPI_DSI_RESET_BYTE_N 21 | ||
33 | #define IMX8MQ_RESET_MIPI_DSI_RESET_N 22 | ||
34 | #define IMX8MQ_RESET_MIPI_DIS_DPI_RESET_N 23 | ||
35 | #define IMX8MQ_RESET_MIPI_DIS_ESC_RESET_N 24 | ||
36 | #define IMX8MQ_RESET_MIPI_DIS_PCLK_RESET_N 25 | ||
37 | #define IMX8MQ_RESET_PCIEPHY 26 | ||
38 | #define IMX8MQ_RESET_PCIEPHY_PERST 27 | ||
39 | #define IMX8MQ_RESET_PCIE_CTRL_APPS_EN 28 | ||
40 | #define IMX8MQ_RESET_PCIE_CTRL_APPS_TURNOFF 29 | ||
41 | #define IMX8MQ_RESET_HDMI_PHY_APB_RESET 30 | ||
42 | #define IMX8MQ_RESET_DISP_RESET 31 | ||
43 | #define IMX8MQ_RESET_GPU_RESET 32 | ||
44 | #define IMX8MQ_RESET_VPU_RESET 33 | ||
45 | #define IMX8MQ_RESET_PCIEPHY2 34 | ||
46 | #define IMX8MQ_RESET_PCIEPHY2_PERST 35 | ||
47 | #define IMX8MQ_RESET_PCIE2_CTRL_APPS_EN 36 | ||
48 | #define IMX8MQ_RESET_PCIE2_CTRL_APPS_TURNOFF 37 | ||
49 | #define IMX8MQ_RESET_MIPI_CSI1_CORE_RESET 38 | ||
50 | #define IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET 39 | ||
51 | #define IMX8MQ_RESET_MIPI_CSI1_ESC_RESET 40 | ||
52 | #define IMX8MQ_RESET_MIPI_CSI2_CORE_RESET 41 | ||
53 | #define IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET 42 | ||
54 | #define IMX8MQ_RESET_MIPI_CSI2_ESC_RESET 43 | ||
55 | #define IMX8MQ_RESET_DDRC1_PRST 44 | ||
56 | #define IMX8MQ_RESET_DDRC1_CORE_RESET 45 | ||
57 | #define IMX8MQ_RESET_DDRC1_PHY_RESET 46 | ||
58 | #define IMX8MQ_RESET_DDRC2_PRST 47 | ||
59 | #define IMX8MQ_RESET_DDRC2_CORE_RESET 48 | ||
60 | #define IMX8MQ_RESET_DDRC2_PHY_RESET 49 | ||
61 | |||
62 | #define IMX8MQ_RESET_NUM 50 | ||
63 | |||
64 | #endif | ||
diff --git a/include/dt-bindings/reset/xlnx-zynqmp-resets.h b/include/dt-bindings/reset/xlnx-zynqmp-resets.h new file mode 100644 index 000000000000..d44525b9f8db --- /dev/null +++ b/include/dt-bindings/reset/xlnx-zynqmp-resets.h | |||
@@ -0,0 +1,130 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Copyright (C) 2018 Xilinx, Inc. | ||
4 | */ | ||
5 | |||
6 | #ifndef _DT_BINDINGS_ZYNQMP_RESETS_H | ||
7 | #define _DT_BINDINGS_ZYNQMP_RESETS_H | ||
8 | |||
9 | #define ZYNQMP_RESET_PCIE_CFG 0 | ||
10 | #define ZYNQMP_RESET_PCIE_BRIDGE 1 | ||
11 | #define ZYNQMP_RESET_PCIE_CTRL 2 | ||
12 | #define ZYNQMP_RESET_DP 3 | ||
13 | #define ZYNQMP_RESET_SWDT_CRF 4 | ||
14 | #define ZYNQMP_RESET_AFI_FM5 5 | ||
15 | #define ZYNQMP_RESET_AFI_FM4 6 | ||
16 | #define ZYNQMP_RESET_AFI_FM3 7 | ||
17 | #define ZYNQMP_RESET_AFI_FM2 8 | ||
18 | #define ZYNQMP_RESET_AFI_FM1 9 | ||
19 | #define ZYNQMP_RESET_AFI_FM0 10 | ||
20 | #define ZYNQMP_RESET_GDMA 11 | ||
21 | #define ZYNQMP_RESET_GPU_PP1 12 | ||
22 | #define ZYNQMP_RESET_GPU_PP0 13 | ||
23 | #define ZYNQMP_RESET_GPU 14 | ||
24 | #define ZYNQMP_RESET_GT 15 | ||
25 | #define ZYNQMP_RESET_SATA 16 | ||
26 | #define ZYNQMP_RESET_ACPU3_PWRON 17 | ||
27 | #define ZYNQMP_RESET_ACPU2_PWRON 18 | ||
28 | #define ZYNQMP_RESET_ACPU1_PWRON 19 | ||
29 | #define ZYNQMP_RESET_ACPU0_PWRON 20 | ||
30 | #define ZYNQMP_RESET_APU_L2 21 | ||
31 | #define ZYNQMP_RESET_ACPU3 22 | ||
32 | #define ZYNQMP_RESET_ACPU2 23 | ||
33 | #define ZYNQMP_RESET_ACPU1 24 | ||
34 | #define ZYNQMP_RESET_ACPU0 25 | ||
35 | #define ZYNQMP_RESET_DDR 26 | ||
36 | #define ZYNQMP_RESET_APM_FPD 27 | ||
37 | #define ZYNQMP_RESET_SOFT 28 | ||
38 | #define ZYNQMP_RESET_GEM0 29 | ||
39 | #define ZYNQMP_RESET_GEM1 30 | ||
40 | #define ZYNQMP_RESET_GEM2 31 | ||
41 | #define ZYNQMP_RESET_GEM3 32 | ||
42 | #define ZYNQMP_RESET_QSPI 33 | ||
43 | #define ZYNQMP_RESET_UART0 34 | ||
44 | #define ZYNQMP_RESET_UART1 35 | ||
45 | #define ZYNQMP_RESET_SPI0 36 | ||
46 | #define ZYNQMP_RESET_SPI1 37 | ||
47 | #define ZYNQMP_RESET_SDIO0 38 | ||
48 | #define ZYNQMP_RESET_SDIO1 39 | ||
49 | #define ZYNQMP_RESET_CAN0 40 | ||
50 | #define ZYNQMP_RESET_CAN1 41 | ||
51 | #define ZYNQMP_RESET_I2C0 42 | ||
52 | #define ZYNQMP_RESET_I2C1 43 | ||
53 | #define ZYNQMP_RESET_TTC0 44 | ||
54 | #define ZYNQMP_RESET_TTC1 45 | ||
55 | #define ZYNQMP_RESET_TTC2 46 | ||
56 | #define ZYNQMP_RESET_TTC3 47 | ||
57 | #define ZYNQMP_RESET_SWDT_CRL 48 | ||
58 | #define ZYNQMP_RESET_NAND 49 | ||
59 | #define ZYNQMP_RESET_ADMA 50 | ||
60 | #define ZYNQMP_RESET_GPIO 51 | ||
61 | #define ZYNQMP_RESET_IOU_CC 52 | ||
62 | #define ZYNQMP_RESET_TIMESTAMP 53 | ||
63 | #define ZYNQMP_RESET_RPU_R50 54 | ||
64 | #define ZYNQMP_RESET_RPU_R51 55 | ||
65 | #define ZYNQMP_RESET_RPU_AMBA 56 | ||
66 | #define ZYNQMP_RESET_OCM 57 | ||
67 | #define ZYNQMP_RESET_RPU_PGE 58 | ||
68 | #define ZYNQMP_RESET_USB0_CORERESET 59 | ||
69 | #define ZYNQMP_RESET_USB1_CORERESET 60 | ||
70 | #define ZYNQMP_RESET_USB0_HIBERRESET 61 | ||
71 | #define ZYNQMP_RESET_USB1_HIBERRESET 62 | ||
72 | #define ZYNQMP_RESET_USB0_APB 63 | ||
73 | #define ZYNQMP_RESET_USB1_APB 64 | ||
74 | #define ZYNQMP_RESET_IPI 65 | ||
75 | #define ZYNQMP_RESET_APM_LPD 66 | ||
76 | #define ZYNQMP_RESET_RTC 67 | ||
77 | #define ZYNQMP_RESET_SYSMON 68 | ||
78 | #define ZYNQMP_RESET_AFI_FM6 69 | ||
79 | #define ZYNQMP_RESET_LPD_SWDT 70 | ||
80 | #define ZYNQMP_RESET_FPD 71 | ||
81 | #define ZYNQMP_RESET_RPU_DBG1 72 | ||
82 | #define ZYNQMP_RESET_RPU_DBG0 73 | ||
83 | #define ZYNQMP_RESET_DBG_LPD 74 | ||
84 | #define ZYNQMP_RESET_DBG_FPD 75 | ||
85 | #define ZYNQMP_RESET_APLL 76 | ||
86 | #define ZYNQMP_RESET_DPLL 77 | ||
87 | #define ZYNQMP_RESET_VPLL 78 | ||
88 | #define ZYNQMP_RESET_IOPLL 79 | ||
89 | #define ZYNQMP_RESET_RPLL 80 | ||
90 | #define ZYNQMP_RESET_GPO3_PL_0 81 | ||
91 | #define ZYNQMP_RESET_GPO3_PL_1 82 | ||
92 | #define ZYNQMP_RESET_GPO3_PL_2 83 | ||
93 | #define ZYNQMP_RESET_GPO3_PL_3 84 | ||
94 | #define ZYNQMP_RESET_GPO3_PL_4 85 | ||
95 | #define ZYNQMP_RESET_GPO3_PL_5 86 | ||
96 | #define ZYNQMP_RESET_GPO3_PL_6 87 | ||
97 | #define ZYNQMP_RESET_GPO3_PL_7 88 | ||
98 | #define ZYNQMP_RESET_GPO3_PL_8 89 | ||
99 | #define ZYNQMP_RESET_GPO3_PL_9 90 | ||
100 | #define ZYNQMP_RESET_GPO3_PL_10 91 | ||
101 | #define ZYNQMP_RESET_GPO3_PL_11 92 | ||
102 | #define ZYNQMP_RESET_GPO3_PL_12 93 | ||
103 | #define ZYNQMP_RESET_GPO3_PL_13 94 | ||
104 | #define ZYNQMP_RESET_GPO3_PL_14 95 | ||
105 | #define ZYNQMP_RESET_GPO3_PL_15 96 | ||
106 | #define ZYNQMP_RESET_GPO3_PL_16 97 | ||
107 | #define ZYNQMP_RESET_GPO3_PL_17 98 | ||
108 | #define ZYNQMP_RESET_GPO3_PL_18 99 | ||
109 | #define ZYNQMP_RESET_GPO3_PL_19 100 | ||
110 | #define ZYNQMP_RESET_GPO3_PL_20 101 | ||
111 | #define ZYNQMP_RESET_GPO3_PL_21 102 | ||
112 | #define ZYNQMP_RESET_GPO3_PL_22 103 | ||
113 | #define ZYNQMP_RESET_GPO3_PL_23 104 | ||
114 | #define ZYNQMP_RESET_GPO3_PL_24 105 | ||
115 | #define ZYNQMP_RESET_GPO3_PL_25 106 | ||
116 | #define ZYNQMP_RESET_GPO3_PL_26 107 | ||
117 | #define ZYNQMP_RESET_GPO3_PL_27 108 | ||
118 | #define ZYNQMP_RESET_GPO3_PL_28 109 | ||
119 | #define ZYNQMP_RESET_GPO3_PL_29 110 | ||
120 | #define ZYNQMP_RESET_GPO3_PL_30 111 | ||
121 | #define ZYNQMP_RESET_GPO3_PL_31 112 | ||
122 | #define ZYNQMP_RESET_RPU_LS 113 | ||
123 | #define ZYNQMP_RESET_PS_ONLY 114 | ||
124 | #define ZYNQMP_RESET_PL 115 | ||
125 | #define ZYNQMP_RESET_PS_PL0 116 | ||
126 | #define ZYNQMP_RESET_PS_PL1 117 | ||
127 | #define ZYNQMP_RESET_PS_PL2 118 | ||
128 | #define ZYNQMP_RESET_PS_PL3 119 | ||
129 | |||
130 | #endif | ||
diff --git a/include/dt-bindings/soc/bcm2835-pm.h b/include/dt-bindings/soc/bcm2835-pm.h new file mode 100644 index 000000000000..153d75b8d99f --- /dev/null +++ b/include/dt-bindings/soc/bcm2835-pm.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ | ||
2 | |||
3 | #ifndef _DT_BINDINGS_ARM_BCM2835_PM_H | ||
4 | #define _DT_BINDINGS_ARM_BCM2835_PM_H | ||
5 | |||
6 | #define BCM2835_POWER_DOMAIN_GRAFX 0 | ||
7 | #define BCM2835_POWER_DOMAIN_GRAFX_V3D 1 | ||
8 | #define BCM2835_POWER_DOMAIN_IMAGE 2 | ||
9 | #define BCM2835_POWER_DOMAIN_IMAGE_PERI 3 | ||
10 | #define BCM2835_POWER_DOMAIN_IMAGE_ISP 4 | ||
11 | #define BCM2835_POWER_DOMAIN_IMAGE_H264 5 | ||
12 | #define BCM2835_POWER_DOMAIN_USB 6 | ||
13 | #define BCM2835_POWER_DOMAIN_DSI0 7 | ||
14 | #define BCM2835_POWER_DOMAIN_DSI1 8 | ||
15 | #define BCM2835_POWER_DOMAIN_CAM0 9 | ||
16 | #define BCM2835_POWER_DOMAIN_CAM1 10 | ||
17 | #define BCM2835_POWER_DOMAIN_CCP2TX 11 | ||
18 | #define BCM2835_POWER_DOMAIN_HDMI 12 | ||
19 | |||
20 | #define BCM2835_POWER_DOMAIN_COUNT 13 | ||
21 | |||
22 | #define BCM2835_RESET_V3D 0 | ||
23 | #define BCM2835_RESET_ISP 1 | ||
24 | #define BCM2835_RESET_H264 2 | ||
25 | |||
26 | #define BCM2835_RESET_COUNT 3 | ||
27 | |||
28 | #endif /* _DT_BINDINGS_ARM_BCM2835_PM_H */ | ||
diff --git a/include/linux/firmware/imx/svc/misc.h b/include/linux/firmware/imx/svc/misc.h index e21c49aba92f..031dd4d3c766 100644 --- a/include/linux/firmware/imx/svc/misc.h +++ b/include/linux/firmware/imx/svc/misc.h | |||
@@ -52,4 +52,7 @@ int imx_sc_misc_set_control(struct imx_sc_ipc *ipc, u32 resource, | |||
52 | int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, | 52 | int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, |
53 | u8 ctrl, u32 *val); | 53 | u8 ctrl, u32 *val); |
54 | 54 | ||
55 | int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource, | ||
56 | bool enable, u64 phys_addr); | ||
57 | |||
55 | #endif /* _SC_MISC_API_H */ | 58 | #endif /* _SC_MISC_API_H */ |
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 3c3c28eff56a..642dab10f65d 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h | |||
@@ -28,12 +28,35 @@ | |||
28 | /* SMC SIP service Call Function Identifier Prefix */ | 28 | /* SMC SIP service Call Function Identifier Prefix */ |
29 | #define PM_SIP_SVC 0xC2000000 | 29 | #define PM_SIP_SVC 0xC2000000 |
30 | #define PM_GET_TRUSTZONE_VERSION 0xa03 | 30 | #define PM_GET_TRUSTZONE_VERSION 0xa03 |
31 | #define PM_SET_SUSPEND_MODE 0xa02 | ||
32 | #define GET_CALLBACK_DATA 0xa01 | ||
31 | 33 | ||
32 | /* Number of 32bits values in payload */ | 34 | /* Number of 32bits values in payload */ |
33 | #define PAYLOAD_ARG_CNT 4U | 35 | #define PAYLOAD_ARG_CNT 4U |
34 | 36 | ||
37 | /* Number of arguments for a callback */ | ||
38 | #define CB_ARG_CNT 4 | ||
39 | |||
40 | /* Payload size (consists of callback API ID + arguments) */ | ||
41 | #define CB_PAYLOAD_SIZE (CB_ARG_CNT + 1) | ||
42 | |||
43 | #define ZYNQMP_PM_MAX_QOS 100U | ||
44 | |||
45 | /* Node capabilities */ | ||
46 | #define ZYNQMP_PM_CAPABILITY_ACCESS 0x1U | ||
47 | #define ZYNQMP_PM_CAPABILITY_CONTEXT 0x2U | ||
48 | #define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U | ||
49 | #define ZYNQMP_PM_CAPABILITY_POWER 0x8U | ||
50 | |||
35 | enum pm_api_id { | 51 | enum pm_api_id { |
36 | PM_GET_API_VERSION = 1, | 52 | PM_GET_API_VERSION = 1, |
53 | PM_REQUEST_NODE = 13, | ||
54 | PM_RELEASE_NODE, | ||
55 | PM_SET_REQUIREMENT, | ||
56 | PM_RESET_ASSERT = 17, | ||
57 | PM_RESET_GET_STATUS, | ||
58 | PM_PM_INIT_FINALIZE = 21, | ||
59 | PM_GET_CHIPID = 24, | ||
37 | PM_IOCTL = 34, | 60 | PM_IOCTL = 34, |
38 | PM_QUERY_DATA, | 61 | PM_QUERY_DATA, |
39 | PM_CLOCK_ENABLE, | 62 | PM_CLOCK_ENABLE, |
@@ -75,6 +98,149 @@ enum pm_query_id { | |||
75 | PM_QID_CLOCK_GET_NUM_CLOCKS = 12, | 98 | PM_QID_CLOCK_GET_NUM_CLOCKS = 12, |
76 | }; | 99 | }; |
77 | 100 | ||
101 | enum zynqmp_pm_reset_action { | ||
102 | PM_RESET_ACTION_RELEASE, | ||
103 | PM_RESET_ACTION_ASSERT, | ||
104 | PM_RESET_ACTION_PULSE, | ||
105 | }; | ||
106 | |||
107 | enum zynqmp_pm_reset { | ||
108 | ZYNQMP_PM_RESET_START = 1000, | ||
109 | ZYNQMP_PM_RESET_PCIE_CFG = ZYNQMP_PM_RESET_START, | ||
110 | ZYNQMP_PM_RESET_PCIE_BRIDGE, | ||
111 | ZYNQMP_PM_RESET_PCIE_CTRL, | ||
112 | ZYNQMP_PM_RESET_DP, | ||
113 | ZYNQMP_PM_RESET_SWDT_CRF, | ||
114 | ZYNQMP_PM_RESET_AFI_FM5, | ||
115 | ZYNQMP_PM_RESET_AFI_FM4, | ||
116 | ZYNQMP_PM_RESET_AFI_FM3, | ||
117 | ZYNQMP_PM_RESET_AFI_FM2, | ||
118 | ZYNQMP_PM_RESET_AFI_FM1, | ||
119 | ZYNQMP_PM_RESET_AFI_FM0, | ||
120 | ZYNQMP_PM_RESET_GDMA, | ||
121 | ZYNQMP_PM_RESET_GPU_PP1, | ||
122 | ZYNQMP_PM_RESET_GPU_PP0, | ||
123 | ZYNQMP_PM_RESET_GPU, | ||
124 | ZYNQMP_PM_RESET_GT, | ||
125 | ZYNQMP_PM_RESET_SATA, | ||
126 | ZYNQMP_PM_RESET_ACPU3_PWRON, | ||
127 | ZYNQMP_PM_RESET_ACPU2_PWRON, | ||
128 | ZYNQMP_PM_RESET_ACPU1_PWRON, | ||
129 | ZYNQMP_PM_RESET_ACPU0_PWRON, | ||
130 | ZYNQMP_PM_RESET_APU_L2, | ||
131 | ZYNQMP_PM_RESET_ACPU3, | ||
132 | ZYNQMP_PM_RESET_ACPU2, | ||
133 | ZYNQMP_PM_RESET_ACPU1, | ||
134 | ZYNQMP_PM_RESET_ACPU0, | ||
135 | ZYNQMP_PM_RESET_DDR, | ||
136 | ZYNQMP_PM_RESET_APM_FPD, | ||
137 | ZYNQMP_PM_RESET_SOFT, | ||
138 | ZYNQMP_PM_RESET_GEM0, | ||
139 | ZYNQMP_PM_RESET_GEM1, | ||
140 | ZYNQMP_PM_RESET_GEM2, | ||
141 | ZYNQMP_PM_RESET_GEM3, | ||
142 | ZYNQMP_PM_RESET_QSPI, | ||
143 | ZYNQMP_PM_RESET_UART0, | ||
144 | ZYNQMP_PM_RESET_UART1, | ||
145 | ZYNQMP_PM_RESET_SPI0, | ||
146 | ZYNQMP_PM_RESET_SPI1, | ||
147 | ZYNQMP_PM_RESET_SDIO0, | ||
148 | ZYNQMP_PM_RESET_SDIO1, | ||
149 | ZYNQMP_PM_RESET_CAN0, | ||
150 | ZYNQMP_PM_RESET_CAN1, | ||
151 | ZYNQMP_PM_RESET_I2C0, | ||
152 | ZYNQMP_PM_RESET_I2C1, | ||
153 | ZYNQMP_PM_RESET_TTC0, | ||
154 | ZYNQMP_PM_RESET_TTC1, | ||
155 | ZYNQMP_PM_RESET_TTC2, | ||
156 | ZYNQMP_PM_RESET_TTC3, | ||
157 | ZYNQMP_PM_RESET_SWDT_CRL, | ||
158 | ZYNQMP_PM_RESET_NAND, | ||
159 | ZYNQMP_PM_RESET_ADMA, | ||
160 | ZYNQMP_PM_RESET_GPIO, | ||
161 | ZYNQMP_PM_RESET_IOU_CC, | ||
162 | ZYNQMP_PM_RESET_TIMESTAMP, | ||
163 | ZYNQMP_PM_RESET_RPU_R50, | ||
164 | ZYNQMP_PM_RESET_RPU_R51, | ||
165 | ZYNQMP_PM_RESET_RPU_AMBA, | ||
166 | ZYNQMP_PM_RESET_OCM, | ||
167 | ZYNQMP_PM_RESET_RPU_PGE, | ||
168 | ZYNQMP_PM_RESET_USB0_CORERESET, | ||
169 | ZYNQMP_PM_RESET_USB1_CORERESET, | ||
170 | ZYNQMP_PM_RESET_USB0_HIBERRESET, | ||
171 | ZYNQMP_PM_RESET_USB1_HIBERRESET, | ||
172 | ZYNQMP_PM_RESET_USB0_APB, | ||
173 | ZYNQMP_PM_RESET_USB1_APB, | ||
174 | ZYNQMP_PM_RESET_IPI, | ||
175 | ZYNQMP_PM_RESET_APM_LPD, | ||
176 | ZYNQMP_PM_RESET_RTC, | ||
177 | ZYNQMP_PM_RESET_SYSMON, | ||
178 | ZYNQMP_PM_RESET_AFI_FM6, | ||
179 | ZYNQMP_PM_RESET_LPD_SWDT, | ||
180 | ZYNQMP_PM_RESET_FPD, | ||
181 | ZYNQMP_PM_RESET_RPU_DBG1, | ||
182 | ZYNQMP_PM_RESET_RPU_DBG0, | ||
183 | ZYNQMP_PM_RESET_DBG_LPD, | ||
184 | ZYNQMP_PM_RESET_DBG_FPD, | ||
185 | ZYNQMP_PM_RESET_APLL, | ||
186 | ZYNQMP_PM_RESET_DPLL, | ||
187 | ZYNQMP_PM_RESET_VPLL, | ||
188 | ZYNQMP_PM_RESET_IOPLL, | ||
189 | ZYNQMP_PM_RESET_RPLL, | ||
190 | ZYNQMP_PM_RESET_GPO3_PL_0, | ||
191 | ZYNQMP_PM_RESET_GPO3_PL_1, | ||
192 | ZYNQMP_PM_RESET_GPO3_PL_2, | ||
193 | ZYNQMP_PM_RESET_GPO3_PL_3, | ||
194 | ZYNQMP_PM_RESET_GPO3_PL_4, | ||
195 | ZYNQMP_PM_RESET_GPO3_PL_5, | ||
196 | ZYNQMP_PM_RESET_GPO3_PL_6, | ||
197 | ZYNQMP_PM_RESET_GPO3_PL_7, | ||
198 | ZYNQMP_PM_RESET_GPO3_PL_8, | ||
199 | ZYNQMP_PM_RESET_GPO3_PL_9, | ||
200 | ZYNQMP_PM_RESET_GPO3_PL_10, | ||
201 | ZYNQMP_PM_RESET_GPO3_PL_11, | ||
202 | ZYNQMP_PM_RESET_GPO3_PL_12, | ||
203 | ZYNQMP_PM_RESET_GPO3_PL_13, | ||
204 | ZYNQMP_PM_RESET_GPO3_PL_14, | ||
205 | ZYNQMP_PM_RESET_GPO3_PL_15, | ||
206 | ZYNQMP_PM_RESET_GPO3_PL_16, | ||
207 | ZYNQMP_PM_RESET_GPO3_PL_17, | ||
208 | ZYNQMP_PM_RESET_GPO3_PL_18, | ||
209 | ZYNQMP_PM_RESET_GPO3_PL_19, | ||
210 | ZYNQMP_PM_RESET_GPO3_PL_20, | ||
211 | ZYNQMP_PM_RESET_GPO3_PL_21, | ||
212 | ZYNQMP_PM_RESET_GPO3_PL_22, | ||
213 | ZYNQMP_PM_RESET_GPO3_PL_23, | ||
214 | ZYNQMP_PM_RESET_GPO3_PL_24, | ||
215 | ZYNQMP_PM_RESET_GPO3_PL_25, | ||
216 | ZYNQMP_PM_RESET_GPO3_PL_26, | ||
217 | ZYNQMP_PM_RESET_GPO3_PL_27, | ||
218 | ZYNQMP_PM_RESET_GPO3_PL_28, | ||
219 | ZYNQMP_PM_RESET_GPO3_PL_29, | ||
220 | ZYNQMP_PM_RESET_GPO3_PL_30, | ||
221 | ZYNQMP_PM_RESET_GPO3_PL_31, | ||
222 | ZYNQMP_PM_RESET_RPU_LS, | ||
223 | ZYNQMP_PM_RESET_PS_ONLY, | ||
224 | ZYNQMP_PM_RESET_PL, | ||
225 | ZYNQMP_PM_RESET_PS_PL0, | ||
226 | ZYNQMP_PM_RESET_PS_PL1, | ||
227 | ZYNQMP_PM_RESET_PS_PL2, | ||
228 | ZYNQMP_PM_RESET_PS_PL3, | ||
229 | ZYNQMP_PM_RESET_END = ZYNQMP_PM_RESET_PS_PL3 | ||
230 | }; | ||
231 | |||
232 | enum zynqmp_pm_suspend_reason { | ||
233 | SUSPEND_POWER_REQUEST = 201, | ||
234 | SUSPEND_ALERT, | ||
235 | SUSPEND_SYSTEM_SHUTDOWN, | ||
236 | }; | ||
237 | |||
238 | enum zynqmp_pm_request_ack { | ||
239 | ZYNQMP_PM_REQUEST_ACK_NO = 1, | ||
240 | ZYNQMP_PM_REQUEST_ACK_BLOCKING, | ||
241 | ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING, | ||
242 | }; | ||
243 | |||
78 | /** | 244 | /** |
79 | * struct zynqmp_pm_query_data - PM query data | 245 | * struct zynqmp_pm_query_data - PM query data |
80 | * @qid: query ID | 246 | * @qid: query ID |
@@ -91,6 +257,7 @@ struct zynqmp_pm_query_data { | |||
91 | 257 | ||
92 | struct zynqmp_eemi_ops { | 258 | struct zynqmp_eemi_ops { |
93 | int (*get_api_version)(u32 *version); | 259 | int (*get_api_version)(u32 *version); |
260 | int (*get_chipid)(u32 *idcode, u32 *version); | ||
94 | int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out); | 261 | int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out); |
95 | int (*clock_enable)(u32 clock_id); | 262 | int (*clock_enable)(u32 clock_id); |
96 | int (*clock_disable)(u32 clock_id); | 263 | int (*clock_disable)(u32 clock_id); |
@@ -102,8 +269,25 @@ struct zynqmp_eemi_ops { | |||
102 | int (*clock_setparent)(u32 clock_id, u32 parent_id); | 269 | int (*clock_setparent)(u32 clock_id, u32 parent_id); |
103 | int (*clock_getparent)(u32 clock_id, u32 *parent_id); | 270 | int (*clock_getparent)(u32 clock_id, u32 *parent_id); |
104 | int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out); | 271 | int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out); |
272 | int (*reset_assert)(const enum zynqmp_pm_reset reset, | ||
273 | const enum zynqmp_pm_reset_action assert_flag); | ||
274 | int (*reset_get_status)(const enum zynqmp_pm_reset reset, u32 *status); | ||
275 | int (*init_finalize)(void); | ||
276 | int (*set_suspend_mode)(u32 mode); | ||
277 | int (*request_node)(const u32 node, | ||
278 | const u32 capabilities, | ||
279 | const u32 qos, | ||
280 | const enum zynqmp_pm_request_ack ack); | ||
281 | int (*release_node)(const u32 node); | ||
282 | int (*set_requirement)(const u32 node, | ||
283 | const u32 capabilities, | ||
284 | const u32 qos, | ||
285 | const enum zynqmp_pm_request_ack ack); | ||
105 | }; | 286 | }; |
106 | 287 | ||
288 | int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1, | ||
289 | u32 arg2, u32 arg3, u32 *ret_payload); | ||
290 | |||
107 | #if IS_REACHABLE(CONFIG_ARCH_ZYNQMP) | 291 | #if IS_REACHABLE(CONFIG_ARCH_ZYNQMP) |
108 | const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); | 292 | const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); |
109 | #else | 293 | #else |
diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h index 941b11811f85..1fc0edd71c52 100644 --- a/include/linux/fsl/guts.h +++ b/include/linux/fsl/guts.h | |||
@@ -135,8 +135,6 @@ struct ccsr_guts { | |||
135 | u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ | 135 | u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ |
136 | } __attribute__ ((packed)); | 136 | } __attribute__ ((packed)); |
137 | 137 | ||
138 | u32 fsl_guts_get_svr(void); | ||
139 | |||
140 | /* Alternate function signal multiplex control */ | 138 | /* Alternate function signal multiplex control */ |
141 | #define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) | 139 | #define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) |
142 | 140 | ||
diff --git a/include/linux/mfd/bcm2835-pm.h b/include/linux/mfd/bcm2835-pm.h new file mode 100644 index 000000000000..ed37dc40e82a --- /dev/null +++ b/include/linux/mfd/bcm2835-pm.h | |||
@@ -0,0 +1,14 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0+ */ | ||
2 | |||
3 | #ifndef BCM2835_MFD_PM_H | ||
4 | #define BCM2835_MFD_PM_H | ||
5 | |||
6 | #include <linux/regmap.h> | ||
7 | |||
8 | struct bcm2835_pm { | ||
9 | struct device *dev; | ||
10 | void __iomem *base; | ||
11 | void __iomem *asb; | ||
12 | }; | ||
13 | |||
14 | #endif /* BCM2835_MFD_PM_H */ | ||
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index f9bd2f34b99f..14eaeeb46f41 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h | |||
@@ -779,4 +779,13 @@ struct typec_device_id { | |||
779 | kernel_ulong_t driver_data; | 779 | kernel_ulong_t driver_data; |
780 | }; | 780 | }; |
781 | 781 | ||
782 | /** | ||
783 | * struct tee_client_device_id - tee based device identifier | ||
784 | * @uuid: For TEE based client devices we use the device uuid as | ||
785 | * the identifier. | ||
786 | */ | ||
787 | struct tee_client_device_id { | ||
788 | uuid_t uuid; | ||
789 | }; | ||
790 | |||
782 | #endif /* LINUX_MOD_DEVICETABLE_H */ | 791 | #endif /* LINUX_MOD_DEVICETABLE_H */ |
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index b895f4e79868..900359342965 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h | |||
@@ -86,6 +86,8 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp); | |||
86 | 86 | ||
87 | unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); | 87 | unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); |
88 | 88 | ||
89 | unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp); | ||
90 | |||
89 | bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); | 91 | bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp); |
90 | 92 | ||
91 | int dev_pm_opp_get_opp_count(struct device *dev); | 93 | int dev_pm_opp_get_opp_count(struct device *dev); |
@@ -158,6 +160,11 @@ static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) | |||
158 | return 0; | 160 | return 0; |
159 | } | 161 | } |
160 | 162 | ||
163 | static inline unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) | ||
164 | { | ||
165 | return 0; | ||
166 | } | ||
167 | |||
161 | static inline bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) | 168 | static inline bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp) |
162 | { | 169 | { |
163 | return false; | 170 | return false; |
diff --git a/include/linux/reset/socfpga.h b/include/linux/reset/socfpga.h new file mode 100644 index 000000000000..b11a2047c342 --- /dev/null +++ b/include/linux/reset/socfpga.h | |||
@@ -0,0 +1,7 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #ifndef __LINUX_RESET_SOCFPGA_H__ | ||
3 | #define __LINUX_RESET_SOCFPGA_H__ | ||
4 | |||
5 | void __init socfpga_reset_init(void); | ||
6 | |||
7 | #endif /* __LINUX_RESET_SOCFPGA_H__ */ | ||
diff --git a/include/linux/reset/sunxi.h b/include/linux/reset/sunxi.h new file mode 100644 index 000000000000..1ad7fffb413e --- /dev/null +++ b/include/linux/reset/sunxi.h | |||
@@ -0,0 +1,7 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #ifndef __LINUX_RESET_SUNXI_H__ | ||
3 | #define __LINUX_RESET_SUNXI_H__ | ||
4 | |||
5 | void __init sun6i_reset_init(void); | ||
6 | |||
7 | #endif /* __LINUX_RESET_SUNXI_H__ */ | ||
diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 69c285b1c990..eb71a50b8afc 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h | |||
@@ -162,6 +162,12 @@ int llcc_slice_deactivate(struct llcc_slice_desc *desc); | |||
162 | */ | 162 | */ |
163 | int qcom_llcc_probe(struct platform_device *pdev, | 163 | int qcom_llcc_probe(struct platform_device *pdev, |
164 | const struct llcc_slice_config *table, u32 sz); | 164 | const struct llcc_slice_config *table, u32 sz); |
165 | |||
166 | /** | ||
167 | * qcom_llcc_remove - remove the sct table | ||
168 | * @pdev: Platform device pointer | ||
169 | */ | ||
170 | int qcom_llcc_remove(struct platform_device *pdev); | ||
165 | #else | 171 | #else |
166 | static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid) | 172 | static inline struct llcc_slice_desc *llcc_slice_getd(u32 uid) |
167 | { | 173 | { |
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 6cfe05893a76..4a49f80e7f71 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h | |||
@@ -15,11 +15,14 @@ | |||
15 | #ifndef __TEE_DRV_H | 15 | #ifndef __TEE_DRV_H |
16 | #define __TEE_DRV_H | 16 | #define __TEE_DRV_H |
17 | 17 | ||
18 | #include <linux/types.h> | 18 | #include <linux/device.h> |
19 | #include <linux/idr.h> | 19 | #include <linux/idr.h> |
20 | #include <linux/kref.h> | 20 | #include <linux/kref.h> |
21 | #include <linux/list.h> | 21 | #include <linux/list.h> |
22 | #include <linux/mod_devicetable.h> | ||
22 | #include <linux/tee.h> | 23 | #include <linux/tee.h> |
24 | #include <linux/types.h> | ||
25 | #include <linux/uuid.h> | ||
23 | 26 | ||
24 | /* | 27 | /* |
25 | * The file describes the API provided by the generic TEE driver to the | 28 | * The file describes the API provided by the generic TEE driver to the |
@@ -47,6 +50,11 @@ struct tee_shm_pool; | |||
47 | * @releasing: flag that indicates if context is being released right now. | 50 | * @releasing: flag that indicates if context is being released right now. |
48 | * It is needed to break circular dependency on context during | 51 | * It is needed to break circular dependency on context during |
49 | * shared memory release. | 52 | * shared memory release. |
53 | * @supp_nowait: flag that indicates that requests in this context should not | ||
54 | * wait for tee-supplicant daemon to be started if not present | ||
55 | * and just return with an error code. It is needed for requests | ||
56 | * that arises from TEE based kernel drivers that should be | ||
57 | * non-blocking in nature. | ||
50 | */ | 58 | */ |
51 | struct tee_context { | 59 | struct tee_context { |
52 | struct tee_device *teedev; | 60 | struct tee_device *teedev; |
@@ -54,6 +62,7 @@ struct tee_context { | |||
54 | void *data; | 62 | void *data; |
55 | struct kref refcount; | 63 | struct kref refcount; |
56 | bool releasing; | 64 | bool releasing; |
65 | bool supp_nowait; | ||
57 | }; | 66 | }; |
58 | 67 | ||
59 | struct tee_param_memref { | 68 | struct tee_param_memref { |
@@ -526,6 +535,18 @@ int tee_client_invoke_func(struct tee_context *ctx, | |||
526 | struct tee_ioctl_invoke_arg *arg, | 535 | struct tee_ioctl_invoke_arg *arg, |
527 | struct tee_param *param); | 536 | struct tee_param *param); |
528 | 537 | ||
538 | /** | ||
539 | * tee_client_cancel_req() - Request cancellation of the previous open-session | ||
540 | * or invoke-command operations in a Trusted Application | ||
541 | * @ctx: TEE Context | ||
542 | * @arg: Cancellation arguments, see description of | ||
543 | * struct tee_ioctl_cancel_arg | ||
544 | * | ||
545 | * Returns < 0 on error else 0 if the cancellation was successfully requested. | ||
546 | */ | ||
547 | int tee_client_cancel_req(struct tee_context *ctx, | ||
548 | struct tee_ioctl_cancel_arg *arg); | ||
549 | |||
529 | static inline bool tee_param_is_memref(struct tee_param *param) | 550 | static inline bool tee_param_is_memref(struct tee_param *param) |
530 | { | 551 | { |
531 | switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { | 552 | switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { |
@@ -538,4 +559,31 @@ static inline bool tee_param_is_memref(struct tee_param *param) | |||
538 | } | 559 | } |
539 | } | 560 | } |
540 | 561 | ||
562 | extern struct bus_type tee_bus_type; | ||
563 | |||
564 | /** | ||
565 | * struct tee_client_device - tee based device | ||
566 | * @id: device identifier | ||
567 | * @dev: device structure | ||
568 | */ | ||
569 | struct tee_client_device { | ||
570 | struct tee_client_device_id id; | ||
571 | struct device dev; | ||
572 | }; | ||
573 | |||
574 | #define to_tee_client_device(d) container_of(d, struct tee_client_device, dev) | ||
575 | |||
576 | /** | ||
577 | * struct tee_client_driver - tee client driver | ||
578 | * @id_table: device id table supported by this driver | ||
579 | * @driver: driver structure | ||
580 | */ | ||
581 | struct tee_client_driver { | ||
582 | const struct tee_client_device_id *id_table; | ||
583 | struct device_driver driver; | ||
584 | }; | ||
585 | |||
586 | #define to_tee_client_driver(d) \ | ||
587 | container_of(d, struct tee_client_driver, driver) | ||
588 | |||
541 | #endif /*__TEE_DRV_H*/ | 589 | #endif /*__TEE_DRV_H*/ |
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h index 4be1aa4435ae..7800e12ee042 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h | |||
@@ -73,6 +73,8 @@ enum rpi_firmware_property_tag { | |||
73 | RPI_FIRMWARE_GET_CUSTOMER_OTP = 0x00030021, | 73 | RPI_FIRMWARE_GET_CUSTOMER_OTP = 0x00030021, |
74 | RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030, | 74 | RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030, |
75 | RPI_FIRMWARE_GET_THROTTLED = 0x00030046, | 75 | RPI_FIRMWARE_GET_THROTTLED = 0x00030046, |
76 | RPI_FIRMWARE_GET_CLOCK_MEASURED = 0x00030047, | ||
77 | RPI_FIRMWARE_NOTIFY_REBOOT = 0x00030048, | ||
76 | RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, | 78 | RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, |
77 | RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, | 79 | RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, |
78 | RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, | 80 | RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, |
@@ -86,6 +88,8 @@ enum rpi_firmware_property_tag { | |||
86 | RPI_FIRMWARE_SET_GPIO_CONFIG = 0x00038043, | 88 | RPI_FIRMWARE_SET_GPIO_CONFIG = 0x00038043, |
87 | RPI_FIRMWARE_GET_PERIPH_REG = 0x00030045, | 89 | RPI_FIRMWARE_GET_PERIPH_REG = 0x00030045, |
88 | RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045, | 90 | RPI_FIRMWARE_SET_PERIPH_REG = 0x00038045, |
91 | RPI_FIRMWARE_GET_POE_HAT_VAL = 0x00030049, | ||
92 | RPI_FIRMWARE_SET_POE_HAT_VAL = 0x00030050, | ||
89 | 93 | ||
90 | 94 | ||
91 | /* Dispmanx TAGS */ | 95 | /* Dispmanx TAGS */ |
diff --git a/include/soc/fsl/dpaa2-io.h b/include/soc/fsl/dpaa2-io.h index 3447fd10a3e6..672cfb58046f 100644 --- a/include/soc/fsl/dpaa2-io.h +++ b/include/soc/fsl/dpaa2-io.h | |||
@@ -111,9 +111,9 @@ int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, u32 fqid, | |||
111 | const struct dpaa2_fd *fd); | 111 | const struct dpaa2_fd *fd); |
112 | int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, u32 qdid, u8 prio, | 112 | int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, u32 qdid, u8 prio, |
113 | u16 qdbin, const struct dpaa2_fd *fd); | 113 | u16 qdbin, const struct dpaa2_fd *fd); |
114 | int dpaa2_io_service_release(struct dpaa2_io *d, u32 bpid, | 114 | int dpaa2_io_service_release(struct dpaa2_io *d, u16 bpid, |
115 | const u64 *buffers, unsigned int num_buffers); | 115 | const u64 *buffers, unsigned int num_buffers); |
116 | int dpaa2_io_service_acquire(struct dpaa2_io *d, u32 bpid, | 116 | int dpaa2_io_service_acquire(struct dpaa2_io *d, u16 bpid, |
117 | u64 *buffers, unsigned int num_buffers); | 117 | u64 *buffers, unsigned int num_buffers); |
118 | 118 | ||
119 | struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, | 119 | struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, |
diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index b02f926a0216..45960aa08f4a 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <soc/tegra/bpmp-abi.h> | 23 | #include <soc/tegra/bpmp-abi.h> |
24 | 24 | ||
25 | struct tegra_bpmp_clk; | 25 | struct tegra_bpmp_clk; |
26 | struct tegra_bpmp_ops; | ||
26 | 27 | ||
27 | struct tegra_bpmp_soc { | 28 | struct tegra_bpmp_soc { |
28 | struct { | 29 | struct { |
@@ -32,6 +33,8 @@ struct tegra_bpmp_soc { | |||
32 | unsigned int timeout; | 33 | unsigned int timeout; |
33 | } cpu_tx, thread, cpu_rx; | 34 | } cpu_tx, thread, cpu_rx; |
34 | } channels; | 35 | } channels; |
36 | |||
37 | const struct tegra_bpmp_ops *ops; | ||
35 | unsigned int num_resets; | 38 | unsigned int num_resets; |
36 | }; | 39 | }; |
37 | 40 | ||
@@ -47,6 +50,7 @@ struct tegra_bpmp_channel { | |||
47 | struct tegra_bpmp_mb_data *ob; | 50 | struct tegra_bpmp_mb_data *ob; |
48 | struct completion completion; | 51 | struct completion completion; |
49 | struct tegra_ivc *ivc; | 52 | struct tegra_ivc *ivc; |
53 | unsigned int index; | ||
50 | }; | 54 | }; |
51 | 55 | ||
52 | typedef void (*tegra_bpmp_mrq_handler_t)(unsigned int mrq, | 56 | typedef void (*tegra_bpmp_mrq_handler_t)(unsigned int mrq, |
@@ -63,12 +67,7 @@ struct tegra_bpmp_mrq { | |||
63 | struct tegra_bpmp { | 67 | struct tegra_bpmp { |
64 | const struct tegra_bpmp_soc *soc; | 68 | const struct tegra_bpmp_soc *soc; |
65 | struct device *dev; | 69 | struct device *dev; |
66 | 70 | void *priv; | |
67 | struct { | ||
68 | struct gen_pool *pool; | ||
69 | dma_addr_t phys; | ||
70 | void *virt; | ||
71 | } tx, rx; | ||
72 | 71 | ||
73 | struct { | 72 | struct { |
74 | struct mbox_client client; | 73 | struct mbox_client client; |
@@ -173,6 +172,8 @@ static inline bool tegra_bpmp_mrq_is_supported(struct tegra_bpmp *bpmp, | |||
173 | } | 172 | } |
174 | #endif | 173 | #endif |
175 | 174 | ||
175 | void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp); | ||
176 | |||
176 | #if IS_ENABLED(CONFIG_CLK_TEGRA_BPMP) | 177 | #if IS_ENABLED(CONFIG_CLK_TEGRA_BPMP) |
177 | int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp); | 178 | int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp); |
178 | #else | 179 | #else |
diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index a9db1b501de1..b32ee5d82dd4 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h | |||
@@ -161,7 +161,6 @@ enum tegra_io_pad { | |||
161 | #define TEGRA_IO_RAIL_LVDS TEGRA_IO_PAD_LVDS | 161 | #define TEGRA_IO_RAIL_LVDS TEGRA_IO_PAD_LVDS |
162 | 162 | ||
163 | #ifdef CONFIG_SOC_TEGRA_PMC | 163 | #ifdef CONFIG_SOC_TEGRA_PMC |
164 | int tegra_powergate_is_powered(unsigned int id); | ||
165 | int tegra_powergate_power_on(unsigned int id); | 164 | int tegra_powergate_power_on(unsigned int id); |
166 | int tegra_powergate_power_off(unsigned int id); | 165 | int tegra_powergate_power_off(unsigned int id); |
167 | int tegra_powergate_remove_clamping(unsigned int id); | 166 | int tegra_powergate_remove_clamping(unsigned int id); |
@@ -182,11 +181,6 @@ void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); | |||
182 | void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); | 181 | void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); |
183 | 182 | ||
184 | #else | 183 | #else |
185 | static inline int tegra_powergate_is_powered(unsigned int id) | ||
186 | { | ||
187 | return -ENOSYS; | ||
188 | } | ||
189 | |||
190 | static inline int tegra_powergate_power_on(unsigned int id) | 184 | static inline int tegra_powergate_power_on(unsigned int id) |
191 | { | 185 | { |
192 | return -ENOSYS; | 186 | return -ENOSYS; |
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 293004499b4d..160718383a71 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c | |||
@@ -225,5 +225,8 @@ int main(void) | |||
225 | DEVID_FIELD(typec_device_id, svid); | 225 | DEVID_FIELD(typec_device_id, svid); |
226 | DEVID_FIELD(typec_device_id, mode); | 226 | DEVID_FIELD(typec_device_id, mode); |
227 | 227 | ||
228 | DEVID(tee_client_device_id); | ||
229 | DEVID_FIELD(tee_client_device_id, uuid); | ||
230 | |||
228 | return 0; | 231 | return 0; |
229 | } | 232 | } |
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index a37af7d71973..d0e41723627f 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c | |||
@@ -37,6 +37,9 @@ typedef unsigned char __u8; | |||
37 | typedef struct { | 37 | typedef struct { |
38 | __u8 b[16]; | 38 | __u8 b[16]; |
39 | } uuid_le; | 39 | } uuid_le; |
40 | typedef struct { | ||
41 | __u8 b[16]; | ||
42 | } uuid_t; | ||
40 | 43 | ||
41 | /* Big exception to the "don't include kernel headers into userspace, which | 44 | /* Big exception to the "don't include kernel headers into userspace, which |
42 | * even potentially has different endianness and word sizes, since | 45 | * even potentially has different endianness and word sizes, since |
@@ -1287,6 +1290,21 @@ static int do_typec_entry(const char *filename, void *symval, char *alias) | |||
1287 | return 1; | 1290 | return 1; |
1288 | } | 1291 | } |
1289 | 1292 | ||
1293 | /* Looks like: tee:uuid */ | ||
1294 | static int do_tee_entry(const char *filename, void *symval, char *alias) | ||
1295 | { | ||
1296 | DEF_FIELD(symval, tee_client_device_id, uuid); | ||
1297 | |||
1298 | sprintf(alias, "tee:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", | ||
1299 | uuid.b[0], uuid.b[1], uuid.b[2], uuid.b[3], uuid.b[4], | ||
1300 | uuid.b[5], uuid.b[6], uuid.b[7], uuid.b[8], uuid.b[9], | ||
1301 | uuid.b[10], uuid.b[11], uuid.b[12], uuid.b[13], uuid.b[14], | ||
1302 | uuid.b[15]); | ||
1303 | |||
1304 | add_wildcard(alias); | ||
1305 | return 1; | ||
1306 | } | ||
1307 | |||
1290 | /* Does namelen bytes of name exactly match the symbol? */ | 1308 | /* Does namelen bytes of name exactly match the symbol? */ |
1291 | static bool sym_is(const char *name, unsigned namelen, const char *symbol) | 1309 | static bool sym_is(const char *name, unsigned namelen, const char *symbol) |
1292 | { | 1310 | { |
@@ -1357,6 +1375,7 @@ static const struct devtable devtable[] = { | |||
1357 | {"fslmc", SIZE_fsl_mc_device_id, do_fsl_mc_entry}, | 1375 | {"fslmc", SIZE_fsl_mc_device_id, do_fsl_mc_entry}, |
1358 | {"tbsvc", SIZE_tb_service_id, do_tbsvc_entry}, | 1376 | {"tbsvc", SIZE_tb_service_id, do_tbsvc_entry}, |
1359 | {"typec", SIZE_typec_device_id, do_typec_entry}, | 1377 | {"typec", SIZE_typec_device_id, do_typec_entry}, |
1378 | {"tee", SIZE_tee_client_device_id, do_tee_entry}, | ||
1360 | }; | 1379 | }; |
1361 | 1380 | ||
1362 | /* Create MODULE_ALIAS() statements. | 1381 | /* Create MODULE_ALIAS() statements. |