OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From b018e44a68dc2f4df819ae194e39e07313841dad Mon Sep 17 00:00:00 2001 |
2 | From: Yangbo Lu <yangbo.lu@nxp.com> |
||
3 | Date: Wed, 17 Jan 2018 15:27:58 +0800 |
||
4 | Subject: [PATCH 15/30] cpufreq: support layerscape |
||
5 | |||
6 | This is an integrated patch for layerscape pm support. |
||
7 | |||
8 | Signed-off-by: Tang Yuantian <Yuantian.Tang@nxp.com> |
||
9 | Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> |
||
10 | --- |
||
11 | drivers/cpufreq/Kconfig | 2 +- |
||
12 | drivers/cpufreq/qoriq-cpufreq.c | 176 +++++++++++++++------------------------- |
||
13 | drivers/firmware/psci.c | 12 ++- |
||
14 | drivers/soc/fsl/rcpm.c | 158 ++++++++++++++++++++++++++++++++++++ |
||
15 | 4 files changed, 235 insertions(+), 113 deletions(-) |
||
16 | create mode 100644 drivers/soc/fsl/rcpm.c |
||
17 | |||
18 | --- a/drivers/cpufreq/Kconfig |
||
19 | +++ b/drivers/cpufreq/Kconfig |
||
20 | @@ -334,7 +334,7 @@ endif |
||
21 | |||
22 | config QORIQ_CPUFREQ |
||
23 | tristate "CPU frequency scaling driver for Freescale QorIQ SoCs" |
||
24 | - depends on OF && COMMON_CLK && (PPC_E500MC || ARM) |
||
25 | + depends on OF && COMMON_CLK && (PPC_E500MC || ARM || ARM64) |
||
26 | depends on !CPU_THERMAL || THERMAL |
||
27 | select CLK_QORIQ |
||
28 | help |
||
29 | --- a/drivers/cpufreq/qoriq-cpufreq.c |
||
30 | +++ b/drivers/cpufreq/qoriq-cpufreq.c |
||
31 | @@ -11,6 +11,7 @@ |
||
32 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
||
33 | |||
34 | #include <linux/clk.h> |
||
35 | +#include <linux/clk-provider.h> |
||
36 | #include <linux/cpufreq.h> |
||
37 | #include <linux/cpu_cooling.h> |
||
38 | #include <linux/errno.h> |
||
39 | @@ -22,10 +23,6 @@ |
||
40 | #include <linux/slab.h> |
||
41 | #include <linux/smp.h> |
||
42 | |||
43 | -#if !defined(CONFIG_ARM) |
||
44 | -#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */ |
||
45 | -#endif |
||
46 | - |
||
47 | /** |
||
48 | * struct cpu_data |
||
49 | * @pclk: the parent clock of cpu |
||
50 | @@ -37,73 +34,51 @@ struct cpu_data { |
||
51 | struct thermal_cooling_device *cdev; |
||
52 | }; |
||
53 | |||
54 | +/* |
||
55 | + * Don't use cpufreq on this SoC -- used when the SoC would have otherwise |
||
56 | + * matched a more generic compatible. |
||
57 | + */ |
||
58 | +#define SOC_BLACKLIST 1 |
||
59 | + |
||
60 | /** |
||
61 | * struct soc_data - SoC specific data |
||
62 | - * @freq_mask: mask the disallowed frequencies |
||
63 | - * @flag: unique flags |
||
64 | + * @flags: SOC_xxx |
||
65 | */ |
||
66 | struct soc_data { |
||
67 | - u32 freq_mask[4]; |
||
68 | - u32 flag; |
||
69 | -}; |
||
70 | - |
||
71 | -#define FREQ_MASK 1 |
||
72 | -/* see hardware specification for the allowed frqeuencies */ |
||
73 | -static const struct soc_data sdata[] = { |
||
74 | - { /* used by p2041 and p3041 */ |
||
75 | - .freq_mask = {0x8, 0x8, 0x2, 0x2}, |
||
76 | - .flag = FREQ_MASK, |
||
77 | - }, |
||
78 | - { /* used by p5020 */ |
||
79 | - .freq_mask = {0x8, 0x2}, |
||
80 | - .flag = FREQ_MASK, |
||
81 | - }, |
||
82 | - { /* used by p4080, p5040 */ |
||
83 | - .freq_mask = {0}, |
||
84 | - .flag = 0, |
||
85 | - }, |
||
86 | + u32 flags; |
||
87 | }; |
||
88 | |||
89 | -/* |
||
90 | - * the minimum allowed core frequency, in Hz |
||
91 | - * for chassis v1.0, >= platform frequency |
||
92 | - * for chassis v2.0, >= platform frequency / 2 |
||
93 | - */ |
||
94 | -static u32 min_cpufreq; |
||
95 | -static const u32 *fmask; |
||
96 | - |
||
97 | -#if defined(CONFIG_ARM) |
||
98 | -static int get_cpu_physical_id(int cpu) |
||
99 | -{ |
||
100 | - return topology_core_id(cpu); |
||
101 | -} |
||
102 | -#else |
||
103 | -static int get_cpu_physical_id(int cpu) |
||
104 | -{ |
||
105 | - return get_hard_smp_processor_id(cpu); |
||
106 | -} |
||
107 | -#endif |
||
108 | - |
||
109 | static u32 get_bus_freq(void) |
||
110 | { |
||
111 | struct device_node *soc; |
||
112 | u32 sysfreq; |
||
113 | + struct clk *pltclk; |
||
114 | + int ret; |
||
115 | |||
116 | + /* get platform freq by searching bus-frequency property */ |
||
117 | soc = of_find_node_by_type(NULL, "soc"); |
||
118 | - if (!soc) |
||
119 | - return 0; |
||
120 | - |
||
121 | - if (of_property_read_u32(soc, "bus-frequency", &sysfreq)) |
||
122 | - sysfreq = 0; |
||
123 | + if (soc) { |
||
124 | + ret = of_property_read_u32(soc, "bus-frequency", &sysfreq); |
||
125 | + of_node_put(soc); |
||
126 | + if (!ret) |
||
127 | + return sysfreq; |
||
128 | + } |
||
129 | |||
130 | - of_node_put(soc); |
||
131 | + /* get platform freq by its clock name */ |
||
132 | + pltclk = clk_get(NULL, "cg-pll0-div1"); |
||
133 | + if (IS_ERR(pltclk)) { |
||
134 | + pr_err("%s: can't get bus frequency %ld\n", |
||
135 | + __func__, PTR_ERR(pltclk)); |
||
136 | + return PTR_ERR(pltclk); |
||
137 | + } |
||
138 | |||
139 | - return sysfreq; |
||
140 | + return clk_get_rate(pltclk); |
||
141 | } |
||
142 | |||
143 | -static struct device_node *cpu_to_clk_node(int cpu) |
||
144 | +static struct clk *cpu_to_clk(int cpu) |
||
145 | { |
||
146 | - struct device_node *np, *clk_np; |
||
147 | + struct device_node *np; |
||
148 | + struct clk *clk; |
||
149 | |||
150 | if (!cpu_present(cpu)) |
||
151 | return NULL; |
||
152 | @@ -112,37 +87,28 @@ static struct device_node *cpu_to_clk_no |
||
153 | if (!np) |
||
154 | return NULL; |
||
155 | |||
156 | - clk_np = of_parse_phandle(np, "clocks", 0); |
||
157 | - if (!clk_np) |
||
158 | - return NULL; |
||
159 | - |
||
160 | + clk = of_clk_get(np, 0); |
||
161 | of_node_put(np); |
||
162 | - |
||
163 | - return clk_np; |
||
164 | + return clk; |
||
165 | } |
||
166 | |||
167 | /* traverse cpu nodes to get cpu mask of sharing clock wire */ |
||
168 | static void set_affected_cpus(struct cpufreq_policy *policy) |
||
169 | { |
||
170 | - struct device_node *np, *clk_np; |
||
171 | struct cpumask *dstp = policy->cpus; |
||
172 | + struct clk *clk; |
||
173 | int i; |
||
174 | |||
175 | - np = cpu_to_clk_node(policy->cpu); |
||
176 | - if (!np) |
||
177 | - return; |
||
178 | - |
||
179 | for_each_present_cpu(i) { |
||
180 | - clk_np = cpu_to_clk_node(i); |
||
181 | - if (!clk_np) |
||
182 | + clk = cpu_to_clk(i); |
||
183 | + if (IS_ERR(clk)) { |
||
184 | + pr_err("%s: no clock for cpu %d\n", __func__, i); |
||
185 | continue; |
||
186 | + } |
||
187 | |||
188 | - if (clk_np == np) |
||
189 | + if (clk_is_match(policy->clk, clk)) |
||
190 | cpumask_set_cpu(i, dstp); |
||
191 | - |
||
192 | - of_node_put(clk_np); |
||
193 | } |
||
194 | - of_node_put(np); |
||
195 | } |
||
196 | |||
197 | /* reduce the duplicated frequencies in frequency table */ |
||
198 | @@ -198,10 +164,11 @@ static void freq_table_sort(struct cpufr |
||
199 | |||
200 | static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) |
||
201 | { |
||
202 | - struct device_node *np, *pnode; |
||
203 | + struct device_node *np; |
||
204 | int i, count, ret; |
||
205 | - u32 freq, mask; |
||
206 | + u32 freq; |
||
207 | struct clk *clk; |
||
208 | + const struct clk_hw *hwclk; |
||
209 | struct cpufreq_frequency_table *table; |
||
210 | struct cpu_data *data; |
||
211 | unsigned int cpu = policy->cpu; |
||
212 | @@ -221,17 +188,13 @@ static int qoriq_cpufreq_cpu_init(struct |
||
213 | goto err_nomem2; |
||
214 | } |
||
215 | |||
216 | - pnode = of_parse_phandle(np, "clocks", 0); |
||
217 | - if (!pnode) { |
||
218 | - pr_err("%s: could not get clock information\n", __func__); |
||
219 | - goto err_nomem2; |
||
220 | - } |
||
221 | + hwclk = __clk_get_hw(policy->clk); |
||
222 | + count = clk_hw_get_num_parents(hwclk); |
||
223 | |||
224 | - count = of_property_count_strings(pnode, "clock-names"); |
||
225 | data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL); |
||
226 | if (!data->pclk) { |
||
227 | pr_err("%s: no memory\n", __func__); |
||
228 | - goto err_node; |
||
229 | + goto err_nomem2; |
||
230 | } |
||
231 | |||
232 | table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL); |
||
233 | @@ -240,23 +203,11 @@ static int qoriq_cpufreq_cpu_init(struct |
||
234 | goto err_pclk; |
||
235 | } |
||
236 | |||
237 | - if (fmask) |
||
238 | - mask = fmask[get_cpu_physical_id(cpu)]; |
||
239 | - else |
||
240 | - mask = 0x0; |
||
241 | - |
||
242 | for (i = 0; i < count; i++) { |
||
243 | - clk = of_clk_get(pnode, i); |
||
244 | + clk = clk_hw_get_parent_by_index(hwclk, i)->clk; |
||
245 | data->pclk[i] = clk; |
||
246 | freq = clk_get_rate(clk); |
||
247 | - /* |
||
248 | - * the clock is valid if its frequency is not masked |
||
249 | - * and large than minimum allowed frequency. |
||
250 | - */ |
||
251 | - if (freq < min_cpufreq || (mask & (1 << i))) |
||
252 | - table[i].frequency = CPUFREQ_ENTRY_INVALID; |
||
253 | - else |
||
254 | - table[i].frequency = freq / 1000; |
||
255 | + table[i].frequency = freq / 1000; |
||
256 | table[i].driver_data = i; |
||
257 | } |
||
258 | freq_table_redup(table, count); |
||
259 | @@ -282,7 +233,6 @@ static int qoriq_cpufreq_cpu_init(struct |
||
260 | policy->cpuinfo.transition_latency = u64temp + 1; |
||
261 | |||
262 | of_node_put(np); |
||
263 | - of_node_put(pnode); |
||
264 | |||
265 | return 0; |
||
266 | |||
267 | @@ -290,10 +240,7 @@ err_nomem1: |
||
268 | kfree(table); |
||
269 | err_pclk: |
||
270 | kfree(data->pclk); |
||
271 | -err_node: |
||
272 | - of_node_put(pnode); |
||
273 | err_nomem2: |
||
274 | - policy->driver_data = NULL; |
||
275 | kfree(data); |
||
276 | err_np: |
||
277 | of_node_put(np); |
||
278 | @@ -357,12 +304,25 @@ static struct cpufreq_driver qoriq_cpufr |
||
279 | .attr = cpufreq_generic_attr, |
||
280 | }; |
||
281 | |||
282 | +static const struct soc_data blacklist = { |
||
283 | + .flags = SOC_BLACKLIST, |
||
284 | +}; |
||
285 | + |
||
286 | static const struct of_device_id node_matches[] __initconst = { |
||
287 | - { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], }, |
||
288 | - { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], }, |
||
289 | - { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], }, |
||
290 | - { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], }, |
||
291 | - { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], }, |
||
292 | + /* e6500 cannot use cpufreq due to erratum A-008083 */ |
||
293 | + { .compatible = "fsl,b4420-clockgen", &blacklist }, |
||
294 | + { .compatible = "fsl,b4860-clockgen", &blacklist }, |
||
295 | + { .compatible = "fsl,t2080-clockgen", &blacklist }, |
||
296 | + { .compatible = "fsl,t4240-clockgen", &blacklist }, |
||
297 | + |
||
298 | + { .compatible = "fsl,ls1012a-clockgen", }, |
||
299 | + { .compatible = "fsl,ls1021a-clockgen", }, |
||
300 | + { .compatible = "fsl,ls1043a-clockgen", }, |
||
301 | + { .compatible = "fsl,ls1046a-clockgen", }, |
||
302 | + { .compatible = "fsl,ls1088a-clockgen", }, |
||
303 | + { .compatible = "fsl,ls2080a-clockgen", }, |
||
304 | + { .compatible = "fsl,p4080-clockgen", }, |
||
305 | + { .compatible = "fsl,qoriq-clockgen-1.0", }, |
||
306 | { .compatible = "fsl,qoriq-clockgen-2.0", }, |
||
307 | {} |
||
308 | }; |
||
309 | @@ -380,16 +340,12 @@ static int __init qoriq_cpufreq_init(voi |
||
310 | |||
311 | match = of_match_node(node_matches, np); |
||
312 | data = match->data; |
||
313 | - if (data) { |
||
314 | - if (data->flag) |
||
315 | - fmask = data->freq_mask; |
||
316 | - min_cpufreq = get_bus_freq(); |
||
317 | - } else { |
||
318 | - min_cpufreq = get_bus_freq() / 2; |
||
319 | - } |
||
320 | |||
321 | of_node_put(np); |
||
322 | |||
323 | + if (data && data->flags & SOC_BLACKLIST) |
||
324 | + return -ENODEV; |
||
325 | + |
||
326 | ret = cpufreq_register_driver(&qoriq_cpufreq_driver); |
||
327 | if (!ret) |
||
328 | pr_info("Freescale QorIQ CPU frequency scaling driver\n"); |
||
329 | --- a/drivers/firmware/psci.c |
||
330 | +++ b/drivers/firmware/psci.c |
||
331 | @@ -437,8 +437,12 @@ CPUIDLE_METHOD_OF_DECLARE(psci, "psci", |
||
332 | |||
333 | static int psci_system_suspend(unsigned long unused) |
||
334 | { |
||
335 | - return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), |
||
336 | - virt_to_phys(cpu_resume), 0, 0); |
||
337 | + u32 state; |
||
338 | + |
||
339 | + state = ( 2 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | |
||
340 | + (1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT); |
||
341 | + |
||
342 | + return psci_cpu_suspend(state, virt_to_phys(cpu_resume)); |
||
343 | } |
||
344 | |||
345 | static int psci_system_suspend_enter(suspend_state_t state) |
||
346 | @@ -458,6 +462,8 @@ static void __init psci_init_system_susp |
||
347 | if (!IS_ENABLED(CONFIG_SUSPEND)) |
||
348 | return; |
||
349 | |||
350 | + suspend_set_ops(&psci_suspend_ops); |
||
351 | + |
||
352 | ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND)); |
||
353 | |||
354 | if (ret != PSCI_RET_NOT_SUPPORTED) |
||
355 | @@ -562,6 +568,8 @@ static void __init psci_0_2_set_function |
||
356 | arm_pm_restart = psci_sys_reset; |
||
357 | |||
358 | pm_power_off = psci_sys_poweroff; |
||
359 | + psci_init_system_suspend(); |
||
360 | + suspend_set_ops(&psci_suspend_ops); |
||
361 | } |
||
362 | |||
363 | /* |
||
364 | --- /dev/null |
||
365 | +++ b/drivers/soc/fsl/rcpm.c |
||
366 | @@ -0,0 +1,158 @@ |
||
367 | +/* |
||
368 | + * Run Control and Power Management (RCPM) driver |
||
369 | + * |
||
370 | + * Copyright 2016 NXP |
||
371 | + * |
||
372 | + * This program is free software; you can redistribute it and/or modify |
||
373 | + * it under the terms of the GNU General Public License as published by |
||
374 | + * the Free Software Foundation; either version 2 of the License, or |
||
375 | + * (at your option) any later version. |
||
376 | + * |
||
377 | + * This program is distributed in the hope that it will be useful, |
||
378 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
379 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
380 | + * GNU General Public License for more details. |
||
381 | + * |
||
382 | + */ |
||
383 | +#define pr_fmt(fmt) "RCPM: %s: " fmt, __func__ |
||
384 | + |
||
385 | +#include <linux/kernel.h> |
||
386 | +#include <linux/io.h> |
||
387 | +#include <linux/of_platform.h> |
||
388 | +#include <linux/of_address.h> |
||
389 | +#include <linux/suspend.h> |
||
390 | + |
||
391 | +/* RCPM register offset */ |
||
392 | +#define RCPM_IPPDEXPCR0 0x140 |
||
393 | + |
||
394 | +#define RCPM_WAKEUP_CELL_SIZE 2 |
||
395 | + |
||
396 | +struct rcpm_config { |
||
397 | + int ipp_num; |
||
398 | + int ippdexpcr_offset; |
||
399 | + u32 ippdexpcr[2]; |
||
400 | + void *rcpm_reg_base; |
||
401 | +}; |
||
402 | + |
||
403 | +static struct rcpm_config *rcpm; |
||
404 | + |
||
405 | +static inline void rcpm_reg_write(u32 offset, u32 value) |
||
406 | +{ |
||
407 | + iowrite32be(value, rcpm->rcpm_reg_base + offset); |
||
408 | +} |
||
409 | + |
||
410 | +static inline u32 rcpm_reg_read(u32 offset) |
||
411 | +{ |
||
412 | + return ioread32be(rcpm->rcpm_reg_base + offset); |
||
413 | +} |
||
414 | + |
||
415 | +static void rcpm_wakeup_fixup(struct device *dev, void *data) |
||
416 | +{ |
||
417 | + struct device_node *node = dev ? dev->of_node : NULL; |
||
418 | + u32 value[RCPM_WAKEUP_CELL_SIZE]; |
||
419 | + int ret, i; |
||
420 | + |
||
421 | + if (!dev || !node || !device_may_wakeup(dev)) |
||
422 | + return; |
||
423 | + |
||
424 | + /* |
||
425 | + * Get the values in the "rcpm-wakeup" property. |
||
426 | + * Three values are: |
||
427 | + * The first is a pointer to the RCPM node. |
||
428 | + * The second is the value of the ippdexpcr0 register. |
||
429 | + * The third is the value of the ippdexpcr1 register. |
||
430 | + */ |
||
431 | + ret = of_property_read_u32_array(node, "fsl,rcpm-wakeup", |
||
432 | + value, RCPM_WAKEUP_CELL_SIZE); |
||
433 | + if (ret) |
||
434 | + return; |
||
435 | + |
||
436 | + pr_debug("wakeup source: the device %s\n", node->full_name); |
||
437 | + |
||
438 | + for (i = 0; i < rcpm->ipp_num; i++) |
||
439 | + rcpm->ippdexpcr[i] |= value[i + 1]; |
||
440 | +} |
||
441 | + |
||
442 | +static int rcpm_suspend_prepare(void) |
||
443 | +{ |
||
444 | + int i; |
||
445 | + u32 val; |
||
446 | + |
||
447 | + BUG_ON(!rcpm); |
||
448 | + |
||
449 | + for (i = 0; i < rcpm->ipp_num; i++) |
||
450 | + rcpm->ippdexpcr[i] = 0; |
||
451 | + |
||
452 | + dpm_for_each_dev(NULL, rcpm_wakeup_fixup); |
||
453 | + |
||
454 | + for (i = 0; i < rcpm->ipp_num; i++) { |
||
455 | + if (rcpm->ippdexpcr[i]) { |
||
456 | + val = rcpm_reg_read(rcpm->ippdexpcr_offset + 4 * i); |
||
457 | + rcpm_reg_write(rcpm->ippdexpcr_offset + 4 * i, |
||
458 | + val | rcpm->ippdexpcr[i]); |
||
459 | + pr_debug("ippdexpcr%d = 0x%x\n", i, rcpm->ippdexpcr[i]); |
||
460 | + } |
||
461 | + } |
||
462 | + |
||
463 | + return 0; |
||
464 | +} |
||
465 | + |
||
466 | +static int rcpm_suspend_notifier_call(struct notifier_block *bl, |
||
467 | + unsigned long state, |
||
468 | + void *unused) |
||
469 | +{ |
||
470 | + switch (state) { |
||
471 | + case PM_SUSPEND_PREPARE: |
||
472 | + rcpm_suspend_prepare(); |
||
473 | + break; |
||
474 | + } |
||
475 | + |
||
476 | + return NOTIFY_DONE; |
||
477 | +} |
||
478 | + |
||
479 | +static struct rcpm_config rcpm_default_config = { |
||
480 | + .ipp_num = 1, |
||
481 | + .ippdexpcr_offset = RCPM_IPPDEXPCR0, |
||
482 | +}; |
||
483 | + |
||
484 | +static const struct of_device_id rcpm_matches[] = { |
||
485 | + { |
||
486 | + .compatible = "fsl,qoriq-rcpm-2.1", |
||
487 | + .data = &rcpm_default_config, |
||
488 | + }, |
||
489 | + {} |
||
490 | +}; |
||
491 | + |
||
492 | +static struct notifier_block rcpm_suspend_notifier = { |
||
493 | + .notifier_call = rcpm_suspend_notifier_call, |
||
494 | +}; |
||
495 | + |
||
496 | +static int __init layerscape_rcpm_init(void) |
||
497 | +{ |
||
498 | + const struct of_device_id *match; |
||
499 | + struct device_node *np; |
||
500 | + |
||
501 | + np = of_find_matching_node_and_match(NULL, rcpm_matches, &match); |
||
502 | + if (!np) { |
||
503 | + pr_err("Can't find the RCPM node.\n"); |
||
504 | + return -EINVAL; |
||
505 | + } |
||
506 | + |
||
507 | + if (match->data) |
||
508 | + rcpm = (struct rcpm_config *)match->data; |
||
509 | + else |
||
510 | + return -EINVAL; |
||
511 | + |
||
512 | + rcpm->rcpm_reg_base = of_iomap(np, 0); |
||
513 | + of_node_put(np); |
||
514 | + if (!rcpm->rcpm_reg_base) |
||
515 | + return -ENOMEM; |
||
516 | + |
||
517 | + register_pm_notifier(&rcpm_suspend_notifier); |
||
518 | + |
||
519 | + pr_info("The RCPM driver initialized.\n"); |
||
520 | + |
||
521 | + return 0; |
||
522 | +} |
||
523 | + |
||
524 | +subsys_initcall(layerscape_rcpm_init); |