1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2010 Google, Inc. |
4 | * |
5 | * Author: |
6 | * Colin Cross <ccross@google.com> |
7 | * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation |
8 | */ |
9 | |
10 | #include <linux/bits.h> |
11 | #include <linux/cpu.h> |
12 | #include <linux/err.h> |
13 | #include <linux/init.h> |
14 | #include <linux/module.h> |
15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/pm_opp.h> |
18 | #include <linux/types.h> |
19 | |
20 | #include <soc/tegra/common.h> |
21 | #include <soc/tegra/fuse.h> |
22 | |
23 | static bool cpu0_node_has_opp_v2_prop(void) |
24 | { |
25 | struct device_node *np = of_cpu_device_node_get(cpu: 0); |
26 | bool ret = false; |
27 | |
28 | if (of_property_present(np, propname: "operating-points-v2" )) |
29 | ret = true; |
30 | |
31 | of_node_put(node: np); |
32 | return ret; |
33 | } |
34 | |
35 | static void tegra20_cpufreq_put_supported_hw(void *opp_token) |
36 | { |
37 | dev_pm_opp_put_supported_hw(token: (unsigned long) opp_token); |
38 | } |
39 | |
40 | static void tegra20_cpufreq_dt_unregister(void *cpufreq_dt) |
41 | { |
42 | platform_device_unregister(cpufreq_dt); |
43 | } |
44 | |
45 | static int tegra20_cpufreq_probe(struct platform_device *pdev) |
46 | { |
47 | struct platform_device *cpufreq_dt; |
48 | struct device *cpu_dev; |
49 | u32 versions[2]; |
50 | int err; |
51 | |
52 | if (!cpu0_node_has_opp_v2_prop()) { |
53 | dev_err(&pdev->dev, "operating points not found\n" ); |
54 | dev_err(&pdev->dev, "please update your device tree\n" ); |
55 | return -ENODEV; |
56 | } |
57 | |
58 | if (of_machine_is_compatible(compat: "nvidia,tegra20" )) { |
59 | versions[0] = BIT(tegra_sku_info.cpu_process_id); |
60 | versions[1] = BIT(tegra_sku_info.soc_speedo_id); |
61 | } else { |
62 | versions[0] = BIT(tegra_sku_info.cpu_process_id); |
63 | versions[1] = BIT(tegra_sku_info.cpu_speedo_id); |
64 | } |
65 | |
66 | dev_info(&pdev->dev, "hardware version 0x%x 0x%x\n" , |
67 | versions[0], versions[1]); |
68 | |
69 | cpu_dev = get_cpu_device(cpu: 0); |
70 | if (WARN_ON(!cpu_dev)) |
71 | return -ENODEV; |
72 | |
73 | err = dev_pm_opp_set_supported_hw(dev: cpu_dev, versions, count: 2); |
74 | if (err < 0) { |
75 | dev_err(&pdev->dev, "failed to set supported hw: %d\n" , err); |
76 | return err; |
77 | } |
78 | |
79 | err = devm_add_action_or_reset(&pdev->dev, |
80 | tegra20_cpufreq_put_supported_hw, |
81 | (void *)((unsigned long) err)); |
82 | if (err) |
83 | return err; |
84 | |
85 | cpufreq_dt = platform_device_register_simple(name: "cpufreq-dt" , id: -1, NULL, num: 0); |
86 | err = PTR_ERR_OR_ZERO(ptr: cpufreq_dt); |
87 | if (err) { |
88 | dev_err(&pdev->dev, |
89 | "failed to create cpufreq-dt device: %d\n" , err); |
90 | return err; |
91 | } |
92 | |
93 | err = devm_add_action_or_reset(&pdev->dev, |
94 | tegra20_cpufreq_dt_unregister, |
95 | cpufreq_dt); |
96 | if (err) |
97 | return err; |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static struct platform_driver tegra20_cpufreq_driver = { |
103 | .probe = tegra20_cpufreq_probe, |
104 | .driver = { |
105 | .name = "tegra20-cpufreq" , |
106 | }, |
107 | }; |
108 | module_platform_driver(tegra20_cpufreq_driver); |
109 | |
110 | MODULE_ALIAS("platform:tegra20-cpufreq" ); |
111 | MODULE_AUTHOR("Colin Cross <ccross@android.com>" ); |
112 | MODULE_DESCRIPTION("NVIDIA Tegra20 cpufreq driver" ); |
113 | MODULE_LICENSE("GPL" ); |
114 | |