mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-14 18:06:48 +07:00
soc/tegra: pmc: Query PCLK clock rate at probe time
It is possible to get a lockup if kernel decides to enter LP2 cpuidle from some clk-notifier, in that case CCF's "prepare" mutex is kept locked and thus clk_get_rate(pclk) blocks on the same mutex with interrupts being disabled, hanging machine. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Acked-By: Peter De Schrijver <pdeschrijver@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
783807436f
commit
e57a243f5d
@ -324,6 +324,7 @@ static const char * const tegra210_reset_sources[] = {
|
|||||||
* @pctl_dev: pin controller exposed by the PMC
|
* @pctl_dev: pin controller exposed by the PMC
|
||||||
* @domain: IRQ domain provided by the PMC
|
* @domain: IRQ domain provided by the PMC
|
||||||
* @irq: chip implementation for the IRQ domain
|
* @irq: chip implementation for the IRQ domain
|
||||||
|
* @clk_nb: pclk clock changes handler
|
||||||
*/
|
*/
|
||||||
struct tegra_pmc {
|
struct tegra_pmc {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
@ -359,6 +360,8 @@ struct tegra_pmc {
|
|||||||
|
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
struct irq_chip irq;
|
struct irq_chip irq;
|
||||||
|
|
||||||
|
struct notifier_block clk_nb;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
|
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
|
||||||
@ -1207,7 +1210,7 @@ static int tegra_io_pad_prepare(struct tegra_pmc *pmc, enum tegra_io_pad id,
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (pmc->clk) {
|
if (pmc->clk) {
|
||||||
rate = clk_get_rate(pmc->clk);
|
rate = pmc->rate;
|
||||||
if (!rate) {
|
if (!rate) {
|
||||||
dev_err(pmc->dev, "failed to get clock rate\n");
|
dev_err(pmc->dev, "failed to get clock rate\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -1448,6 +1451,7 @@ void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
|
|||||||
void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
|
void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
|
||||||
{
|
{
|
||||||
unsigned long long rate = 0;
|
unsigned long long rate = 0;
|
||||||
|
u64 ticks;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
@ -1456,7 +1460,7 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TEGRA_SUSPEND_LP2:
|
case TEGRA_SUSPEND_LP2:
|
||||||
rate = clk_get_rate(pmc->clk);
|
rate = pmc->rate;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1466,21 +1470,15 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode)
|
|||||||
if (WARN_ON_ONCE(rate == 0))
|
if (WARN_ON_ONCE(rate == 0))
|
||||||
rate = 100000000;
|
rate = 100000000;
|
||||||
|
|
||||||
if (rate != pmc->rate) {
|
ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1;
|
||||||
u64 ticks;
|
do_div(ticks, USEC_PER_SEC);
|
||||||
|
tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER);
|
||||||
|
|
||||||
ticks = pmc->cpu_good_time * rate + USEC_PER_SEC - 1;
|
ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1;
|
||||||
do_div(ticks, USEC_PER_SEC);
|
do_div(ticks, USEC_PER_SEC);
|
||||||
tegra_pmc_writel(pmc, ticks, PMC_CPUPWRGOOD_TIMER);
|
tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER);
|
||||||
|
|
||||||
ticks = pmc->cpu_off_time * rate + USEC_PER_SEC - 1;
|
wmb();
|
||||||
do_div(ticks, USEC_PER_SEC);
|
|
||||||
tegra_pmc_writel(pmc, ticks, PMC_CPUPWROFF_TIMER);
|
|
||||||
|
|
||||||
wmb();
|
|
||||||
|
|
||||||
pmc->rate = rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = tegra_pmc_readl(pmc, PMC_CNTRL);
|
value = tegra_pmc_readl(pmc, PMC_CNTRL);
|
||||||
value &= ~PMC_CNTRL_SIDE_EFFECT_LP0;
|
value &= ~PMC_CNTRL_SIDE_EFFECT_LP0;
|
||||||
@ -2140,6 +2138,33 @@ static int tegra_pmc_irq_init(struct tegra_pmc *pmc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tegra_pmc_clk_notify_cb(struct notifier_block *nb,
|
||||||
|
unsigned long action, void *ptr)
|
||||||
|
{
|
||||||
|
struct tegra_pmc *pmc = container_of(nb, struct tegra_pmc, clk_nb);
|
||||||
|
struct clk_notifier_data *data = ptr;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case PRE_RATE_CHANGE:
|
||||||
|
mutex_lock(&pmc->powergates_lock);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case POST_RATE_CHANGE:
|
||||||
|
pmc->rate = data->new_rate;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case ABORT_RATE_CHANGE:
|
||||||
|
mutex_unlock(&pmc->powergates_lock);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return notifier_from_errno(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int tegra_pmc_probe(struct platform_device *pdev)
|
static int tegra_pmc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
@ -2203,6 +2228,23 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||||||
pmc->clk = NULL;
|
pmc->clk = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCLK clock rate can't be retrieved using CLK API because it
|
||||||
|
* causes lockup if CPU enters LP2 idle state from some other
|
||||||
|
* CLK notifier, hence we're caching the rate's value locally.
|
||||||
|
*/
|
||||||
|
if (pmc->clk) {
|
||||||
|
pmc->clk_nb.notifier_call = tegra_pmc_clk_notify_cb;
|
||||||
|
err = clk_notifier_register(pmc->clk, &pmc->clk_nb);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"failed to register clk notifier\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmc->rate = clk_get_rate(pmc->clk);
|
||||||
|
}
|
||||||
|
|
||||||
pmc->dev = &pdev->dev;
|
pmc->dev = &pdev->dev;
|
||||||
|
|
||||||
tegra_pmc_init(pmc);
|
tegra_pmc_init(pmc);
|
||||||
@ -2254,6 +2296,8 @@ static int tegra_pmc_probe(struct platform_device *pdev)
|
|||||||
cleanup_sysfs:
|
cleanup_sysfs:
|
||||||
device_remove_file(&pdev->dev, &dev_attr_reset_reason);
|
device_remove_file(&pdev->dev, &dev_attr_reset_reason);
|
||||||
device_remove_file(&pdev->dev, &dev_attr_reset_level);
|
device_remove_file(&pdev->dev, &dev_attr_reset_level);
|
||||||
|
clk_notifier_unregister(pmc->clk, &pmc->clk_nb);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user