diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-05-04 16:50:47 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-05-04 16:50:47 -0400 |
commit | 512eae392abe7b5be3c1fe69b3d8cf54bb0ff3f2 (patch) | |
tree | 76210fbe08a14c31dc824cc224747b7b3b7e1339 | |
parent | 04974df8049fc4240d22759a91e035082ccd18b4 (diff) | |
parent | 83cb0e4d837af4348cc218638e9d6daddd21d260 (diff) |
Merge tag 'PR_4.7_20160503' of https://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq into pm-devfreq
Pull devfreq material for v4.7 from MyungJoo Ham.
"Updates:
- Passive governor: for SoC subsystems that may either
have an independent voltage rail or have a parent subsystem
or collegue subsystem sharing a voltage rail, when there
is a parent of a collegue that is going to be the owner
of the voltage rail, the dependent subsystem may use the
passive governor.
- Consolidated exynos bus/mem-if driver: now we have a single
driver that supports (almost) all Exynos SoC's bus/mem-if subsystems.
- New devfreq drivers included: Exynos NoC probe"
* tag 'PR_4.7_20160503' of https://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq:
PM / devfreq: style/typo fixes
PM / devfreq: exynos: Add the detailed correlation for Exynos5422 bus
PM / devfreq: event: Find the instance of devfreq-event device by using phandle
PM / devfreq: event: Add new Exynos NoC probe driver
MAINTAINERS: Add samsung bus frequency driver entry
PM / devfreq: exynos: Remove unused exynos4/5 busfreq driver
PM / devfreq: exynos: Add the detailed correlation between sub-blocks and power line
PM / devfreq: exynos: Update documentation for bus devices using passive governor
PM / devfreq: exynos: Add support of bus frequency of sub-blocks using passive governor
PM / devfreq: Add new passive governor
PM / devfreq: Add new DEVFREQ_TRANSITION_NOTIFIER notifier
PM / devfreq: Add devfreq_get_devfreq_by_phandle()
PM / devfreq: exynos: Add documentation for generic exynos bus frequency driver
PM / devfreq: exynos: Add generic exynos bus frequency driver
-rw-r--r-- | Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt | 26 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/devfreq/exynos-bus.txt | 409 | ||||
-rw-r--r-- | MAINTAINERS | 9 | ||||
-rw-r--r-- | drivers/devfreq/Kconfig | 36 | ||||
-rw-r--r-- | drivers/devfreq/Makefile | 4 | ||||
-rw-r--r-- | drivers/devfreq/devfreq-event.c | 5 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 207 | ||||
-rw-r--r-- | drivers/devfreq/event/Kconfig | 8 | ||||
-rw-r--r-- | drivers/devfreq/event/Makefile | 2 | ||||
-rw-r--r-- | drivers/devfreq/event/exynos-nocp.c | 304 | ||||
-rw-r--r-- | drivers/devfreq/event/exynos-nocp.h | 78 | ||||
-rw-r--r-- | drivers/devfreq/exynos-bus.c | 570 | ||||
-rw-r--r-- | drivers/devfreq/exynos/Makefile | 3 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos4_bus.c | 1055 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos4_bus.h | 110 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos5_bus.c | 431 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos_ppmu.c | 119 | ||||
-rw-r--r-- | drivers/devfreq/exynos/exynos_ppmu.h | 86 | ||||
-rw-r--r-- | drivers/devfreq/governor_passive.c | 205 | ||||
-rw-r--r-- | include/linux/devfreq.h | 99 |
20 files changed, 1942 insertions, 1824 deletions
diff --git a/Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt b/Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt new file mode 100644 index 000000000000..fd459f00aa5a --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/exynos-nocp.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | |||
2 | * Samsung Exynos NoC (Network on Chip) Probe device | ||
3 | |||
4 | The Samsung Exynos542x SoC has NoC (Network on Chip) Probe for NoC bus. | ||
5 | NoC provides the primitive values to get the performance data. The packets | ||
6 | that the Network on Chip (NoC) probes detects are transported over | ||
7 | the network infrastructure to observer units. You can configure probes to | ||
8 | capture packets with header or data on the data request response network, | ||
9 | or as traffic debug or statistic collectors. Exynos542x bus has multiple | ||
10 | NoC probes to provide bandwidth information about behavior of the SoC | ||
11 | that you can use while analyzing system performance. | ||
12 | |||
13 | Required properties: | ||
14 | - compatible: Should be "samsung,exynos5420-nocp" | ||
15 | - reg: physical base address of each NoC Probe and length of memory mapped region. | ||
16 | |||
17 | Optional properties: | ||
18 | - clock-names : the name of clock used by the NoC Probe, "nocp" | ||
19 | - clocks : phandles for clock specified in "clock-names" property | ||
20 | |||
21 | Example : NoC Probe nodes in Device Tree are listed below. | ||
22 | |||
23 | nocp_mem0_0: nocp@10CA1000 { | ||
24 | compatible = "samsung,exynos5420-nocp"; | ||
25 | reg = <0x10CA1000 0x200>; | ||
26 | }; | ||
diff --git a/Documentation/devicetree/bindings/devfreq/exynos-bus.txt b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt new file mode 100644 index 000000000000..d3ec8e676b6b --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/exynos-bus.txt | |||
@@ -0,0 +1,409 @@ | |||
1 | * Generic Exynos Bus frequency device | ||
2 | |||
3 | The Samsung Exynos SoC has many buses for data transfer between DRAM | ||
4 | and sub-blocks in SoC. Most Exynos SoCs share the common architecture | ||
5 | for buses. Generally, each bus of Exynos SoC includes a source clock | ||
6 | and a power line, which are able to change the clock frequency | ||
7 | of the bus in runtime. To monitor the usage of each bus in runtime, | ||
8 | the driver uses the PPMU (Platform Performance Monitoring Unit), which | ||
9 | is able to measure the current load of sub-blocks. | ||
10 | |||
11 | The Exynos SoC includes the various sub-blocks which have the each AXI bus. | ||
12 | The each AXI bus has the owned source clock but, has not the only owned | ||
13 | power line. The power line might be shared among one more sub-blocks. | ||
14 | So, we can divide into two type of device as the role of each sub-block. | ||
15 | There are two type of bus devices as following: | ||
16 | - parent bus device | ||
17 | - passive bus device | ||
18 | |||
19 | Basically, parent and passive bus device share the same power line. | ||
20 | The parent bus device can only change the voltage of shared power line | ||
21 | and the rest bus devices (passive bus device) depend on the decision of | ||
22 | the parent bus device. If there are three blocks which share the VDD_xxx | ||
23 | power line, Only one block should be parent device and then the rest blocks | ||
24 | should depend on the parent device as passive device. | ||
25 | |||
26 | VDD_xxx |--- A block (parent) | ||
27 | |--- B block (passive) | ||
28 | |--- C block (passive) | ||
29 | |||
30 | There are a little different composition among Exynos SoC because each Exynos | ||
31 | SoC has different sub-blocks. Therefore, such difference should be specified | ||
32 | in devicetree file instead of each device driver. In result, this driver | ||
33 | is able to support the bus frequency for all Exynos SoCs. | ||
34 | |||
35 | Required properties for all bus devices: | ||
36 | - compatible: Should be "samsung,exynos-bus". | ||
37 | - clock-names : the name of clock used by the bus, "bus". | ||
38 | - clocks : phandles for clock specified in "clock-names" property. | ||
39 | - operating-points-v2: the OPP table including frequency/voltage information | ||
40 | to support DVFS (Dynamic Voltage/Frequency Scaling) feature. | ||
41 | |||
42 | Required properties only for parent bus device: | ||
43 | - vdd-supply: the regulator to provide the buses with the voltage. | ||
44 | - devfreq-events: the devfreq-event device to monitor the current utilization | ||
45 | of buses. | ||
46 | |||
47 | Required properties only for passive bus device: | ||
48 | - devfreq: the parent bus device. | ||
49 | |||
50 | Optional properties only for parent bus device: | ||
51 | - exynos,saturation-ratio: the percentage value which is used to calibrate | ||
52 | the performance count against total cycle count. | ||
53 | - exynos,voltage-tolerance: the percentage value for bus voltage tolerance | ||
54 | which is used to calculate the max voltage. | ||
55 | |||
56 | Detailed correlation between sub-blocks and power line according to Exynos SoC: | ||
57 | - In case of Exynos3250, there are two power line as following: | ||
58 | VDD_MIF |--- DMC | ||
59 | |||
60 | VDD_INT |--- LEFTBUS (parent device) | ||
61 | |--- PERIL | ||
62 | |--- MFC | ||
63 | |--- G3D | ||
64 | |--- RIGHTBUS | ||
65 | |--- PERIR | ||
66 | |--- FSYS | ||
67 | |--- LCD0 | ||
68 | |--- PERIR | ||
69 | |--- ISP | ||
70 | |--- CAM | ||
71 | |||
72 | - In case of Exynos4210, there is one power line as following: | ||
73 | VDD_INT |--- DMC (parent device) | ||
74 | |--- LEFTBUS | ||
75 | |--- PERIL | ||
76 | |--- MFC(L) | ||
77 | |--- G3D | ||
78 | |--- TV | ||
79 | |--- LCD0 | ||
80 | |--- RIGHTBUS | ||
81 | |--- PERIR | ||
82 | |--- MFC(R) | ||
83 | |--- CAM | ||
84 | |--- FSYS | ||
85 | |--- GPS | ||
86 | |--- LCD0 | ||
87 | |--- LCD1 | ||
88 | |||
89 | - In case of Exynos4x12, there are two power line as following: | ||
90 | VDD_MIF |--- DMC | ||
91 | |||
92 | VDD_INT |--- LEFTBUS (parent device) | ||
93 | |--- PERIL | ||
94 | |--- MFC(L) | ||
95 | |--- G3D | ||
96 | |--- TV | ||
97 | |--- IMAGE | ||
98 | |--- RIGHTBUS | ||
99 | |--- PERIR | ||
100 | |--- MFC(R) | ||
101 | |--- CAM | ||
102 | |--- FSYS | ||
103 | |--- GPS | ||
104 | |--- LCD0 | ||
105 | |--- ISP | ||
106 | |||
107 | - In case of Exynos5422, there are two power line as following: | ||
108 | VDD_MIF |--- DREX 0 (parent device, DRAM EXpress controller) | ||
109 | |--- DREX 1 | ||
110 | |||
111 | VDD_INT |--- NoC_Core (parent device) | ||
112 | |--- G2D | ||
113 | |--- G3D | ||
114 | |--- DISP1 | ||
115 | |--- NoC_WCORE | ||
116 | |--- GSCL | ||
117 | |--- MSCL | ||
118 | |--- ISP | ||
119 | |--- MFC | ||
120 | |--- GEN | ||
121 | |--- PERIS | ||
122 | |--- PERIC | ||
123 | |--- FSYS | ||
124 | |--- FSYS2 | ||
125 | |||
126 | Example1: | ||
127 | Show the AXI buses of Exynos3250 SoC. Exynos3250 divides the buses to | ||
128 | power line (regulator). The MIF (Memory Interface) AXI bus is used to | ||
129 | transfer data between DRAM and CPU and uses the VDD_MIF regulator. | ||
130 | |||
131 | - MIF (Memory Interface) block | ||
132 | : VDD_MIF |--- DMC (Dynamic Memory Controller) | ||
133 | |||
134 | - INT (Internal) block | ||
135 | : VDD_INT |--- LEFTBUS (parent device) | ||
136 | |--- PERIL | ||
137 | |--- MFC | ||
138 | |--- G3D | ||
139 | |--- RIGHTBUS | ||
140 | |--- FSYS | ||
141 | |--- LCD0 | ||
142 | |--- PERIR | ||
143 | |--- ISP | ||
144 | |--- CAM | ||
145 | |||
146 | - MIF bus's frequency/voltage table | ||
147 | ----------------------- | ||
148 | |Lv| Freq | Voltage | | ||
149 | ----------------------- | ||
150 | |L1| 50000 |800000 | | ||
151 | |L2| 100000 |800000 | | ||
152 | |L3| 134000 |800000 | | ||
153 | |L4| 200000 |825000 | | ||
154 | |L5| 400000 |875000 | | ||
155 | ----------------------- | ||
156 | |||
157 | - INT bus's frequency/voltage table | ||
158 | ---------------------------------------------------------- | ||
159 | |Block|LEFTBUS|RIGHTBUS|MCUISP |ISP |PERIL ||VDD_INT | | ||
160 | | name| |LCD0 | | | || | | ||
161 | | | |FSYS | | | || | | ||
162 | | | |MFC | | | || | | ||
163 | ---------------------------------------------------------- | ||
164 | |Mode |*parent|passive |passive|passive|passive|| | | ||
165 | ---------------------------------------------------------- | ||
166 | |Lv |Frequency ||Voltage | | ||
167 | ---------------------------------------------------------- | ||
168 | |L1 |50000 |50000 |50000 |50000 |50000 ||900000 | | ||
169 | |L2 |80000 |80000 |80000 |80000 |80000 ||900000 | | ||
170 | |L3 |100000 |100000 |100000 |100000 |100000 ||1000000 | | ||
171 | |L4 |134000 |134000 |200000 |200000 | ||1000000 | | ||
172 | |L5 |200000 |200000 |400000 |300000 | ||1000000 | | ||
173 | ---------------------------------------------------------- | ||
174 | |||
175 | Example2 : | ||
176 | The bus of DMC (Dynamic Memory Controller) block in exynos3250.dtsi | ||
177 | is listed below: | ||
178 | |||
179 | bus_dmc: bus_dmc { | ||
180 | compatible = "samsung,exynos-bus"; | ||
181 | clocks = <&cmu_dmc CLK_DIV_DMC>; | ||
182 | clock-names = "bus"; | ||
183 | operating-points-v2 = <&bus_dmc_opp_table>; | ||
184 | status = "disabled"; | ||
185 | }; | ||
186 | |||
187 | bus_dmc_opp_table: opp_table1 { | ||
188 | compatible = "operating-points-v2"; | ||
189 | opp-shared; | ||
190 | |||
191 | opp@50000000 { | ||
192 | opp-hz = /bits/ 64 <50000000>; | ||
193 | opp-microvolt = <800000>; | ||
194 | }; | ||
195 | opp@100000000 { | ||
196 | opp-hz = /bits/ 64 <100000000>; | ||
197 | opp-microvolt = <800000>; | ||
198 | }; | ||
199 | opp@134000000 { | ||
200 | opp-hz = /bits/ 64 <134000000>; | ||
201 | opp-microvolt = <800000>; | ||
202 | }; | ||
203 | opp@200000000 { | ||
204 | opp-hz = /bits/ 64 <200000000>; | ||
205 | opp-microvolt = <825000>; | ||
206 | }; | ||
207 | opp@400000000 { | ||
208 | opp-hz = /bits/ 64 <400000000>; | ||
209 | opp-microvolt = <875000>; | ||
210 | }; | ||
211 | }; | ||
212 | |||
213 | bus_leftbus: bus_leftbus { | ||
214 | compatible = "samsung,exynos-bus"; | ||
215 | clocks = <&cmu CLK_DIV_GDL>; | ||
216 | clock-names = "bus"; | ||
217 | operating-points-v2 = <&bus_leftbus_opp_table>; | ||
218 | status = "disabled"; | ||
219 | }; | ||
220 | |||
221 | bus_rightbus: bus_rightbus { | ||
222 | compatible = "samsung,exynos-bus"; | ||
223 | clocks = <&cmu CLK_DIV_GDR>; | ||
224 | clock-names = "bus"; | ||
225 | operating-points-v2 = <&bus_leftbus_opp_table>; | ||
226 | status = "disabled"; | ||
227 | }; | ||
228 | |||
229 | bus_lcd0: bus_lcd0 { | ||
230 | compatible = "samsung,exynos-bus"; | ||
231 | clocks = <&cmu CLK_DIV_ACLK_160>; | ||
232 | clock-names = "bus"; | ||
233 | operating-points-v2 = <&bus_leftbus_opp_table>; | ||
234 | status = "disabled"; | ||
235 | }; | ||
236 | |||
237 | bus_fsys: bus_fsys { | ||
238 | compatible = "samsung,exynos-bus"; | ||
239 | clocks = <&cmu CLK_DIV_ACLK_200>; | ||
240 | clock-names = "bus"; | ||
241 | operating-points-v2 = <&bus_leftbus_opp_table>; | ||
242 | status = "disabled"; | ||
243 | }; | ||
244 | |||
245 | bus_mcuisp: bus_mcuisp { | ||
246 | compatible = "samsung,exynos-bus"; | ||
247 | clocks = <&cmu CLK_DIV_ACLK_400_MCUISP>; | ||
248 | clock-names = "bus"; | ||
249 | operating-points-v2 = <&bus_mcuisp_opp_table>; | ||
250 | status = "disabled"; | ||
251 | }; | ||
252 | |||
253 | bus_isp: bus_isp { | ||
254 | compatible = "samsung,exynos-bus"; | ||
255 | clocks = <&cmu CLK_DIV_ACLK_266>; | ||
256 | clock-names = "bus"; | ||
257 | operating-points-v2 = <&bus_isp_opp_table>; | ||
258 | status = "disabled"; | ||
259 | }; | ||
260 | |||
261 | bus_peril: bus_peril { | ||
262 | compatible = "samsung,exynos-bus"; | ||
263 | clocks = <&cmu CLK_DIV_ACLK_100>; | ||
264 | clock-names = "bus"; | ||
265 | operating-points-v2 = <&bus_peril_opp_table>; | ||
266 | status = "disabled"; | ||
267 | }; | ||
268 | |||
269 | bus_mfc: bus_mfc { | ||
270 | compatible = "samsung,exynos-bus"; | ||
271 | clocks = <&cmu CLK_SCLK_MFC>; | ||
272 | clock-names = "bus"; | ||
273 | operating-points-v2 = <&bus_leftbus_opp_table>; | ||
274 | status = "disabled"; | ||
275 | }; | ||
276 | |||
277 | bus_leftbus_opp_table: opp_table1 { | ||
278 | compatible = "operating-points-v2"; | ||
279 | opp-shared; | ||
280 | |||
281 | opp@50000000 { | ||
282 | opp-hz = /bits/ 64 <50000000>; | ||
283 | opp-microvolt = <900000>; | ||
284 | }; | ||
285 | opp@80000000 { | ||
286 | opp-hz = /bits/ 64 <80000000>; | ||
287 | opp-microvolt = <900000>; | ||
288 | }; | ||
289 | opp@100000000 { | ||
290 | opp-hz = /bits/ 64 <100000000>; | ||
291 | opp-microvolt = <1000000>; | ||
292 | }; | ||
293 | opp@134000000 { | ||
294 | opp-hz = /bits/ 64 <134000000>; | ||
295 | opp-microvolt = <1000000>; | ||
296 | }; | ||
297 | opp@200000000 { | ||
298 | opp-hz = /bits/ 64 <200000000>; | ||
299 | opp-microvolt = <1000000>; | ||
300 | }; | ||
301 | }; | ||
302 | |||
303 | bus_mcuisp_opp_table: opp_table2 { | ||
304 | compatible = "operating-points-v2"; | ||
305 | opp-shared; | ||
306 | |||
307 | opp@50000000 { | ||
308 | opp-hz = /bits/ 64 <50000000>; | ||
309 | }; | ||
310 | opp@80000000 { | ||
311 | opp-hz = /bits/ 64 <80000000>; | ||
312 | }; | ||
313 | opp@100000000 { | ||
314 | opp-hz = /bits/ 64 <100000000>; | ||
315 | }; | ||
316 | opp@200000000 { | ||
317 | opp-hz = /bits/ 64 <200000000>; | ||
318 | }; | ||
319 | opp@400000000 { | ||
320 | opp-hz = /bits/ 64 <400000000>; | ||
321 | }; | ||
322 | }; | ||
323 | |||
324 | bus_isp_opp_table: opp_table3 { | ||
325 | compatible = "operating-points-v2"; | ||
326 | opp-shared; | ||
327 | |||
328 | opp@50000000 { | ||
329 | opp-hz = /bits/ 64 <50000000>; | ||
330 | }; | ||
331 | opp@80000000 { | ||
332 | opp-hz = /bits/ 64 <80000000>; | ||
333 | }; | ||
334 | opp@100000000 { | ||
335 | opp-hz = /bits/ 64 <100000000>; | ||
336 | }; | ||
337 | opp@200000000 { | ||
338 | opp-hz = /bits/ 64 <200000000>; | ||
339 | }; | ||
340 | opp@300000000 { | ||
341 | opp-hz = /bits/ 64 <300000000>; | ||
342 | }; | ||
343 | }; | ||
344 | |||
345 | bus_peril_opp_table: opp_table4 { | ||
346 | compatible = "operating-points-v2"; | ||
347 | opp-shared; | ||
348 | |||
349 | opp@50000000 { | ||
350 | opp-hz = /bits/ 64 <50000000>; | ||
351 | }; | ||
352 | opp@80000000 { | ||
353 | opp-hz = /bits/ 64 <80000000>; | ||
354 | }; | ||
355 | opp@100000000 { | ||
356 | opp-hz = /bits/ 64 <100000000>; | ||
357 | }; | ||
358 | }; | ||
359 | |||
360 | |||
361 | Usage case to handle the frequency and voltage of bus on runtime | ||
362 | in exynos3250-rinato.dts is listed below: | ||
363 | |||
364 | &bus_dmc { | ||
365 | devfreq-events = <&ppmu_dmc0_3>, <&ppmu_dmc1_3>; | ||
366 | vdd-supply = <&buck1_reg>; /* VDD_MIF */ | ||
367 | status = "okay"; | ||
368 | }; | ||
369 | |||
370 | &bus_leftbus { | ||
371 | devfreq-events = <&ppmu_leftbus_3>, <&ppmu_rightbus_3>; | ||
372 | vdd-supply = <&buck3_reg>; | ||
373 | status = "okay"; | ||
374 | }; | ||
375 | |||
376 | &bus_rightbus { | ||
377 | devfreq = <&bus_leftbus>; | ||
378 | status = "okay"; | ||
379 | }; | ||
380 | |||
381 | &bus_lcd0 { | ||
382 | devfreq = <&bus_leftbus>; | ||
383 | status = "okay"; | ||
384 | }; | ||
385 | |||
386 | &bus_fsys { | ||
387 | devfreq = <&bus_leftbus>; | ||
388 | status = "okay"; | ||
389 | }; | ||
390 | |||
391 | &bus_mcuisp { | ||
392 | devfreq = <&bus_leftbus>; | ||
393 | status = "okay"; | ||
394 | }; | ||
395 | |||
396 | &bus_isp { | ||
397 | devfreq = <&bus_leftbus>; | ||
398 | status = "okay"; | ||
399 | }; | ||
400 | |||
401 | &bus_peril { | ||
402 | devfreq = <&bus_leftbus>; | ||
403 | status = "okay"; | ||
404 | }; | ||
405 | |||
406 | &bus_mfc { | ||
407 | devfreq = <&bus_leftbus>; | ||
408 | status = "okay"; | ||
409 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 42e65d128d01..7e9da3aa9851 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -3539,6 +3539,15 @@ F: drivers/devfreq/devfreq-event.c | |||
3539 | F: include/linux/devfreq-event.h | 3539 | F: include/linux/devfreq-event.h |
3540 | F: Documentation/devicetree/bindings/devfreq/event/ | 3540 | F: Documentation/devicetree/bindings/devfreq/event/ |
3541 | 3541 | ||
3542 | BUS FREQUENCY DRIVER FOR SAMSUNG EXYNOS | ||
3543 | M: Chanwoo Choi <cw00.choi@samsung.com> | ||
3544 | L: linux-pm@vger.kernel.org | ||
3545 | L: linux-samsung-soc@vger.kernel.org | ||
3546 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git | ||
3547 | S: Maintained | ||
3548 | F: drivers/devfreq/exynos-bus.c | ||
3549 | F: Documentation/devicetree/bindings/devfreq/exynos-bus.txt | ||
3550 | |||
3542 | DEVICE NUMBER REGISTRY | 3551 | DEVICE NUMBER REGISTRY |
3543 | M: Torben Mathiasen <device@lanana.org> | 3552 | M: Torben Mathiasen <device@lanana.org> |
3544 | W: http://lanana.org/docs/device-list/index.html | 3553 | W: http://lanana.org/docs/device-list/index.html |
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 4de78c552251..78dac0e9da11 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig | |||
@@ -64,30 +64,32 @@ config DEVFREQ_GOV_USERSPACE | |||
64 | Otherwise, the governor does not change the frequency | 64 | Otherwise, the governor does not change the frequency |
65 | given at the initialization. | 65 | given at the initialization. |
66 | 66 | ||
67 | config DEVFREQ_GOV_PASSIVE | ||
68 | tristate "Passive" | ||
69 | help | ||
70 | Sets the frequency based on the frequency of its parent devfreq | ||
71 | device. This governor does not change the frequency by itself | ||
72 | through sysfs entries. The passive governor recommends that | ||
73 | devfreq device uses the OPP table to get the frequency/voltage. | ||
74 | |||
67 | comment "DEVFREQ Drivers" | 75 | comment "DEVFREQ Drivers" |
68 | 76 | ||
69 | config ARM_EXYNOS4_BUS_DEVFREQ | 77 | config ARM_EXYNOS_BUS_DEVFREQ |
70 | bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" | 78 | bool "ARM EXYNOS Generic Memory Bus DEVFREQ Driver" |
71 | depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM | 79 | depends on ARCH_EXYNOS |
72 | select DEVFREQ_GOV_SIMPLE_ONDEMAND | 80 | select DEVFREQ_GOV_SIMPLE_ONDEMAND |
81 | select DEVFREQ_GOV_PASSIVE | ||
82 | select DEVFREQ_EVENT_EXYNOS_PPMU | ||
83 | select PM_DEVFREQ_EVENT | ||
73 | select PM_OPP | 84 | select PM_OPP |
74 | help | 85 | help |
75 | This adds the DEVFREQ driver for Exynos4210 memory bus (vdd_int) | 86 | This adds the common DEVFREQ driver for Exynos Memory bus. Exynos |
76 | and Exynos4212/4412 memory interface and bus (vdd_mif + vdd_int). | 87 | Memory bus has one more group of memory bus (e.g, MIF and INT block). |
77 | It reads PPMU counters of memory controllers and adjusts | 88 | Each memory bus group could contain many memoby bus block. It reads |
78 | the operating frequencies and voltages with OPP support. | 89 | PPMU counters of memory controllers by using DEVFREQ-event device |
90 | and adjusts the operating frequencies and voltages with OPP support. | ||
79 | This does not yet operate with optimal voltages. | 91 | This does not yet operate with optimal voltages. |
80 | 92 | ||
81 | config ARM_EXYNOS5_BUS_DEVFREQ | ||
82 | tristate "ARM Exynos5250 Bus DEVFREQ Driver" | ||
83 | depends on SOC_EXYNOS5250 | ||
84 | select DEVFREQ_GOV_SIMPLE_ONDEMAND | ||
85 | select PM_OPP | ||
86 | help | ||
87 | This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int). | ||
88 | It reads PPMU counters of memory controllers and adjusts the | ||
89 | operating frequencies and voltages with OPP support. | ||
90 | |||
91 | config ARM_TEGRA_DEVFREQ | 93 | config ARM_TEGRA_DEVFREQ |
92 | tristate "Tegra DEVFREQ Driver" | 94 | tristate "Tegra DEVFREQ Driver" |
93 | depends on ARCH_TEGRA_124_SOC | 95 | depends on ARCH_TEGRA_124_SOC |
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 5134f9ee983d..09f11d9d40d5 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile | |||
@@ -4,10 +4,10 @@ obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o | |||
4 | obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o | 4 | obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o |
5 | obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o | 5 | obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o |
6 | obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o | 6 | obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o |
7 | obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o | ||
7 | 8 | ||
8 | # DEVFREQ Drivers | 9 | # DEVFREQ Drivers |
9 | obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ | 10 | obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o |
10 | obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ | ||
11 | obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o | 11 | obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o |
12 | 12 | ||
13 | # DEVFREQ Event Drivers | 13 | # DEVFREQ Event Drivers |
diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c index 38bf144ca147..39b048eda2ce 100644 --- a/drivers/devfreq/devfreq-event.c +++ b/drivers/devfreq/devfreq-event.c | |||
@@ -235,6 +235,11 @@ struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev, | |||
235 | 235 | ||
236 | mutex_lock(&devfreq_event_list_lock); | 236 | mutex_lock(&devfreq_event_list_lock); |
237 | list_for_each_entry(edev, &devfreq_event_list, node) { | 237 | list_for_each_entry(edev, &devfreq_event_list, node) { |
238 | if (edev->dev.parent && edev->dev.parent->of_node == node) | ||
239 | goto out; | ||
240 | } | ||
241 | |||
242 | list_for_each_entry(edev, &devfreq_event_list, node) { | ||
238 | if (!strcmp(edev->desc->name, node->name)) | 243 | if (!strcmp(edev->desc->name, node->name)) |
239 | goto out; | 244 | goto out; |
240 | } | 245 | } |
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 984c5e9e7bdd..1d6c803804d5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/list.h> | 25 | #include <linux/list.h> |
26 | #include <linux/printk.h> | 26 | #include <linux/printk.h> |
27 | #include <linux/hrtimer.h> | 27 | #include <linux/hrtimer.h> |
28 | #include <linux/of.h> | ||
28 | #include "governor.h" | 29 | #include "governor.h" |
29 | 30 | ||
30 | static struct class *devfreq_class; | 31 | static struct class *devfreq_class; |
@@ -188,6 +189,29 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
188 | return ERR_PTR(-ENODEV); | 189 | return ERR_PTR(-ENODEV); |
189 | } | 190 | } |
190 | 191 | ||
192 | static int devfreq_notify_transition(struct devfreq *devfreq, | ||
193 | struct devfreq_freqs *freqs, unsigned int state) | ||
194 | { | ||
195 | if (!devfreq) | ||
196 | return -EINVAL; | ||
197 | |||
198 | switch (state) { | ||
199 | case DEVFREQ_PRECHANGE: | ||
200 | srcu_notifier_call_chain(&devfreq->transition_notifier_list, | ||
201 | DEVFREQ_PRECHANGE, freqs); | ||
202 | break; | ||
203 | |||
204 | case DEVFREQ_POSTCHANGE: | ||
205 | srcu_notifier_call_chain(&devfreq->transition_notifier_list, | ||
206 | DEVFREQ_POSTCHANGE, freqs); | ||
207 | break; | ||
208 | default: | ||
209 | return -EINVAL; | ||
210 | } | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
191 | /* Load monitoring helper functions for governors use */ | 215 | /* Load monitoring helper functions for governors use */ |
192 | 216 | ||
193 | /** | 217 | /** |
@@ -199,7 +223,8 @@ static struct devfreq_governor *find_devfreq_governor(const char *name) | |||
199 | */ | 223 | */ |
200 | int update_devfreq(struct devfreq *devfreq) | 224 | int update_devfreq(struct devfreq *devfreq) |
201 | { | 225 | { |
202 | unsigned long freq; | 226 | struct devfreq_freqs freqs; |
227 | unsigned long freq, cur_freq; | ||
203 | int err = 0; | 228 | int err = 0; |
204 | u32 flags = 0; | 229 | u32 flags = 0; |
205 | 230 | ||
@@ -233,10 +258,22 @@ int update_devfreq(struct devfreq *devfreq) | |||
233 | flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ | 258 | flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ |
234 | } | 259 | } |
235 | 260 | ||
261 | if (devfreq->profile->get_cur_freq) | ||
262 | devfreq->profile->get_cur_freq(devfreq->dev.parent, &cur_freq); | ||
263 | else | ||
264 | cur_freq = devfreq->previous_freq; | ||
265 | |||
266 | freqs.old = cur_freq; | ||
267 | freqs.new = freq; | ||
268 | devfreq_notify_transition(devfreq, &freqs, DEVFREQ_PRECHANGE); | ||
269 | |||
236 | err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); | 270 | err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); |
237 | if (err) | 271 | if (err) |
238 | return err; | 272 | return err; |
239 | 273 | ||
274 | freqs.new = freq; | ||
275 | devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); | ||
276 | |||
240 | if (devfreq->profile->freq_table) | 277 | if (devfreq->profile->freq_table) |
241 | if (devfreq_update_status(devfreq, freq)) | 278 | if (devfreq_update_status(devfreq, freq)) |
242 | dev_err(&devfreq->dev, | 279 | dev_err(&devfreq->dev, |
@@ -541,6 +578,8 @@ struct devfreq *devfreq_add_device(struct device *dev, | |||
541 | goto err_out; | 578 | goto err_out; |
542 | } | 579 | } |
543 | 580 | ||
581 | srcu_init_notifier_head(&devfreq->transition_notifier_list); | ||
582 | |||
544 | mutex_unlock(&devfreq->lock); | 583 | mutex_unlock(&devfreq->lock); |
545 | 584 | ||
546 | mutex_lock(&devfreq_list_lock); | 585 | mutex_lock(&devfreq_list_lock); |
@@ -639,6 +678,49 @@ struct devfreq *devm_devfreq_add_device(struct device *dev, | |||
639 | } | 678 | } |
640 | EXPORT_SYMBOL(devm_devfreq_add_device); | 679 | EXPORT_SYMBOL(devm_devfreq_add_device); |
641 | 680 | ||
681 | #ifdef CONFIG_OF | ||
682 | /* | ||
683 | * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree | ||
684 | * @dev - instance to the given device | ||
685 | * @index - index into list of devfreq | ||
686 | * | ||
687 | * return the instance of devfreq device | ||
688 | */ | ||
689 | struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) | ||
690 | { | ||
691 | struct device_node *node; | ||
692 | struct devfreq *devfreq; | ||
693 | |||
694 | if (!dev) | ||
695 | return ERR_PTR(-EINVAL); | ||
696 | |||
697 | if (!dev->of_node) | ||
698 | return ERR_PTR(-EINVAL); | ||
699 | |||
700 | node = of_parse_phandle(dev->of_node, "devfreq", index); | ||
701 | if (!node) | ||
702 | return ERR_PTR(-ENODEV); | ||
703 | |||
704 | mutex_lock(&devfreq_list_lock); | ||
705 | list_for_each_entry(devfreq, &devfreq_list, node) { | ||
706 | if (devfreq->dev.parent | ||
707 | && devfreq->dev.parent->of_node == node) { | ||
708 | mutex_unlock(&devfreq_list_lock); | ||
709 | return devfreq; | ||
710 | } | ||
711 | } | ||
712 | mutex_unlock(&devfreq_list_lock); | ||
713 | |||
714 | return ERR_PTR(-EPROBE_DEFER); | ||
715 | } | ||
716 | #else | ||
717 | struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) | ||
718 | { | ||
719 | return ERR_PTR(-ENODEV); | ||
720 | } | ||
721 | #endif /* CONFIG_OF */ | ||
722 | EXPORT_SYMBOL_GPL(devfreq_get_devfreq_by_phandle); | ||
723 | |||
642 | /** | 724 | /** |
643 | * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() | 725 | * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device() |
644 | * @dev: the device to add devfreq feature. | 726 | * @dev: the device to add devfreq feature. |
@@ -1266,6 +1348,129 @@ void devm_devfreq_unregister_opp_notifier(struct device *dev, | |||
1266 | } | 1348 | } |
1267 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); | 1349 | EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); |
1268 | 1350 | ||
1351 | /** | ||
1352 | * devfreq_register_notifier() - Register a driver with devfreq | ||
1353 | * @devfreq: The devfreq object. | ||
1354 | * @nb: The notifier block to register. | ||
1355 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1356 | */ | ||
1357 | int devfreq_register_notifier(struct devfreq *devfreq, | ||
1358 | struct notifier_block *nb, | ||
1359 | unsigned int list) | ||
1360 | { | ||
1361 | int ret = 0; | ||
1362 | |||
1363 | if (!devfreq) | ||
1364 | return -EINVAL; | ||
1365 | |||
1366 | switch (list) { | ||
1367 | case DEVFREQ_TRANSITION_NOTIFIER: | ||
1368 | ret = srcu_notifier_chain_register( | ||
1369 | &devfreq->transition_notifier_list, nb); | ||
1370 | break; | ||
1371 | default: | ||
1372 | ret = -EINVAL; | ||
1373 | } | ||
1374 | |||
1375 | return ret; | ||
1376 | } | ||
1377 | EXPORT_SYMBOL(devfreq_register_notifier); | ||
1378 | |||
1379 | /* | ||
1380 | * devfreq_unregister_notifier() - Unregister a driver with devfreq | ||
1381 | * @devfreq: The devfreq object. | ||
1382 | * @nb: The notifier block to be unregistered. | ||
1383 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1384 | */ | ||
1385 | int devfreq_unregister_notifier(struct devfreq *devfreq, | ||
1386 | struct notifier_block *nb, | ||
1387 | unsigned int list) | ||
1388 | { | ||
1389 | int ret = 0; | ||
1390 | |||
1391 | if (!devfreq) | ||
1392 | return -EINVAL; | ||
1393 | |||
1394 | switch (list) { | ||
1395 | case DEVFREQ_TRANSITION_NOTIFIER: | ||
1396 | ret = srcu_notifier_chain_unregister( | ||
1397 | &devfreq->transition_notifier_list, nb); | ||
1398 | break; | ||
1399 | default: | ||
1400 | ret = -EINVAL; | ||
1401 | } | ||
1402 | |||
1403 | return ret; | ||
1404 | } | ||
1405 | EXPORT_SYMBOL(devfreq_unregister_notifier); | ||
1406 | |||
1407 | struct devfreq_notifier_devres { | ||
1408 | struct devfreq *devfreq; | ||
1409 | struct notifier_block *nb; | ||
1410 | unsigned int list; | ||
1411 | }; | ||
1412 | |||
1413 | static void devm_devfreq_notifier_release(struct device *dev, void *res) | ||
1414 | { | ||
1415 | struct devfreq_notifier_devres *this = res; | ||
1416 | |||
1417 | devfreq_unregister_notifier(this->devfreq, this->nb, this->list); | ||
1418 | } | ||
1419 | |||
1420 | /** | ||
1421 | * devm_devfreq_register_notifier() | ||
1422 | - Resource-managed devfreq_register_notifier() | ||
1423 | * @dev: The devfreq user device. (parent of devfreq) | ||
1424 | * @devfreq: The devfreq object. | ||
1425 | * @nb: The notifier block to be unregistered. | ||
1426 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1427 | */ | ||
1428 | int devm_devfreq_register_notifier(struct device *dev, | ||
1429 | struct devfreq *devfreq, | ||
1430 | struct notifier_block *nb, | ||
1431 | unsigned int list) | ||
1432 | { | ||
1433 | struct devfreq_notifier_devres *ptr; | ||
1434 | int ret; | ||
1435 | |||
1436 | ptr = devres_alloc(devm_devfreq_notifier_release, sizeof(*ptr), | ||
1437 | GFP_KERNEL); | ||
1438 | if (!ptr) | ||
1439 | return -ENOMEM; | ||
1440 | |||
1441 | ret = devfreq_register_notifier(devfreq, nb, list); | ||
1442 | if (ret) { | ||
1443 | devres_free(ptr); | ||
1444 | return ret; | ||
1445 | } | ||
1446 | |||
1447 | ptr->devfreq = devfreq; | ||
1448 | ptr->nb = nb; | ||
1449 | ptr->list = list; | ||
1450 | devres_add(dev, ptr); | ||
1451 | |||
1452 | return 0; | ||
1453 | } | ||
1454 | EXPORT_SYMBOL(devm_devfreq_register_notifier); | ||
1455 | |||
1456 | /** | ||
1457 | * devm_devfreq_unregister_notifier() | ||
1458 | - Resource-managed devfreq_unregister_notifier() | ||
1459 | * @dev: The devfreq user device. (parent of devfreq) | ||
1460 | * @devfreq: The devfreq object. | ||
1461 | * @nb: The notifier block to be unregistered. | ||
1462 | * @list: DEVFREQ_TRANSITION_NOTIFIER. | ||
1463 | */ | ||
1464 | void devm_devfreq_unregister_notifier(struct device *dev, | ||
1465 | struct devfreq *devfreq, | ||
1466 | struct notifier_block *nb, | ||
1467 | unsigned int list) | ||
1468 | { | ||
1469 | WARN_ON(devres_release(dev, devm_devfreq_notifier_release, | ||
1470 | devm_devfreq_dev_match, devfreq)); | ||
1471 | } | ||
1472 | EXPORT_SYMBOL(devm_devfreq_unregister_notifier); | ||
1473 | |||
1269 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | 1474 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); |
1270 | MODULE_DESCRIPTION("devfreq class support"); | 1475 | MODULE_DESCRIPTION("devfreq class support"); |
1271 | MODULE_LICENSE("GPL"); | 1476 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index a11720affc31..1e8b4f469f38 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig | |||
@@ -13,6 +13,14 @@ menuconfig PM_DEVFREQ_EVENT | |||
13 | 13 | ||
14 | if PM_DEVFREQ_EVENT | 14 | if PM_DEVFREQ_EVENT |
15 | 15 | ||
16 | config DEVFREQ_EVENT_EXYNOS_NOCP | ||
17 | bool "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver" | ||
18 | depends on ARCH_EXYNOS | ||
19 | select PM_OPP | ||
20 | help | ||
21 | This add the devfreq-event driver for Exynos SoC. It provides NoC | ||
22 | (Network on Chip) Probe counters to measure the bandwidth of AXI bus. | ||
23 | |||
16 | config DEVFREQ_EVENT_EXYNOS_PPMU | 24 | config DEVFREQ_EVENT_EXYNOS_PPMU |
17 | bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" | 25 | bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" |
18 | depends on ARCH_EXYNOS | 26 | depends on ARCH_EXYNOS |
diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index be146ead79cf..3d6afd352253 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile | |||
@@ -1,2 +1,4 @@ | |||
1 | # Exynos DEVFREQ Event Drivers | 1 | # Exynos DEVFREQ Event Drivers |
2 | |||
3 | obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o | ||
2 | obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o | 4 | obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o |
diff --git a/drivers/devfreq/event/exynos-nocp.c b/drivers/devfreq/event/exynos-nocp.c new file mode 100644 index 000000000000..6b6a5f310486 --- /dev/null +++ b/drivers/devfreq/event/exynos-nocp.c | |||
@@ -0,0 +1,304 @@ | |||
1 | /* | ||
2 | * exynos-nocp.c - EXYNOS NoC (Network On Chip) Probe support | ||
3 | * | ||
4 | * Copyright (c) 2016 Samsung Electronics Co., Ltd. | ||
5 | * Author : Chanwoo Choi <cw00.choi@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/devfreq-event.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/regmap.h> | ||
19 | |||
20 | #include "exynos-nocp.h" | ||
21 | |||
22 | struct exynos_nocp { | ||
23 | struct devfreq_event_dev *edev; | ||
24 | struct devfreq_event_desc desc; | ||
25 | |||
26 | struct device *dev; | ||
27 | |||
28 | struct regmap *regmap; | ||
29 | struct clk *clk; | ||
30 | }; | ||
31 | |||
32 | /* | ||
33 | * The devfreq-event ops structure for nocp probe. | ||
34 | */ | ||
35 | static int exynos_nocp_set_event(struct devfreq_event_dev *edev) | ||
36 | { | ||
37 | struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev); | ||
38 | int ret; | ||
39 | |||
40 | /* Disable NoC probe */ | ||
41 | ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, | ||
42 | NOCP_MAIN_CTL_STATEN_MASK, 0); | ||
43 | if (ret < 0) { | ||
44 | dev_err(nocp->dev, "failed to disable the NoC probe device\n"); | ||
45 | return ret; | ||
46 | } | ||
47 | |||
48 | /* Set a statistics dump period to 0 */ | ||
49 | ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0); | ||
50 | if (ret < 0) | ||
51 | goto out; | ||
52 | |||
53 | /* Set the IntEvent fields of *_SRC */ | ||
54 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC, | ||
55 | NOCP_CNT_SRC_INTEVENT_MASK, | ||
56 | NOCP_CNT_SRC_INTEVENT_BYTE_MASK); | ||
57 | if (ret < 0) | ||
58 | goto out; | ||
59 | |||
60 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC, | ||
61 | NOCP_CNT_SRC_INTEVENT_MASK, | ||
62 | NOCP_CNT_SRC_INTEVENT_CHAIN_MASK); | ||
63 | if (ret < 0) | ||
64 | goto out; | ||
65 | |||
66 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC, | ||
67 | NOCP_CNT_SRC_INTEVENT_MASK, | ||
68 | NOCP_CNT_SRC_INTEVENT_CYCLE_MASK); | ||
69 | if (ret < 0) | ||
70 | goto out; | ||
71 | |||
72 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC, | ||
73 | NOCP_CNT_SRC_INTEVENT_MASK, | ||
74 | NOCP_CNT_SRC_INTEVENT_CHAIN_MASK); | ||
75 | if (ret < 0) | ||
76 | goto out; | ||
77 | |||
78 | |||
79 | /* Set an alarm with a max/min value of 0 to generate StatALARM */ | ||
80 | ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0); | ||
81 | if (ret < 0) | ||
82 | goto out; | ||
83 | |||
84 | ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0); | ||
85 | if (ret < 0) | ||
86 | goto out; | ||
87 | |||
88 | /* Set AlarmMode */ | ||
89 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE, | ||
90 | NOCP_CNT_ALARM_MODE_MASK, | ||
91 | NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); | ||
92 | if (ret < 0) | ||
93 | goto out; | ||
94 | |||
95 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE, | ||
96 | NOCP_CNT_ALARM_MODE_MASK, | ||
97 | NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); | ||
98 | if (ret < 0) | ||
99 | goto out; | ||
100 | |||
101 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE, | ||
102 | NOCP_CNT_ALARM_MODE_MASK, | ||
103 | NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); | ||
104 | if (ret < 0) | ||
105 | goto out; | ||
106 | |||
107 | ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE, | ||
108 | NOCP_CNT_ALARM_MODE_MASK, | ||
109 | NOCP_CNT_ALARM_MODE_MIN_MAX_MASK); | ||
110 | if (ret < 0) | ||
111 | goto out; | ||
112 | |||
113 | /* Enable the measurements by setting AlarmEn and StatEn */ | ||
114 | ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, | ||
115 | NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK, | ||
116 | NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK); | ||
117 | if (ret < 0) | ||
118 | goto out; | ||
119 | |||
120 | /* Set GlobalEN */ | ||
121 | ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL, | ||
122 | NOCP_CFG_CTL_GLOBALEN_MASK, | ||
123 | NOCP_CFG_CTL_GLOBALEN_MASK); | ||
124 | if (ret < 0) | ||
125 | goto out; | ||
126 | |||
127 | /* Enable NoC probe */ | ||
128 | ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, | ||
129 | NOCP_MAIN_CTL_STATEN_MASK, | ||
130 | NOCP_MAIN_CTL_STATEN_MASK); | ||
131 | if (ret < 0) | ||
132 | goto out; | ||
133 | |||
134 | return 0; | ||
135 | |||
136 | out: | ||
137 | /* Reset NoC probe */ | ||
138 | if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL, | ||
139 | NOCP_MAIN_CTL_STATEN_MASK, 0)) { | ||
140 | dev_err(nocp->dev, "Failed to reset NoC probe device\n"); | ||
141 | } | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | static int exynos_nocp_get_event(struct devfreq_event_dev *edev, | ||
147 | struct devfreq_event_data *edata) | ||
148 | { | ||
149 | struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev); | ||
150 | unsigned int counter[4]; | ||
151 | int ret; | ||
152 | |||
153 | /* Read cycle count */ | ||
154 | ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]); | ||
155 | if (ret < 0) | ||
156 | goto out; | ||
157 | |||
158 | ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]); | ||
159 | if (ret < 0) | ||
160 | goto out; | ||
161 | |||
162 | ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]); | ||
163 | if (ret < 0) | ||
164 | goto out; | ||
165 | |||
166 | ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]); | ||
167 | if (ret < 0) | ||
168 | goto out; | ||
169 | |||
170 | edata->load_count = ((counter[1] << 16) | counter[0]); | ||
171 | edata->total_count = ((counter[3] << 16) | counter[2]); | ||
172 | |||
173 | dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, | ||
174 | edata->load_count, edata->total_count); | ||
175 | |||
176 | return 0; | ||
177 | |||
178 | out: | ||
179 | edata->load_count = 0; | ||
180 | edata->total_count = 0; | ||
181 | |||
182 | dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n"); | ||
183 | |||
184 | return ret; | ||
185 | } | ||
186 | |||
187 | static const struct devfreq_event_ops exynos_nocp_ops = { | ||
188 | .set_event = exynos_nocp_set_event, | ||
189 | .get_event = exynos_nocp_get_event, | ||
190 | }; | ||
191 | |||
192 | static const struct of_device_id exynos_nocp_id_match[] = { | ||
193 | { .compatible = "samsung,exynos5420-nocp", }, | ||
194 | { /* sentinel */ }, | ||
195 | }; | ||
196 | |||
197 | static struct regmap_config exynos_nocp_regmap_config = { | ||
198 | .reg_bits = 32, | ||
199 | .val_bits = 32, | ||
200 | .reg_stride = 4, | ||
201 | .max_register = NOCP_COUNTERS_3_VAL, | ||
202 | }; | ||
203 | |||
204 | static int exynos_nocp_parse_dt(struct platform_device *pdev, | ||
205 | struct exynos_nocp *nocp) | ||
206 | { | ||
207 | struct device *dev = nocp->dev; | ||
208 | struct device_node *np = dev->of_node; | ||
209 | struct resource *res; | ||
210 | void __iomem *base; | ||
211 | |||
212 | if (!np) { | ||
213 | dev_err(dev, "failed to find devicetree node\n"); | ||
214 | return -EINVAL; | ||
215 | } | ||
216 | |||
217 | nocp->clk = devm_clk_get(dev, "nocp"); | ||
218 | if (IS_ERR(nocp->clk)) | ||
219 | nocp->clk = NULL; | ||
220 | |||
221 | /* Maps the memory mapped IO to control nocp register */ | ||
222 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
223 | if (IS_ERR(res)) | ||
224 | return PTR_ERR(res); | ||
225 | |||
226 | base = devm_ioremap_resource(dev, res); | ||
227 | if (IS_ERR(base)) | ||
228 | return PTR_ERR(base); | ||
229 | |||
230 | exynos_nocp_regmap_config.max_register = resource_size(res) - 4; | ||
231 | |||
232 | nocp->regmap = devm_regmap_init_mmio(dev, base, | ||
233 | &exynos_nocp_regmap_config); | ||
234 | if (IS_ERR(nocp->regmap)) { | ||
235 | dev_err(dev, "failed to initialize regmap\n"); | ||
236 | return PTR_ERR(nocp->regmap); | ||
237 | } | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static int exynos_nocp_probe(struct platform_device *pdev) | ||
243 | { | ||
244 | struct device *dev = &pdev->dev; | ||
245 | struct device_node *np = dev->of_node; | ||
246 | struct exynos_nocp *nocp; | ||
247 | int ret; | ||
248 | |||
249 | nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL); | ||
250 | if (!nocp) | ||
251 | return -ENOMEM; | ||
252 | |||
253 | nocp->dev = &pdev->dev; | ||
254 | |||
255 | /* Parse dt data to get resource */ | ||
256 | ret = exynos_nocp_parse_dt(pdev, nocp); | ||
257 | if (ret < 0) { | ||
258 | dev_err(&pdev->dev, | ||
259 | "failed to parse devicetree for resource\n"); | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | /* Add devfreq-event device to measure the bandwidth of NoC */ | ||
264 | nocp->desc.ops = &exynos_nocp_ops; | ||
265 | nocp->desc.driver_data = nocp; | ||
266 | nocp->desc.name = np->full_name; | ||
267 | nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc); | ||
268 | if (IS_ERR(nocp->edev)) { | ||
269 | dev_err(&pdev->dev, | ||
270 | "failed to add devfreq-event device\n"); | ||
271 | return PTR_ERR(nocp->edev); | ||
272 | } | ||
273 | platform_set_drvdata(pdev, nocp); | ||
274 | |||
275 | clk_prepare_enable(nocp->clk); | ||
276 | |||
277 | pr_info("exynos-nocp: new NoC Probe device registered: %s\n", | ||
278 | dev_name(dev)); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | static int exynos_nocp_remove(struct platform_device *pdev) | ||
284 | { | ||
285 | struct exynos_nocp *nocp = platform_get_drvdata(pdev); | ||
286 | |||
287 | clk_disable_unprepare(nocp->clk); | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static struct platform_driver exynos_nocp_driver = { | ||
293 | .probe = exynos_nocp_probe, | ||
294 | .remove = exynos_nocp_remove, | ||
295 | .driver = { | ||
296 | .name = "exynos-nocp", | ||
297 | .of_match_table = exynos_nocp_id_match, | ||
298 | }, | ||
299 | }; | ||
300 | module_platform_driver(exynos_nocp_driver); | ||
301 | |||
302 | MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver"); | ||
303 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | ||
304 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/devfreq/event/exynos-nocp.h b/drivers/devfreq/event/exynos-nocp.h new file mode 100644 index 000000000000..28564db0edb8 --- /dev/null +++ b/drivers/devfreq/event/exynos-nocp.h | |||
@@ -0,0 +1,78 @@ | |||
1 | /* | ||
2 | * exynos-nocp.h - EXYNOS NoC (Network on Chip) Probe header file | ||
3 | * | ||
4 | * Copyright (c) 2016 Samsung Electronics Co., Ltd. | ||
5 | * Author : Chanwoo Choi <cw00.choi@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef __EXYNOS_NOCP_H__ | ||
13 | #define __EXYNOS_NOCP_H__ | ||
14 | |||
15 | enum nocp_reg { | ||
16 | NOCP_ID_REVISION_ID = 0x04, | ||
17 | NOCP_MAIN_CTL = 0x08, | ||
18 | NOCP_CFG_CTL = 0x0C, | ||
19 | |||
20 | NOCP_STAT_PERIOD = 0x24, | ||
21 | NOCP_STAT_GO = 0x28, | ||
22 | NOCP_STAT_ALARM_MIN = 0x2C, | ||
23 | NOCP_STAT_ALARM_MAX = 0x30, | ||
24 | NOCP_STAT_ALARM_STATUS = 0x34, | ||
25 | NOCP_STAT_ALARM_CLR = 0x38, | ||
26 | |||
27 | NOCP_COUNTERS_0_SRC = 0x138, | ||
28 | NOCP_COUNTERS_0_ALARM_MODE = 0x13C, | ||
29 | NOCP_COUNTERS_0_VAL = 0x140, | ||
30 | |||
31 | NOCP_COUNTERS_1_SRC = 0x14C, | ||
32 | NOCP_COUNTERS_1_ALARM_MODE = 0x150, | ||
33 | NOCP_COUNTERS_1_VAL = 0x154, | ||
34 | |||
35 | NOCP_COUNTERS_2_SRC = 0x160, | ||
36 | NOCP_COUNTERS_2_ALARM_MODE = 0x164, | ||
37 | NOCP_COUNTERS_2_VAL = 0x168, | ||
38 | |||
39 | NOCP_COUNTERS_3_SRC = 0x174, | ||
40 | NOCP_COUNTERS_3_ALARM_MODE = 0x178, | ||
41 | NOCP_COUNTERS_3_VAL = 0x17C, | ||
42 | }; | ||
43 | |||
44 | /* NOCP_MAIN_CTL register */ | ||
45 | #define NOCP_MAIN_CTL_ERREN_MASK BIT(0) | ||
46 | #define NOCP_MAIN_CTL_TRACEEN_MASK BIT(1) | ||
47 | #define NOCP_MAIN_CTL_PAYLOADEN_MASK BIT(2) | ||
48 | #define NOCP_MAIN_CTL_STATEN_MASK BIT(3) | ||
49 | #define NOCP_MAIN_CTL_ALARMEN_MASK BIT(4) | ||
50 | #define NOCP_MAIN_CTL_STATCONDDUMP_MASK BIT(5) | ||
51 | #define NOCP_MAIN_CTL_INTRUSIVEMODE_MASK BIT(6) | ||
52 | |||
53 | /* NOCP_CFG_CTL register */ | ||
54 | #define NOCP_CFG_CTL_GLOBALEN_MASK BIT(0) | ||
55 | #define NOCP_CFG_CTL_ACTIVE_MASK BIT(1) | ||
56 | |||
57 | /* NOCP_COUNTERS_x_SRC register */ | ||
58 | #define NOCP_CNT_SRC_INTEVENT_SHIFT 0 | ||
59 | #define NOCP_CNT_SRC_INTEVENT_MASK (0x1F << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
60 | #define NOCP_CNT_SRC_INTEVENT_OFF_MASK (0x0 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
61 | #define NOCP_CNT_SRC_INTEVENT_CYCLE_MASK (0x1 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
62 | #define NOCP_CNT_SRC_INTEVENT_IDLE_MASK (0x2 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
63 | #define NOCP_CNT_SRC_INTEVENT_XFER_MASK (0x3 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
64 | #define NOCP_CNT_SRC_INTEVENT_BUSY_MASK (0x4 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
65 | #define NOCP_CNT_SRC_INTEVENT_WAIT_MASK (0x5 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
66 | #define NOCP_CNT_SRC_INTEVENT_PKT_MASK (0x6 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
67 | #define NOCP_CNT_SRC_INTEVENT_BYTE_MASK (0x8 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
68 | #define NOCP_CNT_SRC_INTEVENT_CHAIN_MASK (0x10 << NOCP_CNT_SRC_INTEVENT_SHIFT) | ||
69 | |||
70 | /* NOCP_COUNTERS_x_ALARM_MODE register */ | ||
71 | #define NOCP_CNT_ALARM_MODE_SHIFT 0 | ||
72 | #define NOCP_CNT_ALARM_MODE_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT) | ||
73 | #define NOCP_CNT_ALARM_MODE_OFF_MASK (0x0 << NOCP_CNT_ALARM_MODE_SHIFT) | ||
74 | #define NOCP_CNT_ALARM_MODE_MIN_MASK (0x1 << NOCP_CNT_ALARM_MODE_SHIFT) | ||
75 | #define NOCP_CNT_ALARM_MODE_MAX_MASK (0x2 << NOCP_CNT_ALARM_MODE_SHIFT) | ||
76 | #define NOCP_CNT_ALARM_MODE_MIN_MAX_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT) | ||
77 | |||
78 | #endif /* __EXYNOS_NOCP_H__ */ | ||
diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c new file mode 100644 index 000000000000..2363d0a189b7 --- /dev/null +++ b/drivers/devfreq/exynos-bus.c | |||
@@ -0,0 +1,570 @@ | |||
1 | /* | ||
2 | * Generic Exynos Bus frequency driver with DEVFREQ Framework | ||
3 | * | ||
4 | * Copyright (c) 2016 Samsung Electronics Co., Ltd. | ||
5 | * Author : Chanwoo Choi <cw00.choi@samsung.com> | ||
6 | * | ||
7 | * This driver support Exynos Bus frequency feature by using | ||
8 | * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/clk.h> | ||
16 | #include <linux/devfreq.h> | ||
17 | #include <linux/devfreq-event.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/export.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/of_device.h> | ||
22 | #include <linux/pm_opp.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/regulator/consumer.h> | ||
25 | #include <linux/slab.h> | ||
26 | |||
27 | #define DEFAULT_SATURATION_RATIO 40 | ||
28 | #define DEFAULT_VOLTAGE_TOLERANCE 2 | ||
29 | |||
30 | struct exynos_bus { | ||
31 | struct device *dev; | ||
32 | |||
33 | struct devfreq *devfreq; | ||
34 | struct devfreq_event_dev **edev; | ||
35 | unsigned int edev_count; | ||
36 | struct mutex lock; | ||
37 | |||
38 | struct dev_pm_opp *curr_opp; | ||
39 | |||
40 | struct regulator *regulator; | ||
41 | struct clk *clk; | ||
42 | unsigned int voltage_tolerance; | ||
43 | unsigned int ratio; | ||
44 | }; | ||
45 | |||
46 | /* | ||
47 | * Control the devfreq-event device to get the current state of bus | ||
48 | */ | ||
49 | #define exynos_bus_ops_edev(ops) \ | ||
50 | static int exynos_bus_##ops(struct exynos_bus *bus) \ | ||
51 | { \ | ||
52 | int i, ret; \ | ||
53 | \ | ||
54 | for (i = 0; i < bus->edev_count; i++) { \ | ||
55 | if (!bus->edev[i]) \ | ||
56 | continue; \ | ||
57 | ret = devfreq_event_##ops(bus->edev[i]); \ | ||
58 | if (ret < 0) \ | ||
59 | return ret; \ | ||
60 | } \ | ||
61 | \ | ||
62 | return 0; \ | ||
63 | } | ||
64 | exynos_bus_ops_edev(enable_edev); | ||
65 | exynos_bus_ops_edev(disable_edev); | ||
66 | exynos_bus_ops_edev(set_event); | ||
67 | |||
68 | static int exynos_bus_get_event(struct exynos_bus *bus, | ||
69 | struct devfreq_event_data *edata) | ||
70 | { | ||
71 | struct devfreq_event_data event_data; | ||
72 | unsigned long load_count = 0, total_count = 0; | ||
73 | int i, ret = 0; | ||
74 | |||
75 | for (i = 0; i < bus->edev_count; i++) { | ||
76 | if (!bus->edev[i]) | ||
77 | continue; | ||
78 | |||
79 | ret = devfreq_event_get_event(bus->edev[i], &event_data); | ||
80 | if (ret < 0) | ||
81 | return ret; | ||
82 | |||
83 | if (i == 0 || event_data.load_count > load_count) { | ||
84 | load_count = event_data.load_count; | ||
85 | total_count = event_data.total_count; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | edata->load_count = load_count; | ||
90 | edata->total_count = total_count; | ||
91 | |||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Must necessary function for devfreq simple-ondemand governor | ||
97 | */ | ||
98 | static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) | ||
99 | { | ||
100 | struct exynos_bus *bus = dev_get_drvdata(dev); | ||
101 | struct dev_pm_opp *new_opp; | ||
102 | unsigned long old_freq, new_freq, old_volt, new_volt, tol; | ||
103 | int ret = 0; | ||
104 | |||
105 | /* Get new opp-bus instance according to new bus clock */ | ||
106 | rcu_read_lock(); | ||
107 | new_opp = devfreq_recommended_opp(dev, freq, flags); | ||
108 | if (IS_ERR(new_opp)) { | ||
109 | dev_err(dev, "failed to get recommended opp instance\n"); | ||
110 | rcu_read_unlock(); | ||
111 | return PTR_ERR(new_opp); | ||
112 | } | ||
113 | |||
114 | new_freq = dev_pm_opp_get_freq(new_opp); | ||
115 | new_volt = dev_pm_opp_get_voltage(new_opp); | ||
116 | old_freq = dev_pm_opp_get_freq(bus->curr_opp); | ||
117 | old_volt = dev_pm_opp_get_voltage(bus->curr_opp); | ||
118 | rcu_read_unlock(); | ||
119 | |||
120 | if (old_freq == new_freq) | ||
121 | return 0; | ||
122 | tol = new_volt * bus->voltage_tolerance / 100; | ||
123 | |||
124 | /* Change voltage and frequency according to new OPP level */ | ||
125 | mutex_lock(&bus->lock); | ||
126 | |||
127 | if (old_freq < new_freq) { | ||
128 | ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol); | ||
129 | if (ret < 0) { | ||
130 | dev_err(bus->dev, "failed to set voltage\n"); | ||
131 | goto out; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | ret = clk_set_rate(bus->clk, new_freq); | ||
136 | if (ret < 0) { | ||
137 | dev_err(dev, "failed to change clock of bus\n"); | ||
138 | clk_set_rate(bus->clk, old_freq); | ||
139 | goto out; | ||
140 | } | ||
141 | |||
142 | if (old_freq > new_freq) { | ||
143 | ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol); | ||
144 | if (ret < 0) { | ||
145 | dev_err(bus->dev, "failed to set voltage\n"); | ||
146 | goto out; | ||
147 | } | ||
148 | } | ||
149 | bus->curr_opp = new_opp; | ||
150 | |||
151 | dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", | ||
152 | old_freq/1000, new_freq/1000); | ||
153 | out: | ||
154 | mutex_unlock(&bus->lock); | ||
155 | |||
156 | return ret; | ||
157 | } | ||
158 | |||
159 | static int exynos_bus_get_dev_status(struct device *dev, | ||
160 | struct devfreq_dev_status *stat) | ||
161 | { | ||
162 | struct exynos_bus *bus = dev_get_drvdata(dev); | ||
163 | struct devfreq_event_data edata; | ||
164 | int ret; | ||
165 | |||
166 | rcu_read_lock(); | ||
167 | stat->current_frequency = dev_pm_opp_get_freq(bus->curr_opp); | ||
168 | rcu_read_unlock(); | ||
169 | |||
170 | ret = exynos_bus_get_event(bus, &edata); | ||
171 | if (ret < 0) { | ||
172 | stat->total_time = stat->busy_time = 0; | ||
173 | goto err; | ||
174 | } | ||
175 | |||
176 | stat->busy_time = (edata.load_count * 100) / bus->ratio; | ||
177 | stat->total_time = edata.total_count; | ||
178 | |||
179 | dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time, | ||
180 | stat->total_time); | ||
181 | |||
182 | err: | ||
183 | ret = exynos_bus_set_event(bus); | ||
184 | if (ret < 0) { | ||
185 | dev_err(dev, "failed to set event to devfreq-event devices\n"); | ||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | return ret; | ||
190 | } | ||
191 | |||
192 | static void exynos_bus_exit(struct device *dev) | ||
193 | { | ||
194 | struct exynos_bus *bus = dev_get_drvdata(dev); | ||
195 | int ret; | ||
196 | |||
197 | ret = exynos_bus_disable_edev(bus); | ||
198 | if (ret < 0) | ||
199 | dev_warn(dev, "failed to disable the devfreq-event devices\n"); | ||
200 | |||
201 | if (bus->regulator) | ||
202 | regulator_disable(bus->regulator); | ||
203 | |||
204 | dev_pm_opp_of_remove_table(dev); | ||
205 | clk_disable_unprepare(bus->clk); | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * Must necessary function for devfreq passive governor | ||
210 | */ | ||
211 | static int exynos_bus_passive_target(struct device *dev, unsigned long *freq, | ||
212 | u32 flags) | ||
213 | { | ||
214 | struct exynos_bus *bus = dev_get_drvdata(dev); | ||
215 | struct dev_pm_opp *new_opp; | ||
216 | unsigned long old_freq, new_freq; | ||
217 | int ret = 0; | ||
218 | |||
219 | /* Get new opp-bus instance according to new bus clock */ | ||
220 | rcu_read_lock(); | ||
221 | new_opp = devfreq_recommended_opp(dev, freq, flags); | ||
222 | if (IS_ERR(new_opp)) { | ||
223 | dev_err(dev, "failed to get recommended opp instance\n"); | ||
224 | rcu_read_unlock(); | ||
225 | return PTR_ERR(new_opp); | ||
226 | } | ||
227 | |||
228 | new_freq = dev_pm_opp_get_freq(new_opp); | ||
229 | old_freq = dev_pm_opp_get_freq(bus->curr_opp); | ||
230 | rcu_read_unlock(); | ||
231 | |||
232 | if (old_freq == new_freq) | ||
233 | return 0; | ||
234 | |||
235 | /* Change the frequency according to new OPP level */ | ||
236 | mutex_lock(&bus->lock); | ||
237 | |||
238 | ret = clk_set_rate(bus->clk, new_freq); | ||
239 | if (ret < 0) { | ||
240 | dev_err(dev, "failed to set the clock of bus\n"); | ||
241 | goto out; | ||
242 | } | ||
243 | |||
244 | *freq = new_freq; | ||
245 | bus->curr_opp = new_opp; | ||
246 | |||
247 | dev_dbg(dev, "Set the frequency of bus (%lukHz -> %lukHz)\n", | ||
248 | old_freq/1000, new_freq/1000); | ||
249 | out: | ||
250 | mutex_unlock(&bus->lock); | ||
251 | |||
252 | return ret; | ||
253 | } | ||
254 | |||
255 | static void exynos_bus_passive_exit(struct device *dev) | ||
256 | { | ||
257 | struct exynos_bus *bus = dev_get_drvdata(dev); | ||
258 | |||
259 | dev_pm_opp_of_remove_table(dev); | ||
260 | clk_disable_unprepare(bus->clk); | ||
261 | } | ||
262 | |||
263 | static int exynos_bus_parent_parse_of(struct device_node *np, | ||
264 | struct exynos_bus *bus) | ||
265 | { | ||
266 | struct device *dev = bus->dev; | ||
267 | int i, ret, count, size; | ||
268 | |||
269 | /* Get the regulator to provide each bus with the power */ | ||
270 | bus->regulator = devm_regulator_get(dev, "vdd"); | ||
271 | if (IS_ERR(bus->regulator)) { | ||
272 | dev_err(dev, "failed to get VDD regulator\n"); | ||
273 | return PTR_ERR(bus->regulator); | ||
274 | } | ||
275 | |||
276 | ret = regulator_enable(bus->regulator); | ||
277 | if (ret < 0) { | ||
278 | dev_err(dev, "failed to enable VDD regulator\n"); | ||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Get the devfreq-event devices to get the current utilization of | ||
284 | * buses. This raw data will be used in devfreq ondemand governor. | ||
285 | */ | ||
286 | count = devfreq_event_get_edev_count(dev); | ||
287 | if (count < 0) { | ||
288 | dev_err(dev, "failed to get the count of devfreq-event dev\n"); | ||
289 | ret = count; | ||
290 | goto err_regulator; | ||
291 | } | ||
292 | bus->edev_count = count; | ||
293 | |||
294 | size = sizeof(*bus->edev) * count; | ||
295 | bus->edev = devm_kzalloc(dev, size, GFP_KERNEL); | ||
296 | if (!bus->edev) { | ||
297 | ret = -ENOMEM; | ||
298 | goto err_regulator; | ||
299 | } | ||
300 | |||
301 | for (i = 0; i < count; i++) { | ||
302 | bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i); | ||
303 | if (IS_ERR(bus->edev[i])) { | ||
304 | ret = -EPROBE_DEFER; | ||
305 | goto err_regulator; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Optionally, Get the saturation ratio according to Exynos SoC | ||
311 | * When measuring the utilization of each AXI bus with devfreq-event | ||
312 | * devices, the measured real cycle might be much lower than the | ||
313 | * total cycle of bus during sampling rate. In result, the devfreq | ||
314 | * simple-ondemand governor might not decide to change the current | ||
315 | * frequency due to too utilization (= real cycle/total cycle). | ||
316 | * So, this property is used to adjust the utilization when calculating | ||
317 | * the busy_time in exynos_bus_get_dev_status(). | ||
318 | */ | ||
319 | if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio)) | ||
320 | bus->ratio = DEFAULT_SATURATION_RATIO; | ||
321 | |||
322 | if (of_property_read_u32(np, "exynos,voltage-tolerance", | ||
323 | &bus->voltage_tolerance)) | ||
324 | bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE; | ||
325 | |||
326 | return 0; | ||
327 | |||
328 | err_regulator: | ||
329 | regulator_disable(bus->regulator); | ||
330 | |||
331 | return ret; | ||
332 | } | ||
333 | |||
334 | static int exynos_bus_parse_of(struct device_node *np, | ||
335 | struct exynos_bus *bus) | ||
336 | { | ||
337 | struct device *dev = bus->dev; | ||
338 | unsigned long rate; | ||
339 | int ret; | ||
340 | |||
341 | /* Get the clock to provide each bus with source clock */ | ||
342 | bus->clk = devm_clk_get(dev, "bus"); | ||
343 | if (IS_ERR(bus->clk)) { | ||
344 | dev_err(dev, "failed to get bus clock\n"); | ||
345 | return PTR_ERR(bus->clk); | ||
346 | } | ||
347 | |||
348 | ret = clk_prepare_enable(bus->clk); | ||
349 | if (ret < 0) { | ||
350 | dev_err(dev, "failed to get enable clock\n"); | ||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | /* Get the freq and voltage from OPP table to scale the bus freq */ | ||
355 | rcu_read_lock(); | ||
356 | ret = dev_pm_opp_of_add_table(dev); | ||
357 | if (ret < 0) { | ||
358 | dev_err(dev, "failed to get OPP table\n"); | ||
359 | rcu_read_unlock(); | ||
360 | goto err_clk; | ||
361 | } | ||
362 | |||
363 | rate = clk_get_rate(bus->clk); | ||
364 | bus->curr_opp = devfreq_recommended_opp(dev, &rate, 0); | ||
365 | if (IS_ERR(bus->curr_opp)) { | ||
366 | dev_err(dev, "failed to find dev_pm_opp\n"); | ||
367 | rcu_read_unlock(); | ||
368 | ret = PTR_ERR(bus->curr_opp); | ||
369 | goto err_opp; | ||
370 | } | ||
371 | rcu_read_unlock(); | ||
372 | |||
373 | return 0; | ||
374 | |||
375 | err_opp: | ||
376 | dev_pm_opp_of_remove_table(dev); | ||
377 | err_clk: | ||
378 | clk_disable_unprepare(bus->clk); | ||
379 | |||
380 | return ret; | ||
381 | } | ||
382 | |||
383 | static int exynos_bus_probe(struct platform_device *pdev) | ||
384 | { | ||
385 | struct device *dev = &pdev->dev; | ||
386 | struct device_node *np = dev->of_node; | ||
387 | struct devfreq_dev_profile *profile; | ||
388 | struct devfreq_simple_ondemand_data *ondemand_data; | ||
389 | struct devfreq_passive_data *passive_data; | ||
390 | struct devfreq *parent_devfreq; | ||
391 | struct exynos_bus *bus; | ||
392 | int ret, max_state; | ||
393 | unsigned long min_freq, max_freq; | ||
394 | |||
395 | if (!np) { | ||
396 | dev_err(dev, "failed to find devicetree node\n"); | ||
397 | return -EINVAL; | ||
398 | } | ||
399 | |||
400 | bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); | ||
401 | if (!bus) | ||
402 | return -ENOMEM; | ||
403 | mutex_init(&bus->lock); | ||
404 | bus->dev = &pdev->dev; | ||
405 | platform_set_drvdata(pdev, bus); | ||
406 | |||
407 | /* Parse the device-tree to get the resource information */ | ||
408 | ret = exynos_bus_parse_of(np, bus); | ||
409 | if (ret < 0) | ||
410 | goto err; | ||
411 | |||
412 | profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL); | ||
413 | if (!profile) { | ||
414 | ret = -ENOMEM; | ||
415 | goto err; | ||
416 | } | ||
417 | |||
418 | if (of_parse_phandle(dev->of_node, "devfreq", 0)) | ||
419 | goto passive; | ||
420 | else | ||
421 | ret = exynos_bus_parent_parse_of(np, bus); | ||
422 | |||
423 | if (ret < 0) | ||
424 | goto err; | ||
425 | |||
426 | /* Initialize the struct profile and governor data for parent device */ | ||
427 | profile->polling_ms = 50; | ||
428 | profile->target = exynos_bus_target; | ||
429 | profile->get_dev_status = exynos_bus_get_dev_status; | ||
430 | profile->exit = exynos_bus_exit; | ||
431 | |||
432 | ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL); | ||
433 | if (!ondemand_data) { | ||
434 | ret = -ENOMEM; | ||
435 | goto err; | ||
436 | } | ||
437 | ondemand_data->upthreshold = 40; | ||
438 | ondemand_data->downdifferential = 5; | ||
439 | |||
440 | /* Add devfreq device to monitor and handle the exynos bus */ | ||
441 | bus->devfreq = devm_devfreq_add_device(dev, profile, "simple_ondemand", | ||
442 | ondemand_data); | ||
443 | if (IS_ERR(bus->devfreq)) { | ||
444 | dev_err(dev, "failed to add devfreq device\n"); | ||
445 | ret = PTR_ERR(bus->devfreq); | ||
446 | goto err; | ||
447 | } | ||
448 | |||
449 | /* Register opp_notifier to catch the change of OPP */ | ||
450 | ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq); | ||
451 | if (ret < 0) { | ||
452 | dev_err(dev, "failed to register opp notifier\n"); | ||
453 | goto err; | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * Enable devfreq-event to get raw data which is used to determine | ||
458 | * current bus load. | ||
459 | */ | ||
460 | ret = exynos_bus_enable_edev(bus); | ||
461 | if (ret < 0) { | ||
462 | dev_err(dev, "failed to enable devfreq-event devices\n"); | ||
463 | goto err; | ||
464 | } | ||
465 | |||
466 | ret = exynos_bus_set_event(bus); | ||
467 | if (ret < 0) { | ||
468 | dev_err(dev, "failed to set event to devfreq-event devices\n"); | ||
469 | goto err; | ||
470 | } | ||
471 | |||
472 | goto out; | ||
473 | passive: | ||
474 | /* Initialize the struct profile and governor data for passive device */ | ||
475 | profile->target = exynos_bus_passive_target; | ||
476 | profile->exit = exynos_bus_passive_exit; | ||
477 | |||
478 | /* Get the instance of parent devfreq device */ | ||
479 | parent_devfreq = devfreq_get_devfreq_by_phandle(dev, 0); | ||
480 | if (IS_ERR(parent_devfreq)) { | ||
481 | ret = -EPROBE_DEFER; | ||
482 | goto err; | ||
483 | } | ||
484 | |||
485 | passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL); | ||
486 | if (!passive_data) { | ||
487 | ret = -ENOMEM; | ||
488 | goto err; | ||
489 | } | ||
490 | passive_data->parent = parent_devfreq; | ||
491 | |||
492 | /* Add devfreq device for exynos bus with passive governor */ | ||
493 | bus->devfreq = devm_devfreq_add_device(dev, profile, "passive", | ||
494 | passive_data); | ||
495 | if (IS_ERR(bus->devfreq)) { | ||
496 | dev_err(dev, | ||
497 | "failed to add devfreq dev with passive governor\n"); | ||
498 | ret = -EPROBE_DEFER; | ||
499 | goto err; | ||
500 | } | ||
501 | |||
502 | out: | ||
503 | max_state = bus->devfreq->profile->max_state; | ||
504 | min_freq = (bus->devfreq->profile->freq_table[0] / 1000); | ||
505 | max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000); | ||
506 | pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n", | ||
507 | dev_name(dev), min_freq, max_freq); | ||
508 | |||
509 | return 0; | ||
510 | |||
511 | err: | ||
512 | dev_pm_opp_of_remove_table(dev); | ||
513 | clk_disable_unprepare(bus->clk); | ||
514 | |||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | #ifdef CONFIG_PM_SLEEP | ||
519 | static int exynos_bus_resume(struct device *dev) | ||
520 | { | ||
521 | struct exynos_bus *bus = dev_get_drvdata(dev); | ||
522 | int ret; | ||
523 | |||
524 | ret = exynos_bus_enable_edev(bus); | ||
525 | if (ret < 0) { | ||
526 | dev_err(dev, "failed to enable the devfreq-event devices\n"); | ||
527 | return ret; | ||
528 | } | ||
529 | |||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static int exynos_bus_suspend(struct device *dev) | ||
534 | { | ||
535 | struct exynos_bus *bus = dev_get_drvdata(dev); | ||
536 | int ret; | ||
537 | |||
538 | ret = exynos_bus_disable_edev(bus); | ||
539 | if (ret < 0) { | ||
540 | dev_err(dev, "failed to disable the devfreq-event devices\n"); | ||
541 | return ret; | ||
542 | } | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | #endif | ||
547 | |||
548 | static const struct dev_pm_ops exynos_bus_pm = { | ||
549 | SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume) | ||
550 | }; | ||
551 | |||
552 | static const struct of_device_id exynos_bus_of_match[] = { | ||
553 | { .compatible = "samsung,exynos-bus", }, | ||
554 | { /* sentinel */ }, | ||
555 | }; | ||
556 | MODULE_DEVICE_TABLE(of, exynos_bus_of_match); | ||
557 | |||
558 | static struct platform_driver exynos_bus_platdrv = { | ||
559 | .probe = exynos_bus_probe, | ||
560 | .driver = { | ||
561 | .name = "exynos-bus", | ||
562 | .pm = &exynos_bus_pm, | ||
563 | .of_match_table = of_match_ptr(exynos_bus_of_match), | ||
564 | }, | ||
565 | }; | ||
566 | module_platform_driver(exynos_bus_platdrv); | ||
567 | |||
568 | MODULE_DESCRIPTION("Generic Exynos Bus frequency driver"); | ||
569 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | ||
570 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile deleted file mode 100644 index 49bc9175f923..000000000000 --- a/drivers/devfreq/exynos/Makefile +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | # Exynos DEVFREQ Drivers | ||
2 | obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos_ppmu.o exynos4_bus.o | ||
3 | obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o | ||
diff --git a/drivers/devfreq/exynos/exynos4_bus.c b/drivers/devfreq/exynos/exynos4_bus.c deleted file mode 100644 index da9509205169..000000000000 --- a/drivers/devfreq/exynos/exynos4_bus.c +++ /dev/null | |||
@@ -1,1055 +0,0 @@ | |||
1 | /* drivers/devfreq/exynos4210_memorybus.c | ||
2 | * | ||
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * MyungJoo Ham <myungjoo.ham@samsung.com> | ||
6 | * | ||
7 | * EXYNOS4 - Memory/Bus clock frequency scaling support in DEVFREQ framework | ||
8 | * This version supports EXYNOS4210 only. This changes bus frequencies | ||
9 | * and vddint voltages. Exynos4412/4212 should be able to be supported | ||
10 | * with minor modifications. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/io.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/mutex.h> | ||
21 | #include <linux/suspend.h> | ||
22 | #include <linux/pm_opp.h> | ||
23 | #include <linux/devfreq.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/regulator/consumer.h> | ||
26 | #include <linux/module.h> | ||
27 | |||
28 | #include <mach/map.h> | ||
29 | |||
30 | #include "exynos_ppmu.h" | ||
31 | #include "exynos4_bus.h" | ||
32 | |||
33 | #define MAX_SAFEVOLT 1200000 /* 1.2V */ | ||
34 | |||
35 | enum exynos4_busf_type { | ||
36 | TYPE_BUSF_EXYNOS4210, | ||
37 | TYPE_BUSF_EXYNOS4x12, | ||
38 | }; | ||
39 | |||
40 | /* Assume that the bus is saturated if the utilization is 40% */ | ||
41 | #define BUS_SATURATION_RATIO 40 | ||
42 | |||
43 | enum busclk_level_idx { | ||
44 | LV_0 = 0, | ||
45 | LV_1, | ||
46 | LV_2, | ||
47 | LV_3, | ||
48 | LV_4, | ||
49 | _LV_END | ||
50 | }; | ||
51 | |||
52 | enum exynos_ppmu_idx { | ||
53 | PPMU_DMC0, | ||
54 | PPMU_DMC1, | ||
55 | PPMU_END, | ||
56 | }; | ||
57 | |||
58 | #define EX4210_LV_MAX LV_2 | ||
59 | #define EX4x12_LV_MAX LV_4 | ||
60 | #define EX4210_LV_NUM (LV_2 + 1) | ||
61 | #define EX4x12_LV_NUM (LV_4 + 1) | ||
62 | |||
63 | /** | ||
64 | * struct busfreq_opp_info - opp information for bus | ||
65 | * @rate: Frequency in hertz | ||
66 | * @volt: Voltage in microvolts corresponding to this OPP | ||
67 | */ | ||
68 | struct busfreq_opp_info { | ||
69 | unsigned long rate; | ||
70 | unsigned long volt; | ||
71 | }; | ||
72 | |||
73 | struct busfreq_data { | ||
74 | enum exynos4_busf_type type; | ||
75 | struct device *dev; | ||
76 | struct devfreq *devfreq; | ||
77 | bool disabled; | ||
78 | struct regulator *vdd_int; | ||
79 | struct regulator *vdd_mif; /* Exynos4412/4212 only */ | ||
80 | struct busfreq_opp_info curr_oppinfo; | ||
81 | struct busfreq_ppmu_data ppmu_data; | ||
82 | |||
83 | struct notifier_block pm_notifier; | ||
84 | struct mutex lock; | ||
85 | |||
86 | /* Dividers calculated at boot/probe-time */ | ||
87 | unsigned int dmc_divtable[_LV_END]; /* DMC0 */ | ||
88 | unsigned int top_divtable[_LV_END]; | ||
89 | }; | ||
90 | |||
91 | /* 4210 controls clock of mif and voltage of int */ | ||
92 | static struct bus_opp_table exynos4210_busclk_table[] = { | ||
93 | {LV_0, 400000, 1150000}, | ||
94 | {LV_1, 267000, 1050000}, | ||
95 | {LV_2, 133000, 1025000}, | ||
96 | {0, 0, 0}, | ||
97 | }; | ||
98 | |||
99 | /* | ||
100 | * MIF is the main control knob clock for Exynos4x12 MIF/INT | ||
101 | * clock and voltage of both mif/int are controlled. | ||
102 | */ | ||
103 | static struct bus_opp_table exynos4x12_mifclk_table[] = { | ||
104 | {LV_0, 400000, 1100000}, | ||
105 | {LV_1, 267000, 1000000}, | ||
106 | {LV_2, 160000, 950000}, | ||
107 | {LV_3, 133000, 950000}, | ||
108 | {LV_4, 100000, 950000}, | ||
109 | {0, 0, 0}, | ||
110 | }; | ||
111 | |||
112 | /* | ||
113 | * INT is not the control knob of 4x12. LV_x is not meant to represent | ||
114 | * the current performance. (MIF does) | ||
115 | */ | ||
116 | static struct bus_opp_table exynos4x12_intclk_table[] = { | ||
117 | {LV_0, 200000, 1000000}, | ||
118 | {LV_1, 160000, 950000}, | ||
119 | {LV_2, 133000, 925000}, | ||
120 | {LV_3, 100000, 900000}, | ||
121 | {0, 0, 0}, | ||
122 | }; | ||
123 | |||
124 | /* TODO: asv volt definitions are "__initdata"? */ | ||
125 | /* Some chips have different operating voltages */ | ||
126 | static unsigned int exynos4210_asv_volt[][EX4210_LV_NUM] = { | ||
127 | {1150000, 1050000, 1050000}, | ||
128 | {1125000, 1025000, 1025000}, | ||
129 | {1100000, 1000000, 1000000}, | ||
130 | {1075000, 975000, 975000}, | ||
131 | {1050000, 950000, 950000}, | ||
132 | }; | ||
133 | |||
134 | static unsigned int exynos4x12_mif_step_50[][EX4x12_LV_NUM] = { | ||
135 | /* 400 267 160 133 100 */ | ||
136 | {1050000, 950000, 900000, 900000, 900000}, /* ASV0 */ | ||
137 | {1050000, 950000, 900000, 900000, 900000}, /* ASV1 */ | ||
138 | {1050000, 950000, 900000, 900000, 900000}, /* ASV2 */ | ||
139 | {1050000, 900000, 900000, 900000, 900000}, /* ASV3 */ | ||
140 | {1050000, 900000, 900000, 900000, 850000}, /* ASV4 */ | ||
141 | {1050000, 900000, 900000, 850000, 850000}, /* ASV5 */ | ||
142 | {1050000, 900000, 850000, 850000, 850000}, /* ASV6 */ | ||
143 | {1050000, 900000, 850000, 850000, 850000}, /* ASV7 */ | ||
144 | {1050000, 900000, 850000, 850000, 850000}, /* ASV8 */ | ||
145 | }; | ||
146 | |||
147 | static unsigned int exynos4x12_int_volt[][EX4x12_LV_NUM] = { | ||
148 | /* 200 160 133 100 */ | ||
149 | {1000000, 950000, 925000, 900000}, /* ASV0 */ | ||
150 | {975000, 925000, 925000, 900000}, /* ASV1 */ | ||
151 | {950000, 925000, 900000, 875000}, /* ASV2 */ | ||
152 | {950000, 900000, 900000, 875000}, /* ASV3 */ | ||
153 | {925000, 875000, 875000, 875000}, /* ASV4 */ | ||
154 | {900000, 850000, 850000, 850000}, /* ASV5 */ | ||
155 | {900000, 850000, 850000, 850000}, /* ASV6 */ | ||
156 | {900000, 850000, 850000, 850000}, /* ASV7 */ | ||
157 | {900000, 850000, 850000, 850000}, /* ASV8 */ | ||
158 | }; | ||
159 | |||
160 | /*** Clock Divider Data for Exynos4210 ***/ | ||
161 | static unsigned int exynos4210_clkdiv_dmc0[][8] = { | ||
162 | /* | ||
163 | * Clock divider value for following | ||
164 | * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD | ||
165 | * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS } | ||
166 | */ | ||
167 | |||
168 | /* DMC L0: 400MHz */ | ||
169 | { 3, 1, 1, 1, 1, 1, 3, 1 }, | ||
170 | /* DMC L1: 266.7MHz */ | ||
171 | { 4, 1, 1, 2, 1, 1, 3, 1 }, | ||
172 | /* DMC L2: 133MHz */ | ||
173 | { 5, 1, 1, 5, 1, 1, 3, 1 }, | ||
174 | }; | ||
175 | static unsigned int exynos4210_clkdiv_top[][5] = { | ||
176 | /* | ||
177 | * Clock divider value for following | ||
178 | * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND } | ||
179 | */ | ||
180 | /* ACLK200 L0: 200MHz */ | ||
181 | { 3, 7, 4, 5, 1 }, | ||
182 | /* ACLK200 L1: 160MHz */ | ||
183 | { 4, 7, 5, 6, 1 }, | ||
184 | /* ACLK200 L2: 133MHz */ | ||
185 | { 5, 7, 7, 7, 1 }, | ||
186 | }; | ||
187 | static unsigned int exynos4210_clkdiv_lr_bus[][2] = { | ||
188 | /* | ||
189 | * Clock divider value for following | ||
190 | * { DIVGDL/R, DIVGPL/R } | ||
191 | */ | ||
192 | /* ACLK_GDL/R L1: 200MHz */ | ||
193 | { 3, 1 }, | ||
194 | /* ACLK_GDL/R L2: 160MHz */ | ||
195 | { 4, 1 }, | ||
196 | /* ACLK_GDL/R L3: 133MHz */ | ||
197 | { 5, 1 }, | ||
198 | }; | ||
199 | |||
200 | /*** Clock Divider Data for Exynos4212/4412 ***/ | ||
201 | static unsigned int exynos4x12_clkdiv_dmc0[][6] = { | ||
202 | /* | ||
203 | * Clock divider value for following | ||
204 | * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD | ||
205 | * DIVDMCP} | ||
206 | */ | ||
207 | |||
208 | /* DMC L0: 400MHz */ | ||
209 | {3, 1, 1, 1, 1, 1}, | ||
210 | /* DMC L1: 266.7MHz */ | ||
211 | {4, 1, 1, 2, 1, 1}, | ||
212 | /* DMC L2: 160MHz */ | ||
213 | {5, 1, 1, 4, 1, 1}, | ||
214 | /* DMC L3: 133MHz */ | ||
215 | {5, 1, 1, 5, 1, 1}, | ||
216 | /* DMC L4: 100MHz */ | ||
217 | {7, 1, 1, 7, 1, 1}, | ||
218 | }; | ||
219 | static unsigned int exynos4x12_clkdiv_dmc1[][6] = { | ||
220 | /* | ||
221 | * Clock divider value for following | ||
222 | * { G2DACP, DIVC2C, DIVC2C_ACLK } | ||
223 | */ | ||
224 | |||
225 | /* DMC L0: 400MHz */ | ||
226 | {3, 1, 1}, | ||
227 | /* DMC L1: 266.7MHz */ | ||
228 | {4, 2, 1}, | ||
229 | /* DMC L2: 160MHz */ | ||
230 | {5, 4, 1}, | ||
231 | /* DMC L3: 133MHz */ | ||
232 | {5, 5, 1}, | ||
233 | /* DMC L4: 100MHz */ | ||
234 | {7, 7, 1}, | ||
235 | }; | ||
236 | static unsigned int exynos4x12_clkdiv_top[][5] = { | ||
237 | /* | ||
238 | * Clock divider value for following | ||
239 | * { DIVACLK266_GPS, DIVACLK100, DIVACLK160, | ||
240 | DIVACLK133, DIVONENAND } | ||
241 | */ | ||
242 | |||
243 | /* ACLK_GDL/R L0: 200MHz */ | ||
244 | {2, 7, 4, 5, 1}, | ||
245 | /* ACLK_GDL/R L1: 200MHz */ | ||
246 | {2, 7, 4, 5, 1}, | ||
247 | /* ACLK_GDL/R L2: 160MHz */ | ||
248 | {4, 7, 5, 7, 1}, | ||
249 | /* ACLK_GDL/R L3: 133MHz */ | ||
250 | {4, 7, 5, 7, 1}, | ||
251 | /* ACLK_GDL/R L4: 100MHz */ | ||
252 | {7, 7, 7, 7, 1}, | ||
253 | }; | ||
254 | static unsigned int exynos4x12_clkdiv_lr_bus[][2] = { | ||
255 | /* | ||
256 | * Clock divider value for following | ||
257 | * { DIVGDL/R, DIVGPL/R } | ||
258 | */ | ||
259 | |||
260 | /* ACLK_GDL/R L0: 200MHz */ | ||
261 | {3, 1}, | ||
262 | /* ACLK_GDL/R L1: 200MHz */ | ||
263 | {3, 1}, | ||
264 | /* ACLK_GDL/R L2: 160MHz */ | ||
265 | {4, 1}, | ||
266 | /* ACLK_GDL/R L3: 133MHz */ | ||
267 | {5, 1}, | ||
268 | /* ACLK_GDL/R L4: 100MHz */ | ||
269 | {7, 1}, | ||
270 | }; | ||
271 | static unsigned int exynos4x12_clkdiv_sclkip[][3] = { | ||
272 | /* | ||
273 | * Clock divider value for following | ||
274 | * { DIVMFC, DIVJPEG, DIVFIMC0~3} | ||
275 | */ | ||
276 | |||
277 | /* SCLK_MFC: 200MHz */ | ||
278 | {3, 3, 4}, | ||
279 | /* SCLK_MFC: 200MHz */ | ||
280 | {3, 3, 4}, | ||
281 | /* SCLK_MFC: 160MHz */ | ||
282 | {4, 4, 5}, | ||
283 | /* SCLK_MFC: 133MHz */ | ||
284 | {5, 5, 5}, | ||
285 | /* SCLK_MFC: 100MHz */ | ||
286 | {7, 7, 7}, | ||
287 | }; | ||
288 | |||
289 | |||
290 | static int exynos4210_set_busclk(struct busfreq_data *data, | ||
291 | struct busfreq_opp_info *oppi) | ||
292 | { | ||
293 | unsigned int index; | ||
294 | unsigned int tmp; | ||
295 | |||
296 | for (index = LV_0; index < EX4210_LV_NUM; index++) | ||
297 | if (oppi->rate == exynos4210_busclk_table[index].clk) | ||
298 | break; | ||
299 | |||
300 | if (index == EX4210_LV_NUM) | ||
301 | return -EINVAL; | ||
302 | |||
303 | /* Change Divider - DMC0 */ | ||
304 | tmp = data->dmc_divtable[index]; | ||
305 | |||
306 | __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); | ||
307 | |||
308 | do { | ||
309 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); | ||
310 | } while (tmp & 0x11111111); | ||
311 | |||
312 | /* Change Divider - TOP */ | ||
313 | tmp = data->top_divtable[index]; | ||
314 | |||
315 | __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); | ||
316 | |||
317 | do { | ||
318 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); | ||
319 | } while (tmp & 0x11111); | ||
320 | |||
321 | /* Change Divider - LEFTBUS */ | ||
322 | tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); | ||
323 | |||
324 | tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); | ||
325 | |||
326 | tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << | ||
327 | EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | | ||
328 | (exynos4210_clkdiv_lr_bus[index][1] << | ||
329 | EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); | ||
330 | |||
331 | __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); | ||
332 | |||
333 | do { | ||
334 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); | ||
335 | } while (tmp & 0x11); | ||
336 | |||
337 | /* Change Divider - RIGHTBUS */ | ||
338 | tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); | ||
339 | |||
340 | tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); | ||
341 | |||
342 | tmp |= ((exynos4210_clkdiv_lr_bus[index][0] << | ||
343 | EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | | ||
344 | (exynos4210_clkdiv_lr_bus[index][1] << | ||
345 | EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); | ||
346 | |||
347 | __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); | ||
348 | |||
349 | do { | ||
350 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); | ||
351 | } while (tmp & 0x11); | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static int exynos4x12_set_busclk(struct busfreq_data *data, | ||
357 | struct busfreq_opp_info *oppi) | ||
358 | { | ||
359 | unsigned int index; | ||
360 | unsigned int tmp; | ||
361 | |||
362 | for (index = LV_0; index < EX4x12_LV_NUM; index++) | ||
363 | if (oppi->rate == exynos4x12_mifclk_table[index].clk) | ||
364 | break; | ||
365 | |||
366 | if (index == EX4x12_LV_NUM) | ||
367 | return -EINVAL; | ||
368 | |||
369 | /* Change Divider - DMC0 */ | ||
370 | tmp = data->dmc_divtable[index]; | ||
371 | |||
372 | __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); | ||
373 | |||
374 | do { | ||
375 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); | ||
376 | } while (tmp & 0x11111111); | ||
377 | |||
378 | /* Change Divider - DMC1 */ | ||
379 | tmp = __raw_readl(EXYNOS4_CLKDIV_DMC1); | ||
380 | |||
381 | tmp &= ~(EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK | | ||
382 | EXYNOS4_CLKDIV_DMC1_C2C_MASK | | ||
383 | EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK); | ||
384 | |||
385 | tmp |= ((exynos4x12_clkdiv_dmc1[index][0] << | ||
386 | EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) | | ||
387 | (exynos4x12_clkdiv_dmc1[index][1] << | ||
388 | EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) | | ||
389 | (exynos4x12_clkdiv_dmc1[index][2] << | ||
390 | EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT)); | ||
391 | |||
392 | __raw_writel(tmp, EXYNOS4_CLKDIV_DMC1); | ||
393 | |||
394 | do { | ||
395 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC1); | ||
396 | } while (tmp & 0x111111); | ||
397 | |||
398 | /* Change Divider - TOP */ | ||
399 | tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); | ||
400 | |||
401 | tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK | | ||
402 | EXYNOS4_CLKDIV_TOP_ACLK100_MASK | | ||
403 | EXYNOS4_CLKDIV_TOP_ACLK160_MASK | | ||
404 | EXYNOS4_CLKDIV_TOP_ACLK133_MASK | | ||
405 | EXYNOS4_CLKDIV_TOP_ONENAND_MASK); | ||
406 | |||
407 | tmp |= ((exynos4x12_clkdiv_top[index][0] << | ||
408 | EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) | | ||
409 | (exynos4x12_clkdiv_top[index][1] << | ||
410 | EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | | ||
411 | (exynos4x12_clkdiv_top[index][2] << | ||
412 | EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | | ||
413 | (exynos4x12_clkdiv_top[index][3] << | ||
414 | EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | | ||
415 | (exynos4x12_clkdiv_top[index][4] << | ||
416 | EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); | ||
417 | |||
418 | __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); | ||
419 | |||
420 | do { | ||
421 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); | ||
422 | } while (tmp & 0x11111); | ||
423 | |||
424 | /* Change Divider - LEFTBUS */ | ||
425 | tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); | ||
426 | |||
427 | tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); | ||
428 | |||
429 | tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << | ||
430 | EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | | ||
431 | (exynos4x12_clkdiv_lr_bus[index][1] << | ||
432 | EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); | ||
433 | |||
434 | __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); | ||
435 | |||
436 | do { | ||
437 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); | ||
438 | } while (tmp & 0x11); | ||
439 | |||
440 | /* Change Divider - RIGHTBUS */ | ||
441 | tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); | ||
442 | |||
443 | tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); | ||
444 | |||
445 | tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] << | ||
446 | EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | | ||
447 | (exynos4x12_clkdiv_lr_bus[index][1] << | ||
448 | EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); | ||
449 | |||
450 | __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); | ||
451 | |||
452 | do { | ||
453 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); | ||
454 | } while (tmp & 0x11); | ||
455 | |||
456 | /* Change Divider - MFC */ | ||
457 | tmp = __raw_readl(EXYNOS4_CLKDIV_MFC); | ||
458 | |||
459 | tmp &= ~(EXYNOS4_CLKDIV_MFC_MASK); | ||
460 | |||
461 | tmp |= ((exynos4x12_clkdiv_sclkip[index][0] << | ||
462 | EXYNOS4_CLKDIV_MFC_SHIFT)); | ||
463 | |||
464 | __raw_writel(tmp, EXYNOS4_CLKDIV_MFC); | ||
465 | |||
466 | do { | ||
467 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_MFC); | ||
468 | } while (tmp & 0x1); | ||
469 | |||
470 | /* Change Divider - JPEG */ | ||
471 | tmp = __raw_readl(EXYNOS4_CLKDIV_CAM1); | ||
472 | |||
473 | tmp &= ~(EXYNOS4_CLKDIV_CAM1_JPEG_MASK); | ||
474 | |||
475 | tmp |= ((exynos4x12_clkdiv_sclkip[index][1] << | ||
476 | EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT)); | ||
477 | |||
478 | __raw_writel(tmp, EXYNOS4_CLKDIV_CAM1); | ||
479 | |||
480 | do { | ||
481 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); | ||
482 | } while (tmp & 0x1); | ||
483 | |||
484 | /* Change Divider - FIMC0~3 */ | ||
485 | tmp = __raw_readl(EXYNOS4_CLKDIV_CAM); | ||
486 | |||
487 | tmp &= ~(EXYNOS4_CLKDIV_CAM_FIMC0_MASK | EXYNOS4_CLKDIV_CAM_FIMC1_MASK | | ||
488 | EXYNOS4_CLKDIV_CAM_FIMC2_MASK | EXYNOS4_CLKDIV_CAM_FIMC3_MASK); | ||
489 | |||
490 | tmp |= ((exynos4x12_clkdiv_sclkip[index][2] << | ||
491 | EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) | | ||
492 | (exynos4x12_clkdiv_sclkip[index][2] << | ||
493 | EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) | | ||
494 | (exynos4x12_clkdiv_sclkip[index][2] << | ||
495 | EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) | | ||
496 | (exynos4x12_clkdiv_sclkip[index][2] << | ||
497 | EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT)); | ||
498 | |||
499 | __raw_writel(tmp, EXYNOS4_CLKDIV_CAM); | ||
500 | |||
501 | do { | ||
502 | tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); | ||
503 | } while (tmp & 0x1111); | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static int exynos4x12_get_intspec(unsigned long mifclk) | ||
509 | { | ||
510 | int i = 0; | ||
511 | |||
512 | while (exynos4x12_intclk_table[i].clk) { | ||
513 | if (exynos4x12_intclk_table[i].clk <= mifclk) | ||
514 | return i; | ||
515 | i++; | ||
516 | } | ||
517 | |||
518 | return -EINVAL; | ||
519 | } | ||
520 | |||
521 | static int exynos4_bus_setvolt(struct busfreq_data *data, | ||
522 | struct busfreq_opp_info *oppi, | ||
523 | struct busfreq_opp_info *oldoppi) | ||
524 | { | ||
525 | int err = 0, tmp; | ||
526 | unsigned long volt = oppi->volt; | ||
527 | |||
528 | switch (data->type) { | ||
529 | case TYPE_BUSF_EXYNOS4210: | ||
530 | /* OPP represents DMC clock + INT voltage */ | ||
531 | err = regulator_set_voltage(data->vdd_int, volt, | ||
532 | MAX_SAFEVOLT); | ||
533 | break; | ||
534 | case TYPE_BUSF_EXYNOS4x12: | ||
535 | /* OPP represents MIF clock + MIF voltage */ | ||
536 | err = regulator_set_voltage(data->vdd_mif, volt, | ||
537 | MAX_SAFEVOLT); | ||
538 | if (err) | ||
539 | break; | ||
540 | |||
541 | tmp = exynos4x12_get_intspec(oppi->rate); | ||
542 | if (tmp < 0) { | ||
543 | err = tmp; | ||
544 | regulator_set_voltage(data->vdd_mif, | ||
545 | oldoppi->volt, | ||
546 | MAX_SAFEVOLT); | ||
547 | break; | ||
548 | } | ||
549 | err = regulator_set_voltage(data->vdd_int, | ||
550 | exynos4x12_intclk_table[tmp].volt, | ||
551 | MAX_SAFEVOLT); | ||
552 | /* Try to recover */ | ||
553 | if (err) | ||
554 | regulator_set_voltage(data->vdd_mif, | ||
555 | oldoppi->volt, | ||
556 | MAX_SAFEVOLT); | ||
557 | break; | ||
558 | default: | ||
559 | err = -EINVAL; | ||
560 | } | ||
561 | |||
562 | return err; | ||
563 | } | ||
564 | |||
565 | static int exynos4_bus_target(struct device *dev, unsigned long *_freq, | ||
566 | u32 flags) | ||
567 | { | ||
568 | int err = 0; | ||
569 | struct platform_device *pdev = container_of(dev, struct platform_device, | ||
570 | dev); | ||
571 | struct busfreq_data *data = platform_get_drvdata(pdev); | ||
572 | struct dev_pm_opp *opp; | ||
573 | unsigned long freq; | ||
574 | unsigned long old_freq = data->curr_oppinfo.rate; | ||
575 | struct busfreq_opp_info new_oppinfo; | ||
576 | |||
577 | rcu_read_lock(); | ||
578 | opp = devfreq_recommended_opp(dev, _freq, flags); | ||
579 | if (IS_ERR(opp)) { | ||
580 | rcu_read_unlock(); | ||
581 | return PTR_ERR(opp); | ||
582 | } | ||
583 | new_oppinfo.rate = dev_pm_opp_get_freq(opp); | ||
584 | new_oppinfo.volt = dev_pm_opp_get_voltage(opp); | ||
585 | rcu_read_unlock(); | ||
586 | freq = new_oppinfo.rate; | ||
587 | |||
588 | if (old_freq == freq) | ||
589 | return 0; | ||
590 | |||
591 | dev_dbg(dev, "targeting %lukHz %luuV\n", freq, new_oppinfo.volt); | ||
592 | |||
593 | mutex_lock(&data->lock); | ||
594 | |||
595 | if (data->disabled) | ||
596 | goto out; | ||
597 | |||
598 | if (old_freq < freq) | ||
599 | err = exynos4_bus_setvolt(data, &new_oppinfo, | ||
600 | &data->curr_oppinfo); | ||
601 | if (err) | ||
602 | goto out; | ||
603 | |||
604 | if (old_freq != freq) { | ||
605 | switch (data->type) { | ||
606 | case TYPE_BUSF_EXYNOS4210: | ||
607 | err = exynos4210_set_busclk(data, &new_oppinfo); | ||
608 | break; | ||
609 | case TYPE_BUSF_EXYNOS4x12: | ||
610 | err = exynos4x12_set_busclk(data, &new_oppinfo); | ||
611 | break; | ||
612 | default: | ||
613 | err = -EINVAL; | ||
614 | } | ||
615 | } | ||
616 | if (err) | ||
617 | goto out; | ||
618 | |||
619 | if (old_freq > freq) | ||
620 | err = exynos4_bus_setvolt(data, &new_oppinfo, | ||
621 | &data->curr_oppinfo); | ||
622 | if (err) | ||
623 | goto out; | ||
624 | |||
625 | data->curr_oppinfo = new_oppinfo; | ||
626 | out: | ||
627 | mutex_unlock(&data->lock); | ||
628 | return err; | ||
629 | } | ||
630 | |||
631 | static int exynos4_bus_get_dev_status(struct device *dev, | ||
632 | struct devfreq_dev_status *stat) | ||
633 | { | ||
634 | struct busfreq_data *data = dev_get_drvdata(dev); | ||
635 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; | ||
636 | int busier; | ||
637 | |||
638 | exynos_read_ppmu(ppmu_data); | ||
639 | busier = exynos_get_busier_ppmu(ppmu_data); | ||
640 | stat->current_frequency = data->curr_oppinfo.rate; | ||
641 | |||
642 | /* Number of cycles spent on memory access */ | ||
643 | stat->busy_time = ppmu_data->ppmu[busier].count[PPMU_PMNCNT3]; | ||
644 | stat->busy_time *= 100 / BUS_SATURATION_RATIO; | ||
645 | stat->total_time = ppmu_data->ppmu[busier].ccnt; | ||
646 | |||
647 | /* If the counters have overflown, retry */ | ||
648 | if (ppmu_data->ppmu[busier].ccnt_overflow || | ||
649 | ppmu_data->ppmu[busier].count_overflow[0]) | ||
650 | return -EAGAIN; | ||
651 | |||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static struct devfreq_dev_profile exynos4_devfreq_profile = { | ||
656 | .initial_freq = 400000, | ||
657 | .polling_ms = 50, | ||
658 | .target = exynos4_bus_target, | ||
659 | .get_dev_status = exynos4_bus_get_dev_status, | ||
660 | }; | ||
661 | |||
662 | static int exynos4210_init_tables(struct busfreq_data *data) | ||
663 | { | ||
664 | u32 tmp; | ||
665 | int mgrp; | ||
666 | int i, err = 0; | ||
667 | |||
668 | tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); | ||
669 | for (i = LV_0; i < EX4210_LV_NUM; i++) { | ||
670 | tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | | ||
671 | EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | | ||
672 | EXYNOS4_CLKDIV_DMC0_DPHY_MASK | | ||
673 | EXYNOS4_CLKDIV_DMC0_DMC_MASK | | ||
674 | EXYNOS4_CLKDIV_DMC0_DMCD_MASK | | ||
675 | EXYNOS4_CLKDIV_DMC0_DMCP_MASK | | ||
676 | EXYNOS4_CLKDIV_DMC0_COPY2_MASK | | ||
677 | EXYNOS4_CLKDIV_DMC0_CORETI_MASK); | ||
678 | |||
679 | tmp |= ((exynos4210_clkdiv_dmc0[i][0] << | ||
680 | EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | | ||
681 | (exynos4210_clkdiv_dmc0[i][1] << | ||
682 | EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | | ||
683 | (exynos4210_clkdiv_dmc0[i][2] << | ||
684 | EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | | ||
685 | (exynos4210_clkdiv_dmc0[i][3] << | ||
686 | EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | | ||
687 | (exynos4210_clkdiv_dmc0[i][4] << | ||
688 | EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | | ||
689 | (exynos4210_clkdiv_dmc0[i][5] << | ||
690 | EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) | | ||
691 | (exynos4210_clkdiv_dmc0[i][6] << | ||
692 | EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) | | ||
693 | (exynos4210_clkdiv_dmc0[i][7] << | ||
694 | EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT)); | ||
695 | |||
696 | data->dmc_divtable[i] = tmp; | ||
697 | } | ||
698 | |||
699 | tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); | ||
700 | for (i = LV_0; i < EX4210_LV_NUM; i++) { | ||
701 | tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK200_MASK | | ||
702 | EXYNOS4_CLKDIV_TOP_ACLK100_MASK | | ||
703 | EXYNOS4_CLKDIV_TOP_ACLK160_MASK | | ||
704 | EXYNOS4_CLKDIV_TOP_ACLK133_MASK | | ||
705 | EXYNOS4_CLKDIV_TOP_ONENAND_MASK); | ||
706 | |||
707 | tmp |= ((exynos4210_clkdiv_top[i][0] << | ||
708 | EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) | | ||
709 | (exynos4210_clkdiv_top[i][1] << | ||
710 | EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | | ||
711 | (exynos4210_clkdiv_top[i][2] << | ||
712 | EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | | ||
713 | (exynos4210_clkdiv_top[i][3] << | ||
714 | EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | | ||
715 | (exynos4210_clkdiv_top[i][4] << | ||
716 | EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); | ||
717 | |||
718 | data->top_divtable[i] = tmp; | ||
719 | } | ||
720 | |||
721 | /* | ||
722 | * TODO: init tmp based on busfreq_data | ||
723 | * (device-tree or platform-data) | ||
724 | */ | ||
725 | tmp = 0; /* Max voltages for the reliability of the unknown */ | ||
726 | |||
727 | pr_debug("ASV Group of Exynos4 is %d\n", tmp); | ||
728 | /* Use merged grouping for voltage */ | ||
729 | switch (tmp) { | ||
730 | case 0: | ||
731 | mgrp = 0; | ||
732 | break; | ||
733 | case 1: | ||
734 | case 2: | ||
735 | mgrp = 1; | ||
736 | break; | ||
737 | case 3: | ||
738 | case 4: | ||
739 | mgrp = 2; | ||
740 | break; | ||
741 | case 5: | ||
742 | case 6: | ||
743 | mgrp = 3; | ||
744 | break; | ||
745 | case 7: | ||
746 | mgrp = 4; | ||
747 | break; | ||
748 | default: | ||
749 | pr_warn("Unknown ASV Group. Use max voltage.\n"); | ||
750 | mgrp = 0; | ||
751 | } | ||
752 | |||
753 | for (i = LV_0; i < EX4210_LV_NUM; i++) | ||
754 | exynos4210_busclk_table[i].volt = exynos4210_asv_volt[mgrp][i]; | ||
755 | |||
756 | for (i = LV_0; i < EX4210_LV_NUM; i++) { | ||
757 | err = dev_pm_opp_add(data->dev, exynos4210_busclk_table[i].clk, | ||
758 | exynos4210_busclk_table[i].volt); | ||
759 | if (err) { | ||
760 | dev_err(data->dev, "Cannot add opp entries.\n"); | ||
761 | return err; | ||
762 | } | ||
763 | } | ||
764 | |||
765 | |||
766 | return 0; | ||
767 | } | ||
768 | |||
769 | static int exynos4x12_init_tables(struct busfreq_data *data) | ||
770 | { | ||
771 | unsigned int i; | ||
772 | unsigned int tmp; | ||
773 | int ret; | ||
774 | |||
775 | /* Enable pause function for DREX2 DVFS */ | ||
776 | tmp = __raw_readl(EXYNOS4_DMC_PAUSE_CTRL); | ||
777 | tmp |= EXYNOS4_DMC_PAUSE_ENABLE; | ||
778 | __raw_writel(tmp, EXYNOS4_DMC_PAUSE_CTRL); | ||
779 | |||
780 | tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); | ||
781 | |||
782 | for (i = 0; i < EX4x12_LV_NUM; i++) { | ||
783 | tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | | ||
784 | EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | | ||
785 | EXYNOS4_CLKDIV_DMC0_DPHY_MASK | | ||
786 | EXYNOS4_CLKDIV_DMC0_DMC_MASK | | ||
787 | EXYNOS4_CLKDIV_DMC0_DMCD_MASK | | ||
788 | EXYNOS4_CLKDIV_DMC0_DMCP_MASK); | ||
789 | |||
790 | tmp |= ((exynos4x12_clkdiv_dmc0[i][0] << | ||
791 | EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | | ||
792 | (exynos4x12_clkdiv_dmc0[i][1] << | ||
793 | EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | | ||
794 | (exynos4x12_clkdiv_dmc0[i][2] << | ||
795 | EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | | ||
796 | (exynos4x12_clkdiv_dmc0[i][3] << | ||
797 | EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | | ||
798 | (exynos4x12_clkdiv_dmc0[i][4] << | ||
799 | EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | | ||
800 | (exynos4x12_clkdiv_dmc0[i][5] << | ||
801 | EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT)); | ||
802 | |||
803 | data->dmc_divtable[i] = tmp; | ||
804 | } | ||
805 | |||
806 | tmp = 0; /* Max voltages for the reliability of the unknown */ | ||
807 | |||
808 | if (tmp > 8) | ||
809 | tmp = 0; | ||
810 | pr_debug("ASV Group of Exynos4x12 is %d\n", tmp); | ||
811 | |||
812 | for (i = 0; i < EX4x12_LV_NUM; i++) { | ||
813 | exynos4x12_mifclk_table[i].volt = | ||
814 | exynos4x12_mif_step_50[tmp][i]; | ||
815 | exynos4x12_intclk_table[i].volt = | ||
816 | exynos4x12_int_volt[tmp][i]; | ||
817 | } | ||
818 | |||
819 | for (i = 0; i < EX4x12_LV_NUM; i++) { | ||
820 | ret = dev_pm_opp_add(data->dev, exynos4x12_mifclk_table[i].clk, | ||
821 | exynos4x12_mifclk_table[i].volt); | ||
822 | if (ret) { | ||
823 | dev_err(data->dev, "Fail to add opp entries.\n"); | ||
824 | return ret; | ||
825 | } | ||
826 | } | ||
827 | |||
828 | return 0; | ||
829 | } | ||
830 | |||
831 | static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, | ||
832 | unsigned long event, void *ptr) | ||
833 | { | ||
834 | struct busfreq_data *data = container_of(this, struct busfreq_data, | ||
835 | pm_notifier); | ||
836 | struct dev_pm_opp *opp; | ||
837 | struct busfreq_opp_info new_oppinfo; | ||
838 | unsigned long maxfreq = ULONG_MAX; | ||
839 | int err = 0; | ||
840 | |||
841 | switch (event) { | ||
842 | case PM_SUSPEND_PREPARE: | ||
843 | /* Set Fastest and Deactivate DVFS */ | ||
844 | mutex_lock(&data->lock); | ||
845 | |||
846 | data->disabled = true; | ||
847 | |||
848 | rcu_read_lock(); | ||
849 | opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq); | ||
850 | if (IS_ERR(opp)) { | ||
851 | rcu_read_unlock(); | ||
852 | dev_err(data->dev, "%s: unable to find a min freq\n", | ||
853 | __func__); | ||
854 | mutex_unlock(&data->lock); | ||
855 | return PTR_ERR(opp); | ||
856 | } | ||
857 | new_oppinfo.rate = dev_pm_opp_get_freq(opp); | ||
858 | new_oppinfo.volt = dev_pm_opp_get_voltage(opp); | ||
859 | rcu_read_unlock(); | ||
860 | |||
861 | err = exynos4_bus_setvolt(data, &new_oppinfo, | ||
862 | &data->curr_oppinfo); | ||
863 | if (err) | ||
864 | goto unlock; | ||
865 | |||
866 | switch (data->type) { | ||
867 | case TYPE_BUSF_EXYNOS4210: | ||
868 | err = exynos4210_set_busclk(data, &new_oppinfo); | ||
869 | break; | ||
870 | case TYPE_BUSF_EXYNOS4x12: | ||
871 | err = exynos4x12_set_busclk(data, &new_oppinfo); | ||
872 | break; | ||
873 | default: | ||
874 | err = -EINVAL; | ||
875 | } | ||
876 | if (err) | ||
877 | goto unlock; | ||
878 | |||
879 | data->curr_oppinfo = new_oppinfo; | ||
880 | unlock: | ||
881 | mutex_unlock(&data->lock); | ||
882 | if (err) | ||
883 | return err; | ||
884 | return NOTIFY_OK; | ||
885 | case PM_POST_RESTORE: | ||
886 | case PM_POST_SUSPEND: | ||
887 | /* Reactivate */ | ||
888 | mutex_lock(&data->lock); | ||
889 | data->disabled = false; | ||
890 | mutex_unlock(&data->lock); | ||
891 | return NOTIFY_OK; | ||
892 | } | ||
893 | |||
894 | return NOTIFY_DONE; | ||
895 | } | ||
896 | |||
897 | static int exynos4_busfreq_probe(struct platform_device *pdev) | ||
898 | { | ||
899 | struct busfreq_data *data; | ||
900 | struct busfreq_ppmu_data *ppmu_data; | ||
901 | struct dev_pm_opp *opp; | ||
902 | struct device *dev = &pdev->dev; | ||
903 | int err = 0; | ||
904 | |||
905 | data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data), GFP_KERNEL); | ||
906 | if (data == NULL) { | ||
907 | dev_err(dev, "Cannot allocate memory.\n"); | ||
908 | return -ENOMEM; | ||
909 | } | ||
910 | |||
911 | ppmu_data = &data->ppmu_data; | ||
912 | ppmu_data->ppmu_end = PPMU_END; | ||
913 | ppmu_data->ppmu = devm_kzalloc(dev, | ||
914 | sizeof(struct exynos_ppmu) * PPMU_END, | ||
915 | GFP_KERNEL); | ||
916 | if (!ppmu_data->ppmu) { | ||
917 | dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); | ||
918 | return -ENOMEM; | ||
919 | } | ||
920 | |||
921 | data->type = pdev->id_entry->driver_data; | ||
922 | ppmu_data->ppmu[PPMU_DMC0].hw_base = S5P_VA_DMC0; | ||
923 | ppmu_data->ppmu[PPMU_DMC1].hw_base = S5P_VA_DMC1; | ||
924 | data->pm_notifier.notifier_call = exynos4_busfreq_pm_notifier_event; | ||
925 | data->dev = dev; | ||
926 | mutex_init(&data->lock); | ||
927 | |||
928 | switch (data->type) { | ||
929 | case TYPE_BUSF_EXYNOS4210: | ||
930 | err = exynos4210_init_tables(data); | ||
931 | break; | ||
932 | case TYPE_BUSF_EXYNOS4x12: | ||
933 | err = exynos4x12_init_tables(data); | ||
934 | break; | ||
935 | default: | ||
936 | dev_err(dev, "Cannot determine the device id %d\n", data->type); | ||
937 | err = -EINVAL; | ||
938 | } | ||
939 | if (err) { | ||
940 | dev_err(dev, "Cannot initialize busfreq table %d\n", | ||
941 | data->type); | ||
942 | return err; | ||
943 | } | ||
944 | |||
945 | data->vdd_int = devm_regulator_get(dev, "vdd_int"); | ||
946 | if (IS_ERR(data->vdd_int)) { | ||
947 | dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); | ||
948 | return PTR_ERR(data->vdd_int); | ||
949 | } | ||
950 | if (data->type == TYPE_BUSF_EXYNOS4x12) { | ||
951 | data->vdd_mif = devm_regulator_get(dev, "vdd_mif"); | ||
952 | if (IS_ERR(data->vdd_mif)) { | ||
953 | dev_err(dev, "Cannot get the regulator \"vdd_mif\"\n"); | ||
954 | return PTR_ERR(data->vdd_mif); | ||
955 | } | ||
956 | } | ||
957 | |||
958 | rcu_read_lock(); | ||
959 | opp = dev_pm_opp_find_freq_floor(dev, | ||
960 | &exynos4_devfreq_profile.initial_freq); | ||
961 | if (IS_ERR(opp)) { | ||
962 | rcu_read_unlock(); | ||
963 | dev_err(dev, "Invalid initial frequency %lu kHz.\n", | ||
964 | exynos4_devfreq_profile.initial_freq); | ||
965 | return PTR_ERR(opp); | ||
966 | } | ||
967 | data->curr_oppinfo.rate = dev_pm_opp_get_freq(opp); | ||
968 | data->curr_oppinfo.volt = dev_pm_opp_get_voltage(opp); | ||
969 | rcu_read_unlock(); | ||
970 | |||
971 | platform_set_drvdata(pdev, data); | ||
972 | |||
973 | data->devfreq = devm_devfreq_add_device(dev, &exynos4_devfreq_profile, | ||
974 | "simple_ondemand", NULL); | ||
975 | if (IS_ERR(data->devfreq)) | ||
976 | return PTR_ERR(data->devfreq); | ||
977 | |||
978 | /* | ||
979 | * Start PPMU (Performance Profiling Monitoring Unit) to check | ||
980 | * utilization of each IP in the Exynos4 SoC. | ||
981 | */ | ||
982 | busfreq_mon_reset(ppmu_data); | ||
983 | |||
984 | /* Register opp_notifier for Exynos4 busfreq */ | ||
985 | err = devm_devfreq_register_opp_notifier(dev, data->devfreq); | ||
986 | if (err < 0) { | ||
987 | dev_err(dev, "Failed to register opp notifier\n"); | ||
988 | return err; | ||
989 | } | ||
990 | |||
991 | /* Register pm_notifier for Exynos4 busfreq */ | ||
992 | err = register_pm_notifier(&data->pm_notifier); | ||
993 | if (err) { | ||
994 | dev_err(dev, "Failed to setup pm notifier\n"); | ||
995 | return err; | ||
996 | } | ||
997 | |||
998 | return 0; | ||
999 | } | ||
1000 | |||
1001 | static int exynos4_busfreq_remove(struct platform_device *pdev) | ||
1002 | { | ||
1003 | struct busfreq_data *data = platform_get_drvdata(pdev); | ||
1004 | |||
1005 | /* Unregister all of notifier chain */ | ||
1006 | unregister_pm_notifier(&data->pm_notifier); | ||
1007 | |||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | #ifdef CONFIG_PM_SLEEP | ||
1012 | static int exynos4_busfreq_resume(struct device *dev) | ||
1013 | { | ||
1014 | struct busfreq_data *data = dev_get_drvdata(dev); | ||
1015 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; | ||
1016 | |||
1017 | busfreq_mon_reset(ppmu_data); | ||
1018 | return 0; | ||
1019 | } | ||
1020 | #endif | ||
1021 | |||
1022 | static SIMPLE_DEV_PM_OPS(exynos4_busfreq_pm_ops, NULL, exynos4_busfreq_resume); | ||
1023 | |||
1024 | static const struct platform_device_id exynos4_busfreq_id[] = { | ||
1025 | { "exynos4210-busfreq", TYPE_BUSF_EXYNOS4210 }, | ||
1026 | { "exynos4412-busfreq", TYPE_BUSF_EXYNOS4x12 }, | ||
1027 | { "exynos4212-busfreq", TYPE_BUSF_EXYNOS4x12 }, | ||
1028 | { }, | ||
1029 | }; | ||
1030 | |||
1031 | static struct platform_driver exynos4_busfreq_driver = { | ||
1032 | .probe = exynos4_busfreq_probe, | ||
1033 | .remove = exynos4_busfreq_remove, | ||
1034 | .id_table = exynos4_busfreq_id, | ||
1035 | .driver = { | ||
1036 | .name = "exynos4-busfreq", | ||
1037 | .pm = &exynos4_busfreq_pm_ops, | ||
1038 | }, | ||
1039 | }; | ||
1040 | |||
1041 | static int __init exynos4_busfreq_init(void) | ||
1042 | { | ||
1043 | return platform_driver_register(&exynos4_busfreq_driver); | ||
1044 | } | ||
1045 | late_initcall(exynos4_busfreq_init); | ||
1046 | |||
1047 | static void __exit exynos4_busfreq_exit(void) | ||
1048 | { | ||
1049 | platform_driver_unregister(&exynos4_busfreq_driver); | ||
1050 | } | ||
1051 | module_exit(exynos4_busfreq_exit); | ||
1052 | |||
1053 | MODULE_LICENSE("GPL"); | ||
1054 | MODULE_DESCRIPTION("EXYNOS4 busfreq driver with devfreq framework"); | ||
1055 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||
diff --git a/drivers/devfreq/exynos/exynos4_bus.h b/drivers/devfreq/exynos/exynos4_bus.h deleted file mode 100644 index 94c73c18d28c..000000000000 --- a/drivers/devfreq/exynos/exynos4_bus.h +++ /dev/null | |||
@@ -1,110 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com/ | ||
4 | * | ||
5 | * EXYNOS4 BUS header | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef __DEVFREQ_EXYNOS4_BUS_H | ||
13 | #define __DEVFREQ_EXYNOS4_BUS_H __FILE__ | ||
14 | |||
15 | #include <mach/map.h> | ||
16 | |||
17 | #define EXYNOS4_CLKDIV_LEFTBUS (S5P_VA_CMU + 0x04500) | ||
18 | #define EXYNOS4_CLKDIV_STAT_LEFTBUS (S5P_VA_CMU + 0x04600) | ||
19 | |||
20 | #define EXYNOS4_CLKDIV_RIGHTBUS (S5P_VA_CMU + 0x08500) | ||
21 | #define EXYNOS4_CLKDIV_STAT_RIGHTBUS (S5P_VA_CMU + 0x08600) | ||
22 | |||
23 | #define EXYNOS4_CLKDIV_TOP (S5P_VA_CMU + 0x0C510) | ||
24 | #define EXYNOS4_CLKDIV_CAM (S5P_VA_CMU + 0x0C520) | ||
25 | #define EXYNOS4_CLKDIV_MFC (S5P_VA_CMU + 0x0C528) | ||
26 | |||
27 | #define EXYNOS4_CLKDIV_STAT_TOP (S5P_VA_CMU + 0x0C610) | ||
28 | #define EXYNOS4_CLKDIV_STAT_MFC (S5P_VA_CMU + 0x0C628) | ||
29 | |||
30 | #define EXYNOS4210_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x0C930) | ||
31 | #define EXYNOS4212_CLKGATE_IP_IMAGE (S5P_VA_CMU + 0x04930) | ||
32 | |||
33 | #define EXYNOS4_CLKDIV_DMC0 (S5P_VA_CMU + 0x10500) | ||
34 | #define EXYNOS4_CLKDIV_DMC1 (S5P_VA_CMU + 0x10504) | ||
35 | #define EXYNOS4_CLKDIV_STAT_DMC0 (S5P_VA_CMU + 0x10600) | ||
36 | #define EXYNOS4_CLKDIV_STAT_DMC1 (S5P_VA_CMU + 0x10604) | ||
37 | |||
38 | #define EXYNOS4_DMC_PAUSE_CTRL (S5P_VA_CMU + 0x11094) | ||
39 | #define EXYNOS4_DMC_PAUSE_ENABLE (1 << 0) | ||
40 | |||
41 | #define EXYNOS4_CLKDIV_DMC0_ACP_SHIFT (0) | ||
42 | #define EXYNOS4_CLKDIV_DMC0_ACP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | ||
43 | #define EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT (4) | ||
44 | #define EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | ||
45 | #define EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT (8) | ||
46 | #define EXYNOS4_CLKDIV_DMC0_DPHY_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | ||
47 | #define EXYNOS4_CLKDIV_DMC0_DMC_SHIFT (12) | ||
48 | #define EXYNOS4_CLKDIV_DMC0_DMC_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | ||
49 | #define EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT (16) | ||
50 | #define EXYNOS4_CLKDIV_DMC0_DMCD_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | ||
51 | #define EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT (20) | ||
52 | #define EXYNOS4_CLKDIV_DMC0_DMCP_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) | ||
53 | #define EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT (24) | ||
54 | #define EXYNOS4_CLKDIV_DMC0_COPY2_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) | ||
55 | #define EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT (28) | ||
56 | #define EXYNOS4_CLKDIV_DMC0_CORETI_MASK (0x7 << EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT) | ||
57 | |||
58 | #define EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT (0) | ||
59 | #define EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK (0xf << EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) | ||
60 | #define EXYNOS4_CLKDIV_DMC1_C2C_SHIFT (4) | ||
61 | #define EXYNOS4_CLKDIV_DMC1_C2C_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) | ||
62 | #define EXYNOS4_CLKDIV_DMC1_PWI_SHIFT (8) | ||
63 | #define EXYNOS4_CLKDIV_DMC1_PWI_MASK (0xf << EXYNOS4_CLKDIV_DMC1_PWI_SHIFT) | ||
64 | #define EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT (12) | ||
65 | #define EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK (0x7 << EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT) | ||
66 | #define EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT (16) | ||
67 | #define EXYNOS4_CLKDIV_DMC1_DVSEM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DVSEM_SHIFT) | ||
68 | #define EXYNOS4_CLKDIV_DMC1_DPM_SHIFT (24) | ||
69 | #define EXYNOS4_CLKDIV_DMC1_DPM_MASK (0x7f << EXYNOS4_CLKDIV_DMC1_DPM_SHIFT) | ||
70 | |||
71 | #define EXYNOS4_CLKDIV_MFC_SHIFT (0) | ||
72 | #define EXYNOS4_CLKDIV_MFC_MASK (0x7 << EXYNOS4_CLKDIV_MFC_SHIFT) | ||
73 | |||
74 | #define EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT (0) | ||
75 | #define EXYNOS4_CLKDIV_TOP_ACLK200_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) | ||
76 | #define EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT (4) | ||
77 | #define EXYNOS4_CLKDIV_TOP_ACLK100_MASK (0xF << EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | ||
78 | #define EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT (8) | ||
79 | #define EXYNOS4_CLKDIV_TOP_ACLK160_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | ||
80 | #define EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT (12) | ||
81 | #define EXYNOS4_CLKDIV_TOP_ACLK133_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | ||
82 | #define EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT (16) | ||
83 | #define EXYNOS4_CLKDIV_TOP_ONENAND_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT) | ||
84 | #define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT (20) | ||
85 | #define EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) | ||
86 | #define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT (24) | ||
87 | #define EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_MASK (0x7 << EXYNOS4_CLKDIV_TOP_ACLK400_MCUISP_SHIFT) | ||
88 | |||
89 | #define EXYNOS4_CLKDIV_BUS_GDLR_SHIFT (0) | ||
90 | #define EXYNOS4_CLKDIV_BUS_GDLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | ||
91 | #define EXYNOS4_CLKDIV_BUS_GPLR_SHIFT (4) | ||
92 | #define EXYNOS4_CLKDIV_BUS_GPLR_MASK (0x7 << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT) | ||
93 | |||
94 | #define EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT (0) | ||
95 | #define EXYNOS4_CLKDIV_CAM_FIMC0_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) | ||
96 | #define EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT (4) | ||
97 | #define EXYNOS4_CLKDIV_CAM_FIMC1_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) | ||
98 | #define EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT (8) | ||
99 | #define EXYNOS4_CLKDIV_CAM_FIMC2_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) | ||
100 | #define EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT (12) | ||
101 | #define EXYNOS4_CLKDIV_CAM_FIMC3_MASK (0xf << EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT) | ||
102 | |||
103 | #define EXYNOS4_CLKDIV_CAM1 (S5P_VA_CMU + 0x0C568) | ||
104 | |||
105 | #define EXYNOS4_CLKDIV_STAT_CAM1 (S5P_VA_CMU + 0x0C668) | ||
106 | |||
107 | #define EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT (0) | ||
108 | #define EXYNOS4_CLKDIV_CAM1_JPEG_MASK (0xf << EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT) | ||
109 | |||
110 | #endif /* __DEVFREQ_EXYNOS4_BUS_H */ | ||
diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c deleted file mode 100644 index 297ea30d4159..000000000000 --- a/drivers/devfreq/exynos/exynos5_bus.c +++ /dev/null | |||
@@ -1,431 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com/ | ||
4 | * | ||
5 | * EXYNOS5 INT clock frequency scaling support using DEVFREQ framework | ||
6 | * Based on work done by Jonghwan Choi <jhbird.choi@samsung.com> | ||
7 | * Support for only EXYNOS5250 is present. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/devfreq.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/pm_opp.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/suspend.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/pm_qos.h> | ||
25 | #include <linux/regulator/consumer.h> | ||
26 | #include <linux/of_address.h> | ||
27 | #include <linux/of_platform.h> | ||
28 | |||
29 | #include "exynos_ppmu.h" | ||
30 | |||
31 | #define MAX_SAFEVOLT 1100000 /* 1.10V */ | ||
32 | /* Assume that the bus is saturated if the utilization is 25% */ | ||
33 | #define INT_BUS_SATURATION_RATIO 25 | ||
34 | |||
35 | enum int_level_idx { | ||
36 | LV_0, | ||
37 | LV_1, | ||
38 | LV_2, | ||
39 | LV_3, | ||
40 | LV_4, | ||
41 | _LV_END | ||
42 | }; | ||
43 | |||
44 | enum exynos_ppmu_list { | ||
45 | PPMU_RIGHT, | ||
46 | PPMU_END, | ||
47 | }; | ||
48 | |||
49 | struct busfreq_data_int { | ||
50 | struct device *dev; | ||
51 | struct devfreq *devfreq; | ||
52 | struct regulator *vdd_int; | ||
53 | struct busfreq_ppmu_data ppmu_data; | ||
54 | unsigned long curr_freq; | ||
55 | bool disabled; | ||
56 | |||
57 | struct notifier_block pm_notifier; | ||
58 | struct mutex lock; | ||
59 | struct pm_qos_request int_req; | ||
60 | struct clk *int_clk; | ||
61 | }; | ||
62 | |||
63 | struct int_bus_opp_table { | ||
64 | unsigned int idx; | ||
65 | unsigned long clk; | ||
66 | unsigned long volt; | ||
67 | }; | ||
68 | |||
69 | static struct int_bus_opp_table exynos5_int_opp_table[] = { | ||
70 | {LV_0, 266000, 1025000}, | ||
71 | {LV_1, 200000, 1025000}, | ||
72 | {LV_2, 160000, 1025000}, | ||
73 | {LV_3, 133000, 1025000}, | ||
74 | {LV_4, 100000, 1025000}, | ||
75 | {0, 0, 0}, | ||
76 | }; | ||
77 | |||
78 | static int exynos5_int_setvolt(struct busfreq_data_int *data, | ||
79 | unsigned long volt) | ||
80 | { | ||
81 | return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT); | ||
82 | } | ||
83 | |||
84 | static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq, | ||
85 | u32 flags) | ||
86 | { | ||
87 | int err = 0; | ||
88 | struct platform_device *pdev = container_of(dev, struct platform_device, | ||
89 | dev); | ||
90 | struct busfreq_data_int *data = platform_get_drvdata(pdev); | ||
91 | struct dev_pm_opp *opp; | ||
92 | unsigned long old_freq, freq; | ||
93 | unsigned long volt; | ||
94 | |||
95 | rcu_read_lock(); | ||
96 | opp = devfreq_recommended_opp(dev, _freq, flags); | ||
97 | if (IS_ERR(opp)) { | ||
98 | rcu_read_unlock(); | ||
99 | dev_err(dev, "%s: Invalid OPP.\n", __func__); | ||
100 | return PTR_ERR(opp); | ||
101 | } | ||
102 | |||
103 | freq = dev_pm_opp_get_freq(opp); | ||
104 | volt = dev_pm_opp_get_voltage(opp); | ||
105 | rcu_read_unlock(); | ||
106 | |||
107 | old_freq = data->curr_freq; | ||
108 | |||
109 | if (old_freq == freq) | ||
110 | return 0; | ||
111 | |||
112 | dev_dbg(dev, "targeting %lukHz %luuV\n", freq, volt); | ||
113 | |||
114 | mutex_lock(&data->lock); | ||
115 | |||
116 | if (data->disabled) | ||
117 | goto out; | ||
118 | |||
119 | if (freq > exynos5_int_opp_table[0].clk) | ||
120 | pm_qos_update_request(&data->int_req, freq * 16 / 1000); | ||
121 | else | ||
122 | pm_qos_update_request(&data->int_req, -1); | ||
123 | |||
124 | if (old_freq < freq) | ||
125 | err = exynos5_int_setvolt(data, volt); | ||
126 | if (err) | ||
127 | goto out; | ||
128 | |||
129 | err = clk_set_rate(data->int_clk, freq * 1000); | ||
130 | |||
131 | if (err) | ||
132 | goto out; | ||
133 | |||
134 | if (old_freq > freq) | ||
135 | err = exynos5_int_setvolt(data, volt); | ||
136 | if (err) | ||
137 | goto out; | ||
138 | |||
139 | data->curr_freq = freq; | ||
140 | out: | ||
141 | mutex_unlock(&data->lock); | ||
142 | return err; | ||
143 | } | ||
144 | |||
145 | static int exynos5_int_get_dev_status(struct device *dev, | ||
146 | struct devfreq_dev_status *stat) | ||
147 | { | ||
148 | struct platform_device *pdev = container_of(dev, struct platform_device, | ||
149 | dev); | ||
150 | struct busfreq_data_int *data = platform_get_drvdata(pdev); | ||
151 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; | ||
152 | int busier_dmc; | ||
153 | |||
154 | exynos_read_ppmu(ppmu_data); | ||
155 | busier_dmc = exynos_get_busier_ppmu(ppmu_data); | ||
156 | |||
157 | stat->current_frequency = data->curr_freq; | ||
158 | |||
159 | /* Number of cycles spent on memory access */ | ||
160 | stat->busy_time = ppmu_data->ppmu[busier_dmc].count[PPMU_PMNCNT3]; | ||
161 | stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO; | ||
162 | stat->total_time = ppmu_data->ppmu[busier_dmc].ccnt; | ||
163 | |||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static struct devfreq_dev_profile exynos5_devfreq_int_profile = { | ||
168 | .initial_freq = 160000, | ||
169 | .polling_ms = 100, | ||
170 | .target = exynos5_busfreq_int_target, | ||
171 | .get_dev_status = exynos5_int_get_dev_status, | ||
172 | }; | ||
173 | |||
174 | static int exynos5250_init_int_tables(struct busfreq_data_int *data) | ||
175 | { | ||
176 | int i, err = 0; | ||
177 | |||
178 | for (i = LV_0; i < _LV_END; i++) { | ||
179 | err = dev_pm_opp_add(data->dev, exynos5_int_opp_table[i].clk, | ||
180 | exynos5_int_opp_table[i].volt); | ||
181 | if (err) { | ||
182 | dev_err(data->dev, "Cannot add opp entries.\n"); | ||
183 | return err; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this, | ||
191 | unsigned long event, void *ptr) | ||
192 | { | ||
193 | struct busfreq_data_int *data = container_of(this, | ||
194 | struct busfreq_data_int, pm_notifier); | ||
195 | struct dev_pm_opp *opp; | ||
196 | unsigned long maxfreq = ULONG_MAX; | ||
197 | unsigned long freq; | ||
198 | unsigned long volt; | ||
199 | int err = 0; | ||
200 | |||
201 | switch (event) { | ||
202 | case PM_SUSPEND_PREPARE: | ||
203 | /* Set Fastest and Deactivate DVFS */ | ||
204 | mutex_lock(&data->lock); | ||
205 | |||
206 | data->disabled = true; | ||
207 | |||
208 | rcu_read_lock(); | ||
209 | opp = dev_pm_opp_find_freq_floor(data->dev, &maxfreq); | ||
210 | if (IS_ERR(opp)) { | ||
211 | rcu_read_unlock(); | ||
212 | err = PTR_ERR(opp); | ||
213 | goto unlock; | ||
214 | } | ||
215 | freq = dev_pm_opp_get_freq(opp); | ||
216 | volt = dev_pm_opp_get_voltage(opp); | ||
217 | rcu_read_unlock(); | ||
218 | |||
219 | err = exynos5_int_setvolt(data, volt); | ||
220 | if (err) | ||
221 | goto unlock; | ||
222 | |||
223 | err = clk_set_rate(data->int_clk, freq * 1000); | ||
224 | |||
225 | if (err) | ||
226 | goto unlock; | ||
227 | |||
228 | data->curr_freq = freq; | ||
229 | unlock: | ||
230 | mutex_unlock(&data->lock); | ||
231 | if (err) | ||
232 | return NOTIFY_BAD; | ||
233 | return NOTIFY_OK; | ||
234 | case PM_POST_RESTORE: | ||
235 | case PM_POST_SUSPEND: | ||
236 | /* Reactivate */ | ||
237 | mutex_lock(&data->lock); | ||
238 | data->disabled = false; | ||
239 | mutex_unlock(&data->lock); | ||
240 | return NOTIFY_OK; | ||
241 | } | ||
242 | |||
243 | return NOTIFY_DONE; | ||
244 | } | ||
245 | |||
246 | static int exynos5_busfreq_int_probe(struct platform_device *pdev) | ||
247 | { | ||
248 | struct busfreq_data_int *data; | ||
249 | struct busfreq_ppmu_data *ppmu_data; | ||
250 | struct dev_pm_opp *opp; | ||
251 | struct device *dev = &pdev->dev; | ||
252 | struct device_node *np; | ||
253 | unsigned long initial_freq; | ||
254 | unsigned long initial_volt; | ||
255 | int err = 0; | ||
256 | int i; | ||
257 | |||
258 | data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int), | ||
259 | GFP_KERNEL); | ||
260 | if (data == NULL) { | ||
261 | dev_err(dev, "Cannot allocate memory.\n"); | ||
262 | return -ENOMEM; | ||
263 | } | ||
264 | |||
265 | ppmu_data = &data->ppmu_data; | ||
266 | ppmu_data->ppmu_end = PPMU_END; | ||
267 | ppmu_data->ppmu = devm_kzalloc(dev, | ||
268 | sizeof(struct exynos_ppmu) * PPMU_END, | ||
269 | GFP_KERNEL); | ||
270 | if (!ppmu_data->ppmu) { | ||
271 | dev_err(dev, "Failed to allocate memory for exynos_ppmu\n"); | ||
272 | return -ENOMEM; | ||
273 | } | ||
274 | |||
275 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu"); | ||
276 | if (np == NULL) { | ||
277 | pr_err("Unable to find PPMU node\n"); | ||
278 | return -ENOENT; | ||
279 | } | ||
280 | |||
281 | for (i = 0; i < ppmu_data->ppmu_end; i++) { | ||
282 | /* map PPMU memory region */ | ||
283 | ppmu_data->ppmu[i].hw_base = of_iomap(np, i); | ||
284 | if (ppmu_data->ppmu[i].hw_base == NULL) { | ||
285 | dev_err(&pdev->dev, "failed to map memory region\n"); | ||
286 | return -ENOMEM; | ||
287 | } | ||
288 | } | ||
289 | data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event; | ||
290 | data->dev = dev; | ||
291 | mutex_init(&data->lock); | ||
292 | |||
293 | err = exynos5250_init_int_tables(data); | ||
294 | if (err) | ||
295 | return err; | ||
296 | |||
297 | data->vdd_int = devm_regulator_get(dev, "vdd_int"); | ||
298 | if (IS_ERR(data->vdd_int)) { | ||
299 | dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); | ||
300 | return PTR_ERR(data->vdd_int); | ||
301 | } | ||
302 | |||
303 | data->int_clk = devm_clk_get(dev, "int_clk"); | ||
304 | if (IS_ERR(data->int_clk)) { | ||
305 | dev_err(dev, "Cannot get clock \"int_clk\"\n"); | ||
306 | return PTR_ERR(data->int_clk); | ||
307 | } | ||
308 | |||
309 | rcu_read_lock(); | ||
310 | opp = dev_pm_opp_find_freq_floor(dev, | ||
311 | &exynos5_devfreq_int_profile.initial_freq); | ||
312 | if (IS_ERR(opp)) { | ||
313 | rcu_read_unlock(); | ||
314 | dev_err(dev, "Invalid initial frequency %lu kHz.\n", | ||
315 | exynos5_devfreq_int_profile.initial_freq); | ||
316 | return PTR_ERR(opp); | ||
317 | } | ||
318 | initial_freq = dev_pm_opp_get_freq(opp); | ||
319 | initial_volt = dev_pm_opp_get_voltage(opp); | ||
320 | rcu_read_unlock(); | ||
321 | data->curr_freq = initial_freq; | ||
322 | |||
323 | err = clk_set_rate(data->int_clk, initial_freq * 1000); | ||
324 | if (err) { | ||
325 | dev_err(dev, "Failed to set initial frequency\n"); | ||
326 | return err; | ||
327 | } | ||
328 | |||
329 | err = exynos5_int_setvolt(data, initial_volt); | ||
330 | if (err) | ||
331 | return err; | ||
332 | |||
333 | platform_set_drvdata(pdev, data); | ||
334 | |||
335 | busfreq_mon_reset(ppmu_data); | ||
336 | |||
337 | data->devfreq = devm_devfreq_add_device(dev, &exynos5_devfreq_int_profile, | ||
338 | "simple_ondemand", NULL); | ||
339 | if (IS_ERR(data->devfreq)) | ||
340 | return PTR_ERR(data->devfreq); | ||
341 | |||
342 | err = devm_devfreq_register_opp_notifier(dev, data->devfreq); | ||
343 | if (err < 0) { | ||
344 | dev_err(dev, "Failed to register opp notifier\n"); | ||
345 | return err; | ||
346 | } | ||
347 | |||
348 | err = register_pm_notifier(&data->pm_notifier); | ||
349 | if (err) { | ||
350 | dev_err(dev, "Failed to setup pm notifier\n"); | ||
351 | return err; | ||
352 | } | ||
353 | |||
354 | /* TODO: Add a new QOS class for int/mif bus */ | ||
355 | pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1); | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static int exynos5_busfreq_int_remove(struct platform_device *pdev) | ||
361 | { | ||
362 | struct busfreq_data_int *data = platform_get_drvdata(pdev); | ||
363 | |||
364 | pm_qos_remove_request(&data->int_req); | ||
365 | unregister_pm_notifier(&data->pm_notifier); | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | #ifdef CONFIG_PM_SLEEP | ||
371 | static int exynos5_busfreq_int_resume(struct device *dev) | ||
372 | { | ||
373 | struct platform_device *pdev = container_of(dev, struct platform_device, | ||
374 | dev); | ||
375 | struct busfreq_data_int *data = platform_get_drvdata(pdev); | ||
376 | struct busfreq_ppmu_data *ppmu_data = &data->ppmu_data; | ||
377 | |||
378 | busfreq_mon_reset(ppmu_data); | ||
379 | return 0; | ||
380 | } | ||
381 | static const struct dev_pm_ops exynos5_busfreq_int_pm = { | ||
382 | .resume = exynos5_busfreq_int_resume, | ||
383 | }; | ||
384 | #endif | ||
385 | static SIMPLE_DEV_PM_OPS(exynos5_busfreq_int_pm_ops, NULL, | ||
386 | exynos5_busfreq_int_resume); | ||
387 | |||
388 | /* platform device pointer for exynos5 devfreq device. */ | ||
389 | static struct platform_device *exynos5_devfreq_pdev; | ||
390 | |||
391 | static struct platform_driver exynos5_busfreq_int_driver = { | ||
392 | .probe = exynos5_busfreq_int_probe, | ||
393 | .remove = exynos5_busfreq_int_remove, | ||
394 | .driver = { | ||
395 | .name = "exynos5-bus-int", | ||
396 | .pm = &exynos5_busfreq_int_pm_ops, | ||
397 | }, | ||
398 | }; | ||
399 | |||
400 | static int __init exynos5_busfreq_int_init(void) | ||
401 | { | ||
402 | int ret; | ||
403 | |||
404 | ret = platform_driver_register(&exynos5_busfreq_int_driver); | ||
405 | if (ret < 0) | ||
406 | goto out; | ||
407 | |||
408 | exynos5_devfreq_pdev = | ||
409 | platform_device_register_simple("exynos5-bus-int", -1, NULL, 0); | ||
410 | if (IS_ERR(exynos5_devfreq_pdev)) { | ||
411 | ret = PTR_ERR(exynos5_devfreq_pdev); | ||
412 | goto out1; | ||
413 | } | ||
414 | |||
415 | return 0; | ||
416 | out1: | ||
417 | platform_driver_unregister(&exynos5_busfreq_int_driver); | ||
418 | out: | ||
419 | return ret; | ||
420 | } | ||
421 | late_initcall(exynos5_busfreq_int_init); | ||
422 | |||
423 | static void __exit exynos5_busfreq_int_exit(void) | ||
424 | { | ||
425 | platform_device_unregister(exynos5_devfreq_pdev); | ||
426 | platform_driver_unregister(&exynos5_busfreq_int_driver); | ||
427 | } | ||
428 | module_exit(exynos5_busfreq_int_exit); | ||
429 | |||
430 | MODULE_LICENSE("GPL"); | ||
431 | MODULE_DESCRIPTION("EXYNOS5 busfreq driver with devfreq framework"); | ||
diff --git a/drivers/devfreq/exynos/exynos_ppmu.c b/drivers/devfreq/exynos/exynos_ppmu.c deleted file mode 100644 index 97b75e513d29..000000000000 --- a/drivers/devfreq/exynos/exynos_ppmu.c +++ /dev/null | |||
@@ -1,119 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com/ | ||
4 | * | ||
5 | * EXYNOS - PPMU support | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/io.h> | ||
15 | |||
16 | #include "exynos_ppmu.h" | ||
17 | |||
18 | void exynos_ppmu_reset(void __iomem *ppmu_base) | ||
19 | { | ||
20 | __raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, ppmu_base); | ||
21 | __raw_writel(PPMU_ENABLE_CYCLE | | ||
22 | PPMU_ENABLE_COUNT0 | | ||
23 | PPMU_ENABLE_COUNT1 | | ||
24 | PPMU_ENABLE_COUNT2 | | ||
25 | PPMU_ENABLE_COUNT3, | ||
26 | ppmu_base + PPMU_CNTENS); | ||
27 | } | ||
28 | |||
29 | void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, | ||
30 | unsigned int evt) | ||
31 | { | ||
32 | __raw_writel(evt, ppmu_base + PPMU_BEVTSEL(ch)); | ||
33 | } | ||
34 | |||
35 | void exynos_ppmu_start(void __iomem *ppmu_base) | ||
36 | { | ||
37 | __raw_writel(PPMU_ENABLE, ppmu_base); | ||
38 | } | ||
39 | |||
40 | void exynos_ppmu_stop(void __iomem *ppmu_base) | ||
41 | { | ||
42 | __raw_writel(PPMU_DISABLE, ppmu_base); | ||
43 | } | ||
44 | |||
45 | unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch) | ||
46 | { | ||
47 | unsigned int total; | ||
48 | |||
49 | if (ch == PPMU_PMNCNT3) | ||
50 | total = ((__raw_readl(ppmu_base + PMCNT_OFFSET(ch)) << 8) | | ||
51 | __raw_readl(ppmu_base + PMCNT_OFFSET(ch + 1))); | ||
52 | else | ||
53 | total = __raw_readl(ppmu_base + PMCNT_OFFSET(ch)); | ||
54 | |||
55 | return total; | ||
56 | } | ||
57 | |||
58 | void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data) | ||
59 | { | ||
60 | unsigned int i; | ||
61 | |||
62 | for (i = 0; i < ppmu_data->ppmu_end; i++) { | ||
63 | void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; | ||
64 | |||
65 | /* Reset the performance and cycle counters */ | ||
66 | exynos_ppmu_reset(ppmu_base); | ||
67 | |||
68 | /* Setup count registers to monitor read/write transactions */ | ||
69 | ppmu_data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; | ||
70 | exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, | ||
71 | ppmu_data->ppmu[i].event[PPMU_PMNCNT3]); | ||
72 | |||
73 | exynos_ppmu_start(ppmu_base); | ||
74 | } | ||
75 | } | ||
76 | EXPORT_SYMBOL(busfreq_mon_reset); | ||
77 | |||
78 | void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data) | ||
79 | { | ||
80 | int i, j; | ||
81 | |||
82 | for (i = 0; i < ppmu_data->ppmu_end; i++) { | ||
83 | void __iomem *ppmu_base = ppmu_data->ppmu[i].hw_base; | ||
84 | |||
85 | exynos_ppmu_stop(ppmu_base); | ||
86 | |||
87 | /* Update local data from PPMU */ | ||
88 | ppmu_data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); | ||
89 | |||
90 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | ||
91 | if (ppmu_data->ppmu[i].event[j] == 0) | ||
92 | ppmu_data->ppmu[i].count[j] = 0; | ||
93 | else | ||
94 | ppmu_data->ppmu[i].count[j] = | ||
95 | exynos_ppmu_read(ppmu_base, j); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | busfreq_mon_reset(ppmu_data); | ||
100 | } | ||
101 | EXPORT_SYMBOL(exynos_read_ppmu); | ||
102 | |||
103 | int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data) | ||
104 | { | ||
105 | unsigned int count = 0; | ||
106 | int i, j, busy = 0; | ||
107 | |||
108 | for (i = 0; i < ppmu_data->ppmu_end; i++) { | ||
109 | for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { | ||
110 | if (ppmu_data->ppmu[i].count[j] > count) { | ||
111 | count = ppmu_data->ppmu[i].count[j]; | ||
112 | busy = i; | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | return busy; | ||
118 | } | ||
119 | EXPORT_SYMBOL(exynos_get_busier_ppmu); | ||
diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h deleted file mode 100644 index 71f17ba3563c..000000000000 --- a/drivers/devfreq/exynos/exynos_ppmu.h +++ /dev/null | |||
@@ -1,86 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
3 | * http://www.samsung.com/ | ||
4 | * | ||
5 | * EXYNOS PPMU header | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef __DEVFREQ_EXYNOS_PPMU_H | ||
13 | #define __DEVFREQ_EXYNOS_PPMU_H __FILE__ | ||
14 | |||
15 | #include <linux/ktime.h> | ||
16 | |||
17 | /* For PPMU Control */ | ||
18 | #define PPMU_ENABLE BIT(0) | ||
19 | #define PPMU_DISABLE 0x0 | ||
20 | #define PPMU_CYCLE_RESET BIT(1) | ||
21 | #define PPMU_COUNTER_RESET BIT(2) | ||
22 | |||
23 | #define PPMU_ENABLE_COUNT0 BIT(0) | ||
24 | #define PPMU_ENABLE_COUNT1 BIT(1) | ||
25 | #define PPMU_ENABLE_COUNT2 BIT(2) | ||
26 | #define PPMU_ENABLE_COUNT3 BIT(3) | ||
27 | #define PPMU_ENABLE_CYCLE BIT(31) | ||
28 | |||
29 | #define PPMU_CNTENS 0x10 | ||
30 | #define PPMU_FLAG 0x50 | ||
31 | #define PPMU_CCNT_OVERFLOW BIT(31) | ||
32 | #define PPMU_CCNT 0x100 | ||
33 | |||
34 | #define PPMU_PMCNT0 0x110 | ||
35 | #define PPMU_PMCNT_OFFSET 0x10 | ||
36 | #define PMCNT_OFFSET(x) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x)) | ||
37 | |||
38 | #define PPMU_BEVT0SEL 0x1000 | ||
39 | #define PPMU_BEVTSEL_OFFSET 0x100 | ||
40 | #define PPMU_BEVTSEL(x) (PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET)) | ||
41 | |||
42 | /* For Event Selection */ | ||
43 | #define RD_DATA_COUNT 0x5 | ||
44 | #define WR_DATA_COUNT 0x6 | ||
45 | #define RDWR_DATA_COUNT 0x7 | ||
46 | |||
47 | enum ppmu_counter { | ||
48 | PPMU_PMNCNT0, | ||
49 | PPMU_PMCCNT1, | ||
50 | PPMU_PMNCNT2, | ||
51 | PPMU_PMNCNT3, | ||
52 | PPMU_PMNCNT_MAX, | ||
53 | }; | ||
54 | |||
55 | struct bus_opp_table { | ||
56 | unsigned int idx; | ||
57 | unsigned long clk; | ||
58 | unsigned long volt; | ||
59 | }; | ||
60 | |||
61 | struct exynos_ppmu { | ||
62 | void __iomem *hw_base; | ||
63 | unsigned int ccnt; | ||
64 | unsigned int event[PPMU_PMNCNT_MAX]; | ||
65 | unsigned int count[PPMU_PMNCNT_MAX]; | ||
66 | unsigned long long ns; | ||
67 | ktime_t reset_time; | ||
68 | bool ccnt_overflow; | ||
69 | bool count_overflow[PPMU_PMNCNT_MAX]; | ||
70 | }; | ||
71 | |||
72 | struct busfreq_ppmu_data { | ||
73 | struct exynos_ppmu *ppmu; | ||
74 | int ppmu_end; | ||
75 | }; | ||
76 | |||
77 | void exynos_ppmu_reset(void __iomem *ppmu_base); | ||
78 | void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, | ||
79 | unsigned int evt); | ||
80 | void exynos_ppmu_start(void __iomem *ppmu_base); | ||
81 | void exynos_ppmu_stop(void __iomem *ppmu_base); | ||
82 | unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch); | ||
83 | void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data); | ||
84 | void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data); | ||
85 | int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data); | ||
86 | #endif /* __DEVFREQ_EXYNOS_PPMU_H */ | ||
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c new file mode 100644 index 000000000000..9ef46e2592c4 --- /dev/null +++ b/drivers/devfreq/governor_passive.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * linux/drivers/devfreq/governor_passive.c | ||
3 | * | ||
4 | * Copyright (C) 2016 Samsung Electronics | ||
5 | * Author: Chanwoo Choi <cw00.choi@samsung.com> | ||
6 | * Author: MyungJoo Ham <myungjoo.ham@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/devfreq.h> | ||
16 | #include "governor.h" | ||
17 | |||
18 | static int devfreq_passive_get_target_freq(struct devfreq *devfreq, | ||
19 | unsigned long *freq) | ||
20 | { | ||
21 | struct devfreq_passive_data *p_data | ||
22 | = (struct devfreq_passive_data *)devfreq->data; | ||
23 | struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent; | ||
24 | unsigned long child_freq = ULONG_MAX; | ||
25 | struct dev_pm_opp *opp; | ||
26 | int i, count, ret = 0; | ||
27 | |||
28 | /* | ||
29 | * If the devfreq device with passive governor has the specific method | ||
30 | * to determine the next frequency, should use the get_target_freq() | ||
31 | * of struct devfreq_passive_data. | ||
32 | */ | ||
33 | if (p_data->get_target_freq) { | ||
34 | ret = p_data->get_target_freq(devfreq, freq); | ||
35 | goto out; | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * If the parent and passive devfreq device uses the OPP table, | ||
40 | * get the next frequency by using the OPP table. | ||
41 | */ | ||
42 | |||
43 | /* | ||
44 | * - parent devfreq device uses the governors except for passive. | ||
45 | * - passive devfreq device uses the passive governor. | ||
46 | * | ||
47 | * Each devfreq has the OPP table. After deciding the new frequency | ||
48 | * from the governor of parent devfreq device, the passive governor | ||
49 | * need to get the index of new frequency on OPP table of parent | ||
50 | * device. And then the index is used for getting the suitable | ||
51 | * new frequency for passive devfreq device. | ||
52 | */ | ||
53 | if (!devfreq->profile || !devfreq->profile->freq_table | ||
54 | || devfreq->profile->max_state <= 0) | ||
55 | return -EINVAL; | ||
56 | |||
57 | /* | ||
58 | * The passive governor have to get the correct frequency from OPP | ||
59 | * list of parent device. Because in this case, *freq is temporary | ||
60 | * value which is decided by ondemand governor. | ||
61 | */ | ||
62 | rcu_read_lock(); | ||
63 | opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0); | ||
64 | rcu_read_unlock(); | ||
65 | if (IS_ERR(opp)) { | ||
66 | ret = PTR_ERR(opp); | ||
67 | goto out; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Get the OPP table's index of decided freqeuncy by governor | ||
72 | * of parent device. | ||
73 | */ | ||
74 | for (i = 0; i < parent_devfreq->profile->max_state; i++) | ||
75 | if (parent_devfreq->profile->freq_table[i] == *freq) | ||
76 | break; | ||
77 | |||
78 | if (i == parent_devfreq->profile->max_state) { | ||
79 | ret = -EINVAL; | ||
80 | goto out; | ||
81 | } | ||
82 | |||
83 | /* Get the suitable frequency by using index of parent device. */ | ||
84 | if (i < devfreq->profile->max_state) { | ||
85 | child_freq = devfreq->profile->freq_table[i]; | ||
86 | } else { | ||
87 | count = devfreq->profile->max_state; | ||
88 | child_freq = devfreq->profile->freq_table[count - 1]; | ||
89 | } | ||
90 | |||
91 | /* Return the suitable frequency for passive device. */ | ||
92 | *freq = child_freq; | ||
93 | |||
94 | out: | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq) | ||
99 | { | ||
100 | int ret; | ||
101 | |||
102 | if (!devfreq->governor) | ||
103 | return -EINVAL; | ||
104 | |||
105 | mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING); | ||
106 | |||
107 | ret = devfreq->governor->get_target_freq(devfreq, &freq); | ||
108 | if (ret < 0) | ||
109 | goto out; | ||
110 | |||
111 | ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0); | ||
112 | if (ret < 0) | ||
113 | goto out; | ||
114 | |||
115 | devfreq->previous_freq = freq; | ||
116 | |||
117 | out: | ||
118 | mutex_unlock(&devfreq->lock); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int devfreq_passive_notifier_call(struct notifier_block *nb, | ||
124 | unsigned long event, void *ptr) | ||
125 | { | ||
126 | struct devfreq_passive_data *data | ||
127 | = container_of(nb, struct devfreq_passive_data, nb); | ||
128 | struct devfreq *devfreq = (struct devfreq *)data->this; | ||
129 | struct devfreq *parent = (struct devfreq *)data->parent; | ||
130 | struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr; | ||
131 | unsigned long freq = freqs->new; | ||
132 | |||
133 | switch (event) { | ||
134 | case DEVFREQ_PRECHANGE: | ||
135 | if (parent->previous_freq > freq) | ||
136 | update_devfreq_passive(devfreq, freq); | ||
137 | break; | ||
138 | case DEVFREQ_POSTCHANGE: | ||
139 | if (parent->previous_freq < freq) | ||
140 | update_devfreq_passive(devfreq, freq); | ||
141 | break; | ||
142 | } | ||
143 | |||
144 | return NOTIFY_DONE; | ||
145 | } | ||
146 | |||
147 | static int devfreq_passive_event_handler(struct devfreq *devfreq, | ||
148 | unsigned int event, void *data) | ||
149 | { | ||
150 | struct device *dev = devfreq->dev.parent; | ||
151 | struct devfreq_passive_data *p_data | ||
152 | = (struct devfreq_passive_data *)devfreq->data; | ||
153 | struct devfreq *parent = (struct devfreq *)p_data->parent; | ||
154 | struct notifier_block *nb = &p_data->nb; | ||
155 | int ret = 0; | ||
156 | |||
157 | if (!parent) | ||
158 | return -EPROBE_DEFER; | ||
159 | |||
160 | switch (event) { | ||
161 | case DEVFREQ_GOV_START: | ||
162 | if (!p_data->this) | ||
163 | p_data->this = devfreq; | ||
164 | |||
165 | nb->notifier_call = devfreq_passive_notifier_call; | ||
166 | ret = devm_devfreq_register_notifier(dev, parent, nb, | ||
167 | DEVFREQ_TRANSITION_NOTIFIER); | ||
168 | break; | ||
169 | case DEVFREQ_GOV_STOP: | ||
170 | devm_devfreq_unregister_notifier(dev, parent, nb, | ||
171 | DEVFREQ_TRANSITION_NOTIFIER); | ||
172 | break; | ||
173 | default: | ||
174 | break; | ||
175 | } | ||
176 | |||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | static struct devfreq_governor devfreq_passive = { | ||
181 | .name = "passive", | ||
182 | .get_target_freq = devfreq_passive_get_target_freq, | ||
183 | .event_handler = devfreq_passive_event_handler, | ||
184 | }; | ||
185 | |||
186 | static int __init devfreq_passive_init(void) | ||
187 | { | ||
188 | return devfreq_add_governor(&devfreq_passive); | ||
189 | } | ||
190 | subsys_initcall(devfreq_passive_init); | ||
191 | |||
192 | static void __exit devfreq_passive_exit(void) | ||
193 | { | ||
194 | int ret; | ||
195 | |||
196 | ret = devfreq_remove_governor(&devfreq_passive); | ||
197 | if (ret) | ||
198 | pr_err("%s: failed remove governor %d\n", __func__, ret); | ||
199 | } | ||
200 | module_exit(devfreq_passive_exit); | ||
201 | |||
202 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | ||
203 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | ||
204 | MODULE_DESCRIPTION("DEVFREQ Passive governor"); | ||
205 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 6fa02a20eb63..2de4e2eea180 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h | |||
@@ -19,6 +19,13 @@ | |||
19 | 19 | ||
20 | #define DEVFREQ_NAME_LEN 16 | 20 | #define DEVFREQ_NAME_LEN 16 |
21 | 21 | ||
22 | /* DEVFREQ notifier interface */ | ||
23 | #define DEVFREQ_TRANSITION_NOTIFIER (0) | ||
24 | |||
25 | /* Transition notifiers of DEVFREQ_TRANSITION_NOTIFIER */ | ||
26 | #define DEVFREQ_PRECHANGE (0) | ||
27 | #define DEVFREQ_POSTCHANGE (1) | ||
28 | |||
22 | struct devfreq; | 29 | struct devfreq; |
23 | 30 | ||
24 | /** | 31 | /** |
@@ -143,6 +150,7 @@ struct devfreq_governor { | |||
143 | * @trans_table: Statistics of devfreq transitions | 150 | * @trans_table: Statistics of devfreq transitions |
144 | * @time_in_state: Statistics of devfreq states | 151 | * @time_in_state: Statistics of devfreq states |
145 | * @last_stat_updated: The last time stat updated | 152 | * @last_stat_updated: The last time stat updated |
153 | * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier | ||
146 | * | 154 | * |
147 | * This structure stores the devfreq information for a give device. | 155 | * This structure stores the devfreq information for a give device. |
148 | * | 156 | * |
@@ -177,6 +185,13 @@ struct devfreq { | |||
177 | unsigned int *trans_table; | 185 | unsigned int *trans_table; |
178 | unsigned long *time_in_state; | 186 | unsigned long *time_in_state; |
179 | unsigned long last_stat_updated; | 187 | unsigned long last_stat_updated; |
188 | |||
189 | struct srcu_notifier_head transition_notifier_list; | ||
190 | }; | ||
191 | |||
192 | struct devfreq_freqs { | ||
193 | unsigned long old; | ||
194 | unsigned long new; | ||
180 | }; | 195 | }; |
181 | 196 | ||
182 | #if defined(CONFIG_PM_DEVFREQ) | 197 | #if defined(CONFIG_PM_DEVFREQ) |
@@ -207,6 +222,22 @@ extern int devm_devfreq_register_opp_notifier(struct device *dev, | |||
207 | struct devfreq *devfreq); | 222 | struct devfreq *devfreq); |
208 | extern void devm_devfreq_unregister_opp_notifier(struct device *dev, | 223 | extern void devm_devfreq_unregister_opp_notifier(struct device *dev, |
209 | struct devfreq *devfreq); | 224 | struct devfreq *devfreq); |
225 | extern int devfreq_register_notifier(struct devfreq *devfreq, | ||
226 | struct notifier_block *nb, | ||
227 | unsigned int list); | ||
228 | extern int devfreq_unregister_notifier(struct devfreq *devfreq, | ||
229 | struct notifier_block *nb, | ||
230 | unsigned int list); | ||
231 | extern int devm_devfreq_register_notifier(struct device *dev, | ||
232 | struct devfreq *devfreq, | ||
233 | struct notifier_block *nb, | ||
234 | unsigned int list); | ||
235 | extern void devm_devfreq_unregister_notifier(struct device *dev, | ||
236 | struct devfreq *devfreq, | ||
237 | struct notifier_block *nb, | ||
238 | unsigned int list); | ||
239 | extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, | ||
240 | int index); | ||
210 | 241 | ||
211 | /** | 242 | /** |
212 | * devfreq_update_stats() - update the last_status pointer in struct devfreq | 243 | * devfreq_update_stats() - update the last_status pointer in struct devfreq |
@@ -241,6 +272,39 @@ struct devfreq_simple_ondemand_data { | |||
241 | }; | 272 | }; |
242 | #endif | 273 | #endif |
243 | 274 | ||
275 | #if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) | ||
276 | /** | ||
277 | * struct devfreq_passive_data - void *data fed to struct devfreq | ||
278 | * and devfreq_add_device | ||
279 | * @parent: the devfreq instance of parent device. | ||
280 | * @get_target_freq: Optional callback, Returns desired operating frequency | ||
281 | * for the device using passive governor. That is called | ||
282 | * when passive governor should decide the next frequency | ||
283 | * by using the new frequency of parent devfreq device | ||
284 | * using governors except for passive governor. | ||
285 | * If the devfreq device has the specific method to decide | ||
286 | * the next frequency, should use this callback. | ||
287 | * @this: the devfreq instance of own device. | ||
288 | * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER list | ||
289 | * | ||
290 | * The devfreq_passive_data have to set the devfreq instance of parent | ||
291 | * device with governors except for the passive governor. But, don't need to | ||
292 | * initialize the 'this' and 'nb' field because the devfreq core will handle | ||
293 | * them. | ||
294 | */ | ||
295 | struct devfreq_passive_data { | ||
296 | /* Should set the devfreq instance of parent device */ | ||
297 | struct devfreq *parent; | ||
298 | |||
299 | /* Optional callback to decide the next frequency of passvice device */ | ||
300 | int (*get_target_freq)(struct devfreq *this, unsigned long *freq); | ||
301 | |||
302 | /* For passive governor's internal use. Don't need to set them */ | ||
303 | struct devfreq *this; | ||
304 | struct notifier_block nb; | ||
305 | }; | ||
306 | #endif | ||
307 | |||
244 | #else /* !CONFIG_PM_DEVFREQ */ | 308 | #else /* !CONFIG_PM_DEVFREQ */ |
245 | static inline struct devfreq *devfreq_add_device(struct device *dev, | 309 | static inline struct devfreq *devfreq_add_device(struct device *dev, |
246 | struct devfreq_dev_profile *profile, | 310 | struct devfreq_dev_profile *profile, |
@@ -307,6 +371,41 @@ static inline void devm_devfreq_unregister_opp_notifier(struct device *dev, | |||
307 | { | 371 | { |
308 | } | 372 | } |
309 | 373 | ||
374 | static inline int devfreq_register_notifier(struct devfreq *devfreq, | ||
375 | struct notifier_block *nb, | ||
376 | unsigned int list) | ||
377 | { | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static inline int devfreq_unregister_notifier(struct devfreq *devfreq, | ||
382 | struct notifier_block *nb, | ||
383 | unsigned int list) | ||
384 | { | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static inline int devm_devfreq_register_notifier(struct device *dev, | ||
389 | struct devfreq *devfreq, | ||
390 | struct notifier_block *nb, | ||
391 | unsigned int list) | ||
392 | { | ||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static inline void devm_devfreq_unregister_notifier(struct device *dev, | ||
397 | struct devfreq *devfreq, | ||
398 | struct notifier_block *nb, | ||
399 | unsigned int list) | ||
400 | { | ||
401 | } | ||
402 | |||
403 | static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, | ||
404 | int index) | ||
405 | { | ||
406 | return ERR_PTR(-ENODEV); | ||
407 | } | ||
408 | |||
310 | static inline int devfreq_update_stats(struct devfreq *df) | 409 | static inline int devfreq_update_stats(struct devfreq *df) |
311 | { | 410 | { |
312 | return -EINVAL; | 411 | return -EINVAL; |