clk-device.c (5224B)
1// SPDX-License-Identifier: GPL-2.0-only 2 3#include <linux/clk.h> 4#include <linux/clk-provider.h> 5#include <linux/mutex.h> 6#include <linux/of_device.h> 7#include <linux/platform_device.h> 8#include <linux/pm_domain.h> 9#include <linux/pm_opp.h> 10#include <linux/pm_runtime.h> 11#include <linux/slab.h> 12 13#include <soc/tegra/common.h> 14 15#include "clk.h" 16 17/* 18 * This driver manages performance state of the core power domain for the 19 * independent PLLs and system clocks. We created a virtual clock device 20 * for such clocks, see tegra_clk_dev_register(). 21 */ 22 23struct tegra_clk_device { 24 struct notifier_block clk_nb; 25 struct device *dev; 26 struct clk_hw *hw; 27 struct mutex lock; 28}; 29 30static int tegra_clock_set_pd_state(struct tegra_clk_device *clk_dev, 31 unsigned long rate) 32{ 33 struct device *dev = clk_dev->dev; 34 struct dev_pm_opp *opp; 35 unsigned int pstate; 36 37 opp = dev_pm_opp_find_freq_ceil(dev, &rate); 38 if (opp == ERR_PTR(-ERANGE)) { 39 /* 40 * Some clocks may be unused by a particular board and they 41 * may have uninitiated clock rate that is overly high. In 42 * this case clock is expected to be disabled, but still we 43 * need to set up performance state of the power domain and 44 * not error out clk initialization. A typical example is 45 * a PCIe clock on Android tablets. 46 */ 47 dev_dbg(dev, "failed to find ceil OPP for %luHz\n", rate); 48 opp = dev_pm_opp_find_freq_floor(dev, &rate); 49 } 50 51 if (IS_ERR(opp)) { 52 dev_err(dev, "failed to find OPP for %luHz: %pe\n", rate, opp); 53 return PTR_ERR(opp); 54 } 55 56 pstate = dev_pm_opp_get_required_pstate(opp, 0); 57 dev_pm_opp_put(opp); 58 59 return dev_pm_genpd_set_performance_state(dev, pstate); 60} 61 62static int tegra_clock_change_notify(struct notifier_block *nb, 63 unsigned long msg, void *data) 64{ 65 struct clk_notifier_data *cnd = data; 66 struct tegra_clk_device *clk_dev; 67 int err = 0; 68 69 clk_dev = container_of(nb, struct tegra_clk_device, clk_nb); 70 71 mutex_lock(&clk_dev->lock); 72 switch (msg) { 73 case PRE_RATE_CHANGE: 74 if (cnd->new_rate > cnd->old_rate) 75 err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate); 76 break; 77 78 case ABORT_RATE_CHANGE: 79 err = tegra_clock_set_pd_state(clk_dev, cnd->old_rate); 80 break; 81 82 case POST_RATE_CHANGE: 83 if (cnd->new_rate < cnd->old_rate) 84 err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate); 85 break; 86 87 default: 88 break; 89 } 90 mutex_unlock(&clk_dev->lock); 91 92 return notifier_from_errno(err); 93} 94 95static int tegra_clock_sync_pd_state(struct tegra_clk_device *clk_dev) 96{ 97 unsigned long rate; 98 int ret; 99 100 mutex_lock(&clk_dev->lock); 101 102 rate = clk_hw_get_rate(clk_dev->hw); 103 ret = tegra_clock_set_pd_state(clk_dev, rate); 104 105 mutex_unlock(&clk_dev->lock); 106 107 return ret; 108} 109 110static int tegra_clock_probe(struct platform_device *pdev) 111{ 112 struct tegra_core_opp_params opp_params = {}; 113 struct tegra_clk_device *clk_dev; 114 struct device *dev = &pdev->dev; 115 struct clk *clk; 116 int err; 117 118 if (!dev->pm_domain) 119 return -EINVAL; 120 121 clk_dev = devm_kzalloc(dev, sizeof(*clk_dev), GFP_KERNEL); 122 if (!clk_dev) 123 return -ENOMEM; 124 125 clk = devm_clk_get(dev, NULL); 126 if (IS_ERR(clk)) 127 return PTR_ERR(clk); 128 129 clk_dev->dev = dev; 130 clk_dev->hw = __clk_get_hw(clk); 131 clk_dev->clk_nb.notifier_call = tegra_clock_change_notify; 132 mutex_init(&clk_dev->lock); 133 134 platform_set_drvdata(pdev, clk_dev); 135 136 /* 137 * Runtime PM was already enabled for this device by the parent clk 138 * driver and power domain state should be synced under clk_dev lock, 139 * hence we don't use the common OPP helper that initializes OPP 140 * state. For some clocks common OPP helper may fail to find ceil 141 * rate, it's handled by this driver. 142 */ 143 err = devm_tegra_core_dev_init_opp_table(dev, &opp_params); 144 if (err) 145 return err; 146 147 err = clk_notifier_register(clk, &clk_dev->clk_nb); 148 if (err) { 149 dev_err(dev, "failed to register clk notifier: %d\n", err); 150 return err; 151 } 152 153 /* 154 * The driver is attaching to a potentially active/resumed clock, hence 155 * we need to sync the power domain performance state in a accordance to 156 * the clock rate if clock is resumed. 157 */ 158 err = tegra_clock_sync_pd_state(clk_dev); 159 if (err) 160 goto unreg_clk; 161 162 return 0; 163 164unreg_clk: 165 clk_notifier_unregister(clk, &clk_dev->clk_nb); 166 167 return err; 168} 169 170/* 171 * Tegra GENPD driver enables clocks during NOIRQ phase. It can't be done 172 * for clocks served by this driver because runtime PM is unavailable in 173 * NOIRQ phase. We will keep clocks resumed during suspend to mitigate this 174 * problem. In practice this makes no difference from a power management 175 * perspective since voltage is kept at a nominal level during suspend anyways. 176 */ 177static const struct dev_pm_ops tegra_clock_pm = { 178 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_resume_and_get, pm_runtime_put) 179}; 180 181static const struct of_device_id tegra_clock_match[] = { 182 { .compatible = "nvidia,tegra20-sclk" }, 183 { .compatible = "nvidia,tegra30-sclk" }, 184 { .compatible = "nvidia,tegra30-pllc" }, 185 { .compatible = "nvidia,tegra30-plle" }, 186 { .compatible = "nvidia,tegra30-pllm" }, 187 { } 188}; 189 190static struct platform_driver tegra_clock_driver = { 191 .driver = { 192 .name = "tegra-clock", 193 .of_match_table = tegra_clock_match, 194 .pm = &tegra_clock_pm, 195 .suppress_bind_attrs = true, 196 }, 197 .probe = tegra_clock_probe, 198}; 199builtin_platform_driver(tegra_clock_driver);