Commit e7590308 authored by Stephen Boyd's avatar Stephen Boyd

Merge tag 'sunxi-clk-fixes-for-4.11-2-bis' of...

Merge tag 'sunxi-clk-fixes-for-4.11-2-bis' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux into clk-fixes

Pull Allwinner clock fixes for 4.11 from Maxime Ripard:

Two build errors fixes for the sunxi-ng drivers.

The two other patches fix random CPU crashes happening on the A33 since
CPUFreq has been enabled in 4.11.

* tag 'sunxi-clk-fixes-for-4.11-2-bis' of https://git.kernel.org/pub/scm/linux/kernel/git/sunxi/linux:
  clk: sunxi-ng: a33: gate then ungate PLL CPU clk after rate change
  clk: sunxi-ng: Add clk notifier to gate then ungate PLL clocks
  clk: sunxi-ng: fix build failure in ccu-sun9i-a80 driver
  clk: sunxi-ng: fix build error without CONFIG_RESET_CONTROLLER
parents daffad21 372fa101
config SUNXI_CCU config SUNXI_CCU
bool "Clock support for Allwinner SoCs" bool "Clock support for Allwinner SoCs"
depends on ARCH_SUNXI || COMPILE_TEST depends on ARCH_SUNXI || COMPILE_TEST
select RESET_CONTROLLER
default ARCH_SUNXI default ARCH_SUNXI
if SUNXI_CCU if SUNXI_CCU
...@@ -135,6 +136,7 @@ config SUN8I_V3S_CCU ...@@ -135,6 +136,7 @@ config SUN8I_V3S_CCU
config SUN9I_A80_CCU config SUN9I_A80_CCU
bool "Support for the Allwinner A80 CCU" bool "Support for the Allwinner A80 CCU"
select SUNXI_CCU_DIV select SUNXI_CCU_DIV
select SUNXI_CCU_MULT
select SUNXI_CCU_GATE select SUNXI_CCU_GATE
select SUNXI_CCU_NKMP select SUNXI_CCU_NKMP
select SUNXI_CCU_NM select SUNXI_CCU_NM
......
...@@ -752,6 +752,13 @@ static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = { ...@@ -752,6 +752,13 @@ static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = {
.num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets), .num_resets = ARRAY_SIZE(sun8i_a33_ccu_resets),
}; };
static struct ccu_pll_nb sun8i_a33_pll_cpu_nb = {
.common = &pll_cpux_clk.common,
/* copy from pll_cpux_clk */
.enable = BIT(31),
.lock = BIT(28),
};
static struct ccu_mux_nb sun8i_a33_cpu_nb = { static struct ccu_mux_nb sun8i_a33_cpu_nb = {
.common = &cpux_clk.common, .common = &cpux_clk.common,
.cm = &cpux_clk.mux, .cm = &cpux_clk.mux,
...@@ -783,6 +790,10 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node) ...@@ -783,6 +790,10 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)
sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc); sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
/* Gate then ungate PLL CPU after any rate changes */
ccu_pll_notifier_register(&sun8i_a33_pll_cpu_nb);
/* Reparent CPU during PLL CPU rate changes */
ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
&sun8i_a33_cpu_nb); &sun8i_a33_cpu_nb);
} }
......
...@@ -14,11 +14,13 @@ ...@@ -14,11 +14,13 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/clk.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "ccu_common.h" #include "ccu_common.h"
#include "ccu_gate.h"
#include "ccu_reset.h" #include "ccu_reset.h"
static DEFINE_SPINLOCK(ccu_lock); static DEFINE_SPINLOCK(ccu_lock);
...@@ -39,6 +41,53 @@ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock) ...@@ -39,6 +41,53 @@ void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000)); WARN_ON(readl_relaxed_poll_timeout(addr, reg, reg & lock, 100, 70000));
} }
/*
* This clock notifier is called when the frequency of a PLL clock is
* changed. In common PLL designs, changes to the dividers take effect
* almost immediately, while changes to the multipliers (implemented
* as dividers in the feedback loop) take a few cycles to work into
* the feedback loop for the PLL to stablize.
*
* Sometimes when the PLL clock rate is changed, the decrease in the
* divider is too much for the decrease in the multiplier to catch up.
* The PLL clock rate will spike, and in some cases, might lock up
* completely.
*
* This notifier callback will gate and then ungate the clock,
* effectively resetting it, so it proceeds to work. Care must be
* taken to reparent consumers to other temporary clocks during the
* rate change, and that this notifier callback must be the first
* to be registered.
*/
static int ccu_pll_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct ccu_pll_nb *pll = to_ccu_pll_nb(nb);
int ret = 0;
if (event != POST_RATE_CHANGE)
goto out;
ccu_gate_helper_disable(pll->common, pll->enable);
ret = ccu_gate_helper_enable(pll->common, pll->enable);
if (ret)
goto out;
ccu_helper_wait_for_lock(pll->common, pll->lock);
out:
return notifier_from_errno(ret);
}
int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb)
{
pll_nb->clk_nb.notifier_call = ccu_pll_notifier_cb;
return clk_notifier_register(pll_nb->common->hw.clk,
&pll_nb->clk_nb);
}
int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
const struct sunxi_ccu_desc *desc) const struct sunxi_ccu_desc *desc)
{ {
......
...@@ -83,6 +83,18 @@ struct sunxi_ccu_desc { ...@@ -83,6 +83,18 @@ struct sunxi_ccu_desc {
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock); void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock);
struct ccu_pll_nb {
struct notifier_block clk_nb;
struct ccu_common *common;
u32 enable;
u32 lock;
};
#define to_ccu_pll_nb(_nb) container_of(_nb, struct ccu_pll_nb, clk_nb)
int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb);
int sunxi_ccu_probe(struct device_node *node, void __iomem *reg, int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
const struct sunxi_ccu_desc *desc); const struct sunxi_ccu_desc *desc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment