1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2014 NVIDIA CORPORATION.  All rights reserved.
4 */
5
6#define dev_fmt(fmt)	"tegra-soc: " fmt
7
8#include <linux/clk.h>
9#include <linux/device.h>
10#include <linux/export.h>
11#include <linux/of.h>
12#include <linux/pm_opp.h>
13#include <linux/pm_runtime.h>
14
15#include <soc/tegra/common.h>
16#include <soc/tegra/fuse.h>
17
18static const struct of_device_id tegra_machine_match[] = {
19	{ .compatible = "nvidia,tegra20", },
20	{ .compatible = "nvidia,tegra30", },
21	{ .compatible = "nvidia,tegra114", },
22	{ .compatible = "nvidia,tegra124", },
23	{ .compatible = "nvidia,tegra132", },
24	{ .compatible = "nvidia,tegra210", },
25	{ }
26};
27
28bool soc_is_tegra(void)
29{
30	const struct of_device_id *match;
31	struct device_node *root;
32
33	root = of_find_node_by_path("/");
34	if (!root)
35		return false;
36
37	match = of_match_node(tegra_machine_match, root);
38	of_node_put(root);
39
40	return match != NULL;
41}
42
43static int tegra_core_dev_init_opp_state(struct device *dev)
44{
45	unsigned long rate;
46	struct clk *clk;
47	bool rpm_enabled;
48	int err;
49
50	clk = devm_clk_get(dev, NULL);
51	if (IS_ERR(clk)) {
52		dev_err(dev, "failed to get clk: %pe\n", clk);
53		return PTR_ERR(clk);
54	}
55
56	rate = clk_get_rate(clk);
57	if (!rate) {
58		dev_err(dev, "failed to get clk rate\n");
59		return -EINVAL;
60	}
61
62	/*
63	 * Runtime PM of the device must be enabled in order to set up
64	 * GENPD's performance properly because GENPD core checks whether
65	 * device is suspended and this check doesn't work while RPM is
66	 * disabled. This makes sure the OPP vote below gets cached in
67	 * GENPD for the device. Instead, the vote is done the next time
68	 * the device gets runtime resumed.
69	 */
70	rpm_enabled = pm_runtime_enabled(dev);
71	if (!rpm_enabled)
72		pm_runtime_enable(dev);
73
74	/* should never happen in practice */
75	if (!pm_runtime_enabled(dev)) {
76		dev_WARN(dev, "failed to enable runtime PM\n");
77		pm_runtime_disable(dev);
78		return -EINVAL;
79	}
80
81	/* first dummy rate-setting initializes voltage vote */
82	err = dev_pm_opp_set_rate(dev, rate);
83
84	if (!rpm_enabled)
85		pm_runtime_disable(dev);
86
87	if (err) {
88		dev_err(dev, "failed to initialize OPP clock: %d\n", err);
89		return err;
90	}
91
92	return 0;
93}
94
95/**
96 * devm_tegra_core_dev_init_opp_table() - initialize OPP table
97 * @dev: device for which OPP table is initialized
98 * @params: pointer to the OPP table configuration
99 *
100 * This function will initialize OPP table and sync OPP state of a Tegra SoC
101 * core device.
102 *
103 * Return: 0 on success or errorno.
104 */
105int devm_tegra_core_dev_init_opp_table(struct device *dev,
106				       struct tegra_core_opp_params *params)
107{
108	u32 hw_version;
109	int err;
110	/*
111	 * The clk's connection id to set is NULL and this is a NULL terminated
112	 * array, hence two NULL entries.
113	 */
114	const char *clk_names[] = { NULL, NULL };
115	struct dev_pm_opp_config config = {
116		/*
117		 * For some devices we don't have any OPP table in the DT, and
118		 * in order to use the same code path for all the devices, we
119		 * create a dummy OPP table for them via this. The dummy OPP
120		 * table is only capable of doing clk_set_rate() on invocation
121		 * of dev_pm_opp_set_rate() and doesn't provide any other
122		 * functionality.
123		 */
124		.clk_names = clk_names,
125	};
126
127	if (of_machine_is_compatible("nvidia,tegra20")) {
128		hw_version = BIT(tegra_sku_info.soc_process_id);
129		config.supported_hw = &hw_version;
130		config.supported_hw_count = 1;
131	} else if (of_machine_is_compatible("nvidia,tegra30")) {
132		hw_version = BIT(tegra_sku_info.soc_speedo_id);
133		config.supported_hw = &hw_version;
134		config.supported_hw_count = 1;
135	}
136
137	err = devm_pm_opp_set_config(dev, &config);
138	if (err) {
139		dev_err(dev, "failed to set OPP config: %d\n", err);
140		return err;
141	}
142
143	/*
144	 * Tegra114+ doesn't support OPP yet, return early for non tegra20/30
145	 * case.
146	 */
147	if (!config.supported_hw)
148		return -ENODEV;
149
150	/*
151	 * Older device-trees have an empty OPP table, we will get
152	 * -ENODEV from devm_pm_opp_of_add_table() in this case.
153	 */
154	err = devm_pm_opp_of_add_table(dev);
155	if (err) {
156		if (err != -ENODEV)
157			dev_err(dev, "failed to add OPP table: %d\n", err);
158
159		return err;
160	}
161
162	if (params->init_state) {
163		err = tegra_core_dev_init_opp_state(dev);
164		if (err)
165			return err;
166	}
167
168	return 0;
169}
170EXPORT_SYMBOL_GPL(devm_tegra_core_dev_init_opp_table);
171