kernel: bump 5.4 to 5.4.117
[openwrt/staging/dedeckeh.git] / target / linux / ipq806x / patches-5.4 / 098-1-cpufreq-add-Krait-dedicated-scaling-driver.patch
1 From cc41a266280cad0b55319e614167c88dff344248 Mon Sep 17 00:00:00 2001
2 From: Ansuel Smith <ansuelsmth@gmail.com>
3 Date: Sat, 22 Feb 2020 16:33:10 +0100
4 Subject: [PATCH 1/8] cpufreq: add Krait dedicated scaling driver
5
6 This new driver is based on generic cpufreq-dt driver.
7 Krait SoCs have 2-4 cpu and one shared L2 cache that can
8 operate at different frequency based on the maximum cpu clk
9 across all core.
10 L2 frequency and voltage are scaled on every frequency change
11 if needed. On Krait SoCs is present a bug that can cause
12 transition problem between frequency bin, to workaround this
13 on more than one transition, the L2 frequency is first set to the
14 base rate and then to the target rate.
15 The L2 frequency use the OPP framework and use the opp-level
16 bindings to link the l2 freq to different cpu freq. This is needed
17 as the Krait l2 clk are note mapped 1:1 to the core clks and some
18 of the l2 clk is set based on a range of the cpu clks. If the driver
19 find a broken config (for example no opp-level set) the l2 scaling is
20 skipped.
21
22 Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
23 ---
24 drivers/cpufreq/Kconfig.arm | 14 +-
25 drivers/cpufreq/Makefile | 2 +
26 drivers/cpufreq/qcom-cpufreq-krait.c | 589 +++++++++++++++++++++++++++
27 3 files changed, 604 insertions(+), 1 deletion(-)
28 create mode 100644 drivers/cpufreq/qcom-cpufreq-krait.c
29
30 --- a/drivers/cpufreq/Kconfig.arm
31 +++ b/drivers/cpufreq/Kconfig.arm
32 @@ -155,6 +155,18 @@ config ARM_QCOM_CPUFREQ_HW
33 The driver implements the cpufreq interface for this HW engine.
34 Say Y if you want to support CPUFreq HW.
35
36 +config ARM_QCOM_CPUFREQ_KRAIT
37 + tristate "CPU Frequency scaling support for Krait SoCs"
38 + depends on ARCH_QCOM || COMPILE_TEST
39 + select PM_OPP
40 + select ARM_QCOM_CPUFREQ_NVMEM
41 + help
42 + This adds the CPUFreq driver for Qualcomm Krait SoC based boards.
43 + This scale the cache clk and regulator based on the different cpu
44 + clks when scaling the different cores clk.
45 +
46 + If in doubt, say N.
47 +
48 config ARM_RASPBERRYPI_CPUFREQ
49 tristate "Raspberry Pi cpufreq support"
50 depends on CLK_RASPBERRYPI || COMPILE_TEST
51 @@ -338,4 +350,4 @@ config ARM_PXA2xx_CPUFREQ
52 help
53 This add the CPUFreq driver support for Intel PXA2xx SOCs.
54
55 - If in doubt, say N.
56 + If in doubt, say N.
57 \ No newline at end of file
58 --- a/drivers/cpufreq/Makefile
59 +++ b/drivers/cpufreq/Makefile
60 @@ -65,6 +65,7 @@ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2
61 obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
62 obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o
63 obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o
64 +obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRAIT) += qcom-cpufreq-krait.o
65 obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o
66 obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
67 obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
68 @@ -87,6 +88,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += te
69 obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o
70 obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o
71 obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
72 +obj-$(CONFIG_ARM_KRAIT_CPUFREQ) += krait-cpufreq.o
73
74
75 ##################################################################################
76 --- /dev/null
77 +++ b/drivers/cpufreq/qcom-cpufreq-krait.c
78 @@ -0,0 +1,601 @@
79 +// SPDX-License-Identifier: GPL-2.0
80 +
81 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
82 +
83 +#include <linux/clk.h>
84 +#include <linux/cpu.h>
85 +#include <linux/cpufreq.h>
86 +#include <linux/cpumask.h>
87 +#include <linux/err.h>
88 +#include <linux/module.h>
89 +#include <linux/of.h>
90 +#include <linux/of_device.h>
91 +#include <linux/pm_opp.h>
92 +#include <linux/platform_device.h>
93 +#include <linux/regulator/consumer.h>
94 +#include <linux/slab.h>
95 +#include <linux/thermal.h>
96 +
97 +#include "cpufreq-dt.h"
98 +
99 +static struct platform_device *l2_pdev;
100 +
101 +struct private_data {
102 + struct opp_table *opp_table;
103 + struct device *cpu_dev;
104 + const char *reg_name;
105 + bool have_static_opps;
106 +};
107 +
108 +static int set_target(struct cpufreq_policy *policy, unsigned int index)
109 +{
110 + struct private_data *priv = policy->driver_data;
111 + unsigned long freq = policy->freq_table[index].frequency;
112 + unsigned long target_freq = freq * 1000;
113 + struct dev_pm_opp *opp;
114 + unsigned int level;
115 + int cpu, ret;
116 +
117 + if (l2_pdev) {
118 + /* find the max freq across all core */
119 + for_each_present_cpu(cpu)
120 + if (cpu != index)
121 + target_freq = max(
122 + target_freq,
123 + (unsigned long)cpufreq_quick_get(cpu));
124 +
125 + opp = dev_pm_opp_find_freq_exact(priv->cpu_dev, target_freq,
126 + true);
127 + if (IS_ERR(opp)) {
128 + dev_err(&l2_pdev->dev, "failed to find OPP for %ld\n",
129 + target_freq);
130 + return PTR_ERR(opp);
131 + }
132 + level = dev_pm_opp_get_level(opp);
133 + dev_pm_opp_put(opp);
134 +
135 + opp = dev_pm_opp_find_level_exact(&l2_pdev->dev, level);
136 + if (IS_ERR(opp)) {
137 + dev_err(&l2_pdev->dev,
138 + "failed to find level OPP for %d\n", level);
139 + return PTR_ERR(opp);
140 + }
141 + target_freq = dev_pm_opp_get_freq(opp);
142 + dev_pm_opp_put(opp);
143 +
144 + ret = dev_pm_opp_set_rate(&l2_pdev->dev, target_freq);
145 + if (ret)
146 + return ret;
147 +
148 + /*
149 + * Hardware constraint:
150 + * Krait CPU cannot operate at 384MHz with L2 at 1Ghz.
151 + * Assume index 0 with the idle freq and level > 0 as
152 + * any L2 freq > 384MHz.
153 + * Skip CPU freq change in this corner case.
154 + */
155 + if (unlikely(index == 0 && level != 0)) {
156 + dev_err(priv->cpu_dev, "Krait CPU can't operate at idle freq with L2 at 1GHz");
157 + return -EINVAL;
158 + }
159 + }
160 +
161 + ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
162 + if (ret)
163 + return ret;
164 +
165 + arch_set_freq_scale(policy->related_cpus, freq,
166 + policy->cpuinfo.max_freq);
167 +
168 + return 0;
169 +}
170 +
171 +/*
172 + * An earlier version of opp-v1 bindings used to name the regulator
173 + * "cpu0-supply", we still need to handle that for backwards compatibility.
174 + */
175 +static const char *find_supply_name(struct device *dev)
176 +{
177 + struct device_node *np;
178 + struct property *pp;
179 + int cpu = dev->id;
180 + const char *name = NULL;
181 +
182 + np = of_node_get(dev->of_node);
183 +
184 + /* This must be valid for sure */
185 + if (WARN_ON(!np))
186 + return NULL;
187 +
188 + /* Try "cpu0" for older DTs */
189 + if (!cpu) {
190 + pp = of_find_property(np, "cpu0-supply", NULL);
191 + if (pp) {
192 + name = "cpu0";
193 + goto node_put;
194 + }
195 + }
196 +
197 + pp = of_find_property(np, "cpu-supply", NULL);
198 + if (pp) {
199 + name = "cpu";
200 + goto node_put;
201 + }
202 +
203 + dev_dbg(dev, "no regulator for cpu%d\n", cpu);
204 +node_put:
205 + of_node_put(np);
206 + return name;
207 +}
208 +
209 +static int resources_available(void)
210 +{
211 + struct device *cpu_dev;
212 + struct regulator *cpu_reg;
213 + struct clk *cpu_clk;
214 + int ret = 0;
215 + const char *name;
216 +
217 + cpu_dev = get_cpu_device(0);
218 + if (!cpu_dev) {
219 + pr_err("failed to get cpu0 device\n");
220 + return -ENODEV;
221 + }
222 +
223 + cpu_clk = clk_get(cpu_dev, NULL);
224 + ret = PTR_ERR_OR_ZERO(cpu_clk);
225 + if (ret) {
226 + /*
227 + * If cpu's clk node is present, but clock is not yet
228 + * registered, we should try defering probe.
229 + */
230 + if (ret == -EPROBE_DEFER)
231 + dev_dbg(cpu_dev, "clock not ready, retry\n");
232 + else
233 + dev_err(cpu_dev, "failed to get clock: %d\n", ret);
234 +
235 + return ret;
236 + }
237 +
238 + clk_put(cpu_clk);
239 +
240 + name = find_supply_name(cpu_dev);
241 + /* Platform doesn't require regulator */
242 + if (!name)
243 + return 0;
244 +
245 + cpu_reg = regulator_get_optional(cpu_dev, name);
246 + ret = PTR_ERR_OR_ZERO(cpu_reg);
247 + if (ret) {
248 + /*
249 + * If cpu's regulator supply node is present, but regulator is
250 + * not yet registered, we should try defering probe.
251 + */
252 + if (ret == -EPROBE_DEFER)
253 + dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n");
254 + else
255 + dev_dbg(cpu_dev, "no regulator for cpu0: %d\n", ret);
256 +
257 + return ret;
258 + }
259 +
260 + regulator_put(cpu_reg);
261 + return 0;
262 +}
263 +
264 +static int cpufreq_init(struct cpufreq_policy *policy)
265 +{
266 + struct cpufreq_frequency_table *freq_table;
267 + struct opp_table *opp_table = NULL;
268 + unsigned int transition_latency;
269 + struct private_data *priv;
270 + struct device *cpu_dev;
271 + bool fallback = false;
272 + struct clk *cpu_clk;
273 + const char *name;
274 + int ret;
275 +
276 + cpu_dev = get_cpu_device(policy->cpu);
277 + if (!cpu_dev) {
278 + pr_err("failed to get cpu%d device\n", policy->cpu);
279 + return -ENODEV;
280 + }
281 +
282 + cpu_clk = clk_get(cpu_dev, NULL);
283 + if (IS_ERR(cpu_clk)) {
284 + ret = PTR_ERR(cpu_clk);
285 + dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret);
286 + return ret;
287 + }
288 +
289 + /* Get OPP-sharing information from "operating-points-v2" bindings */
290 + ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus);
291 + if (ret) {
292 + if (ret != -ENOENT)
293 + goto out_put_clk;
294 +
295 + /*
296 + * operating-points-v2 not supported, fallback to old method of
297 + * finding shared-OPPs for backward compatibility if the
298 + * platform hasn't set sharing CPUs.
299 + */
300 + if (dev_pm_opp_get_sharing_cpus(cpu_dev, policy->cpus))
301 + fallback = true;
302 + }
303 +
304 + /*
305 + * OPP layer will be taking care of regulators now, but it needs to know
306 + * the name of the regulator first.
307 + */
308 + name = find_supply_name(cpu_dev);
309 + if (name) {
310 + opp_table = dev_pm_opp_set_regulators(cpu_dev, &name, 1);
311 + if (IS_ERR(opp_table)) {
312 + ret = PTR_ERR(opp_table);
313 + dev_err(cpu_dev,
314 + "Failed to set regulator for cpu%d: %d\n",
315 + policy->cpu, ret);
316 + goto out_put_clk;
317 + }
318 + }
319 +
320 + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
321 + if (!priv) {
322 + ret = -ENOMEM;
323 + goto out_put_regulator;
324 + }
325 +
326 + priv->reg_name = name;
327 + priv->opp_table = opp_table;
328 +
329 + /*
330 + * Initialize OPP tables for all policy->cpus. They will be shared by
331 + * all CPUs which have marked their CPUs shared with OPP bindings.
332 + *
333 + * For platforms not using operating-points-v2 bindings, we do this
334 + * before updating policy->cpus. Otherwise, we will end up creating
335 + * duplicate OPPs for policy->cpus.
336 + *
337 + * OPPs might be populated at runtime, don't check for error here
338 + */
339 + if (!dev_pm_opp_of_cpumask_add_table(policy->cpus))
340 + priv->have_static_opps = true;
341 +
342 + /*
343 + * But we need OPP table to function so if it is not there let's
344 + * give platform code chance to provide it for us.
345 + */
346 + ret = dev_pm_opp_get_opp_count(cpu_dev);
347 + if (ret <= 0) {
348 + dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
349 + ret = -EPROBE_DEFER;
350 + goto out_free_opp;
351 + }
352 +
353 + if (fallback) {
354 + cpumask_setall(policy->cpus);
355 +
356 + /*
357 + * OPP tables are initialized only for policy->cpu, do it for
358 + * others as well.
359 + */
360 + ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
361 + if (ret)
362 + dev_err(cpu_dev,
363 + "%s: failed to mark OPPs as shared: %d\n",
364 + __func__, ret);
365 + }
366 +
367 + ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
368 + if (ret) {
369 + dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
370 + goto out_free_opp;
371 + }
372 +
373 + priv->cpu_dev = cpu_dev;
374 +
375 + policy->driver_data = priv;
376 + policy->clk = cpu_clk;
377 + policy->freq_table = freq_table;
378 +
379 + policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
380 +
381 + transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
382 + if (!transition_latency)
383 + transition_latency = CPUFREQ_ETERNAL;
384 +
385 + policy->cpuinfo.transition_latency = transition_latency;
386 + policy->dvfs_possible_from_any_cpu = true;
387 +
388 + dev_pm_opp_of_register_em(policy->cpus);
389 +
390 + return 0;
391 +
392 +out_free_opp:
393 + if (priv->have_static_opps)
394 + dev_pm_opp_of_cpumask_remove_table(policy->cpus);
395 + kfree(priv);
396 +out_put_regulator:
397 + if (name)
398 + dev_pm_opp_put_regulators(opp_table);
399 +out_put_clk:
400 + clk_put(cpu_clk);
401 +
402 + return ret;
403 +}
404 +
405 +static int cpufreq_online(struct cpufreq_policy *policy)
406 +{
407 + /* We did light-weight tear down earlier, nothing to do here */
408 + return 0;
409 +}
410 +
411 +static int cpufreq_offline(struct cpufreq_policy *policy)
412 +{
413 + /*
414 + * Preserve policy->driver_data and don't free resources on light-weight
415 + * tear down.
416 + */
417 + return 0;
418 +}
419 +
420 +static int cpufreq_exit(struct cpufreq_policy *policy)
421 +{
422 + struct private_data *priv = policy->driver_data;
423 +
424 + dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
425 + if (priv->have_static_opps)
426 + dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
427 + if (priv->reg_name)
428 + dev_pm_opp_put_regulators(priv->opp_table);
429 +
430 + clk_put(policy->clk);
431 + kfree(priv);
432 +
433 + return 0;
434 +}
435 +
436 +static struct cpufreq_driver krait_cpufreq_driver = {
437 + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
438 + CPUFREQ_IS_COOLING_DEV,
439 + .verify = cpufreq_generic_frequency_table_verify,
440 + .target_index = set_target,
441 + .get = cpufreq_generic_get,
442 + .init = cpufreq_init,
443 + .exit = cpufreq_exit,
444 + .online = cpufreq_online,
445 + .offline = cpufreq_offline,
446 + .name = "krait-cpufreq",
447 + .suspend = cpufreq_generic_suspend,
448 +};
449 +
450 +struct krait_data {
451 + unsigned long idle_freq;
452 + bool regulator_enabled;
453 +};
454 +
455 +static int krait_cache_set_opp(struct dev_pm_set_opp_data *data)
456 +{
457 + unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate;
458 + struct dev_pm_opp_supply *supply = &data->new_opp.supplies[0];
459 + struct regulator *reg = data->regulators[0];
460 + struct clk *clk = data->clk;
461 + struct krait_data *kdata;
462 + unsigned long idle_freq;
463 + int ret;
464 +
465 + kdata = (struct krait_data *)dev_get_drvdata(data->dev);
466 + idle_freq = kdata->idle_freq;
467 +
468 + /* Scaling up? Scale voltage before frequency */
469 + if (freq >= old_freq) {
470 + ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,
471 + supply->u_volt,
472 + supply->u_volt_max);
473 + if (ret)
474 + goto exit;
475 + }
476 +
477 + /*
478 + * Set to idle bin if switching from normal to high bin
479 + * or vice versa. It has been notice that a bug is triggered
480 + * in cache scaling when more than one bin is scaled, to fix
481 + * this we first need to transition to the base rate and then
482 + * to target rate
483 + */
484 + if (likely(freq != idle_freq && old_freq != idle_freq)) {
485 + ret = clk_set_rate(clk, idle_freq);
486 + if (ret)
487 + goto exit;
488 + }
489 +
490 + ret = clk_set_rate(clk, freq);
491 + if (ret)
492 + goto exit;
493 +
494 + /* Scaling down? Scale voltage after frequency */
495 + if (freq < old_freq) {
496 + ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,
497 + supply->u_volt,
498 + supply->u_volt_max);
499 + }
500 +
501 + if (unlikely(!kdata->regulator_enabled)) {
502 + ret = regulator_enable(reg);
503 + if (ret < 0)
504 + dev_warn(data->dev, "Failed to enable regulator: %d", ret);
505 + else
506 + kdata->regulator_enabled = true;
507 + }
508 +
509 +exit:
510 + return ret;
511 +};
512 +
513 +static int krait_cache_probe(struct platform_device *pdev)
514 +{
515 + struct device *dev = &pdev->dev;
516 + struct krait_data *data;
517 + struct opp_table *table;
518 + struct dev_pm_opp *opp;
519 + struct device *cpu_dev;
520 + int ret;
521 +
522 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
523 + if (!data)
524 + return -ENOMEM;
525 +
526 + table = dev_pm_opp_set_regulators(dev, (const char *[]){ "l2" }, 1);
527 + if (IS_ERR(table)) {
528 + ret = PTR_ERR(table);
529 + if (ret != -EPROBE_DEFER)
530 + dev_err(dev, "failed to set regulators %d\n", ret);
531 +
532 + return ret;
533 + }
534 +
535 + ret = PTR_ERR_OR_ZERO(
536 + dev_pm_opp_register_set_opp_helper(dev, krait_cache_set_opp));
537 + if (ret)
538 + return ret;
539 +
540 + ret = dev_pm_opp_of_add_table(dev);
541 + if (ret) {
542 + dev_err(dev, "failed to parse L2 freq thresholds\n");
543 + return ret;
544 + }
545 +
546 + opp = dev_pm_opp_find_freq_ceil(dev, &data->idle_freq);
547 + dev_pm_opp_put(opp);
548 +
549 + /*
550 + * Check opp-level configuration
551 + * At least 2 level must be set or the cache will always be scaled
552 + * the idle freq causing some performance problem
553 + *
554 + * In case of invalid configuration, the l2 scaling is skipped
555 + */
556 + cpu_dev = get_cpu_device(0);
557 + if (!cpu_dev) {
558 + pr_err("failed to get cpu0 device\n");
559 + return -ENODEV;
560 + }
561 +
562 + /*
563 + * Check if we have at least opp-level 1, 0 should always be set to
564 + * the idle freq
565 + */
566 + opp = dev_pm_opp_find_level_exact(dev, 1);
567 + if (IS_ERR(opp)) {
568 + dev_err(dev,
569 + "Invalid configuration found of l2 opp. Can't find opp-level 1");
570 + goto invalid_conf;
571 + }
572 + dev_pm_opp_put(opp);
573 +
574 + /*
575 + * Check if we have at least opp-level 1 in the cpu opp, 0 should always
576 + * be set to the idle freq
577 + */
578 + opp = dev_pm_opp_find_level_exact(cpu_dev, 1);
579 + if (IS_ERR(opp)) {
580 + dev_err(dev,
581 + "Invalid configuration found of cpu opp. Can't find opp-level 1");
582 + goto invalid_conf;
583 + }
584 + dev_pm_opp_put(opp);
585 +
586 + platform_set_drvdata(pdev, data);
587 +
588 + /* The l2 scaling is enabled by linking the cpufreq driver */
589 + l2_pdev = pdev;
590 +
591 + return 0;
592 +
593 +invalid_conf:
594 + dev_pm_opp_remove_table(dev);
595 + dev_pm_opp_put_regulators(table);
596 + dev_pm_opp_unregister_set_opp_helper(table);
597 +
598 + return -EINVAL;
599 +};
600 +
601 +static int krait_cache_remove(struct platform_device *pdev)
602 +{
603 + struct device *dev = &pdev->dev;
604 + struct opp_table *table = dev_pm_opp_get_opp_table(dev);
605 +
606 + dev_pm_opp_remove_table(dev);
607 + dev_pm_opp_put_regulators(table);
608 + dev_pm_opp_unregister_set_opp_helper(table);
609 +
610 + return 0;
611 +};
612 +
613 +static const struct of_device_id krait_cache_match_table[] = {
614 + { .compatible = "qcom,krait-cache" },
615 + {}
616 +};
617 +
618 +static struct platform_driver krait_cache_driver = {
619 + .driver = {
620 + .name = "krait-cache",
621 + .of_match_table = krait_cache_match_table,
622 + },
623 + .probe = krait_cache_probe,
624 + .remove = krait_cache_remove,
625 +};
626 +module_platform_driver(krait_cache_driver);
627 +
628 +static int krait_cpufreq_probe(struct platform_device *pdev)
629 +{
630 + struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev);
631 + int ret;
632 +
633 + /*
634 + * All per-cluster (CPUs sharing clock/voltages) initialization is done
635 + * from ->init(). In probe(), we just need to make sure that clk and
636 + * regulators are available. Else defer probe and retry.
637 + *
638 + * FIXME: Is checking this only for CPU0 sufficient ?
639 + */
640 + ret = resources_available();
641 + if (ret)
642 + return ret;
643 +
644 + if (data) {
645 + if (data->have_governor_per_policy)
646 + krait_cpufreq_driver.flags |=
647 + CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
648 +
649 + krait_cpufreq_driver.resume = data->resume;
650 + if (data->suspend)
651 + krait_cpufreq_driver.suspend = data->suspend;
652 + }
653 +
654 + ret = cpufreq_register_driver(&krait_cpufreq_driver);
655 + if (ret)
656 + dev_err(&pdev->dev, "failed register driver: %d\n", ret);
657 +
658 + return ret;
659 +}
660 +
661 +static int krait_cpufreq_remove(struct platform_device *pdev)
662 +{
663 + cpufreq_unregister_driver(&krait_cpufreq_driver);
664 + return 0;
665 +}
666 +
667 +static struct platform_driver krait_cpufreq_platdrv = {
668 + .driver = {
669 + .name = "krait-cpufreq",
670 + },
671 + .probe = krait_cpufreq_probe,
672 + .remove = krait_cpufreq_remove,
673 +};
674 +module_platform_driver(krait_cpufreq_platdrv);
675 +
676 +MODULE_ALIAS("platform:krait-cpufreq");
677 +MODULE_AUTHOR("Ansuel Smith <ansuelsmth@gmail.com>");
678 +MODULE_DESCRIPTION("Dedicated Krait SoC cpufreq driver");
679 +MODULE_LICENSE("GPL");