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 14 #include <soc/tegra/common.h> 15 #include <soc/tegra/fuse.h> 16 17 static const struct of_device_id tegra_machine_match[] = { 18 { .compatible = "nvidia,tegra20", }, 19 { .compatible = "nvidia,tegra30", }, 20 { .compatible = "nvidia,tegra114", }, 21 { .compatible = "nvidia,tegra124", }, 22 { .compatible = "nvidia,tegra132", }, 23 { .compatible = "nvidia,tegra210", }, 24 { } 25 }; 26 27 bool soc_is_tegra(void) 28 { 29 const struct of_device_id *match; 30 struct device_node *root; 31 32 root = of_find_node_by_path("/"); 33 if (!root) 34 return false; 35 36 match = of_match_node(tegra_machine_match, root); 37 of_node_put(root); 38 39 return match != NULL; 40 } 41 42 static int tegra_core_dev_init_opp_state(struct device *dev) 43 { 44 unsigned long rate; 45 struct clk *clk; 46 int err; 47 48 clk = devm_clk_get(dev, NULL); 49 if (IS_ERR(clk)) { 50 dev_err(dev, "failed to get clk: %pe\n", clk); 51 return PTR_ERR(clk); 52 } 53 54 rate = clk_get_rate(clk); 55 if (!rate) { 56 dev_err(dev, "failed to get clk rate\n"); 57 return -EINVAL; 58 } 59 60 /* first dummy rate-setting initializes voltage vote */ 61 err = dev_pm_opp_set_rate(dev, rate); 62 if (err) { 63 dev_err(dev, "failed to initialize OPP clock: %d\n", err); 64 return err; 65 } 66 67 return 0; 68 } 69 70 /** 71 * devm_tegra_core_dev_init_opp_table() - initialize OPP table 72 * @dev: device for which OPP table is initialized 73 * @params: pointer to the OPP table configuration 74 * 75 * This function will initialize OPP table and sync OPP state of a Tegra SoC 76 * core device. 77 * 78 * Return: 0 on success or errorno. 79 */ 80 int devm_tegra_core_dev_init_opp_table(struct device *dev, 81 struct tegra_core_opp_params *params) 82 { 83 u32 hw_version; 84 int err; 85 86 err = devm_pm_opp_set_clkname(dev, NULL); 87 if (err) { 88 dev_err(dev, "failed to set OPP clk: %d\n", err); 89 return err; 90 } 91 92 /* Tegra114+ doesn't support OPP yet */ 93 if (!of_machine_is_compatible("nvidia,tegra20") && 94 !of_machine_is_compatible("nvidia,tegra30")) 95 return -ENODEV; 96 97 if (of_machine_is_compatible("nvidia,tegra20")) 98 hw_version = BIT(tegra_sku_info.soc_process_id); 99 else 100 hw_version = BIT(tegra_sku_info.soc_speedo_id); 101 102 err = devm_pm_opp_set_supported_hw(dev, &hw_version, 1); 103 if (err) { 104 dev_err(dev, "failed to set OPP supported HW: %d\n", err); 105 return err; 106 } 107 108 /* 109 * Older device-trees have an empty OPP table, we will get 110 * -ENODEV from devm_pm_opp_of_add_table() in this case. 111 */ 112 err = devm_pm_opp_of_add_table(dev); 113 if (err) { 114 if (err == -ENODEV) 115 dev_err_once(dev, "OPP table not found, please update device-tree\n"); 116 else 117 dev_err(dev, "failed to add OPP table: %d\n", err); 118 119 return err; 120 } 121 122 if (params->init_state) { 123 err = tegra_core_dev_init_opp_state(dev); 124 if (err) 125 return err; 126 } 127 128 return 0; 129 } 130 EXPORT_SYMBOL_GPL(devm_tegra_core_dev_init_opp_table); 131