diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 400615baea97..c6420b36cbcb 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -1801,6 +1802,25 @@ static const struct bcm2835_clk_desc clk_desc_array[] = { .ctl_reg = CM_PERIICTL), }; +/* + * Permanently take a reference on the parent of the SDRAM clock. + * + * While the SDRAM is being driven by its dedicated PLL most of the + * time, there is a little loop running in the firmware that + * periodically switches the SDRAM to using our CM clock to do PVT + * recalibration, with the assumption that the previously configured + * SDRAM parent is still enabled and running. + */ +static int bcm2835_mark_sdc_parent_critical(struct clk *sdc) +{ + struct clk *parent = clk_get_parent(sdc); + + if (IS_ERR(parent)) + return PTR_ERR(parent); + + return clk_prepare_enable(parent); +} + static int bcm2835_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1810,6 +1830,7 @@ static int bcm2835_clk_probe(struct platform_device *pdev) const struct bcm2835_clk_desc *desc; const size_t asize = ARRAY_SIZE(clk_desc_array); size_t i; + int ret; cprman = devm_kzalloc(dev, sizeof(*cprman) + asize * sizeof(*clks), @@ -1840,6 +1861,10 @@ static int bcm2835_clk_probe(struct platform_device *pdev) clks[i] = desc->clk_register(cprman, desc->data); } + ret = bcm2835_mark_sdc_parent_critical(clks[BCM2835_CLOCK_SDRAM]); + if (ret) + return ret; + return of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &cprman->onecell); }