brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · dff6d5e Raw
171 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 NVIDIA CORPORATION.  All rights reserved.4 */5 6#define dev_fmt(fmt)	"tegra-soc: " fmt7 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 up64	 * GENPD's performance properly because GENPD core checks whether65	 * device is suspended and this check doesn't work while RPM is66	 * disabled. This makes sure the OPP vote below gets cached in67	 * GENPD for the device. Instead, the vote is done the next time68	 * 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 table97 * @dev: device for which OPP table is initialized98 * @params: pointer to the OPP table configuration99 *100 * This function will initialize OPP table and sync OPP state of a Tegra SoC101 * 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 terminated112	 * 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, and118		 * in order to use the same code path for all the devices, we119		 * create a dummy OPP table for them via this. The dummy OPP120		 * table is only capable of doing clk_set_rate() on invocation121		 * of dev_pm_opp_set_rate() and doesn't provide any other122		 * 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/30145	 * case.146	 */147	if (!config.supported_hw)148		return -ENODEV;149 150	/*151	 * Older device-trees have an empty OPP table, we will get152	 * -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