Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f61143c4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux

Pull clk fixes from Stephen Boyd"

 - one stm32f4 fix for a change that introduced the PLL_I2S and PLL_SAI
   boards

 - two Allwinner clk driver build fixes

 - two Allwinner CPU clk driver fixes where we see random CPUFreq
   crashes because the CPU's PLL locks up sometimes when we change the
   rate

* tag 'clk-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/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
  clk: stm32f4: fix: exclude values 0 and 1 for PLLQ
parents 4988f7a4 e7590308
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -429,6 +429,13 @@ static const struct clk_div_table pll_divp_table[] = {
	{ 0, 2 }, { 1, 4 }, { 2, 6 }, { 3, 8 }, { 0 }
};

static const struct clk_div_table pll_divq_table[] = {
	{ 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 },
	{ 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 12, 12 }, { 13, 13 },
	{ 14, 14 }, { 15, 15 },
	{ 0 }
};

static const struct clk_div_table pll_divr_table[] = {
	{ 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 }
};
@@ -497,7 +504,7 @@ struct stm32f4_div_data {
#define MAX_PLL_DIV 3
static const struct stm32f4_div_data  div_data[MAX_PLL_DIV] = {
	{ 16, 2, 0, pll_divp_table },
	{ 24, 4, CLK_DIVIDER_ONE_BASED, NULL		},
	{ 24, 4, 0, pll_divq_table },
	{ 28, 3, 0, pll_divr_table },
};

+2 −0
Original line number Diff line number Diff line
config SUNXI_CCU
	bool "Clock support for Allwinner SoCs"
	depends on ARCH_SUNXI || COMPILE_TEST
	select RESET_CONTROLLER
	default ARCH_SUNXI

if SUNXI_CCU
@@ -135,6 +136,7 @@ config SUN8I_V3S_CCU
config SUN9I_A80_CCU
	bool "Support for the Allwinner A80 CCU"
	select SUNXI_CCU_DIV
	select SUNXI_CCU_MULT
	select SUNXI_CCU_GATE
	select SUNXI_CCU_NKMP
	select SUNXI_CCU_NM
+11 −0
Original line number Diff line number Diff line
@@ -752,6 +752,13 @@ static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = {
	.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 = {
	.common		= &cpux_clk.common,
	.cm		= &cpux_clk.mux,
@@ -783,6 +790,10 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)

	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,
				  &sun8i_a33_cpu_nb);
}
+49 −0
Original line number Diff line number Diff line
@@ -14,11 +14,13 @@
 * GNU General Public License for more details.
 */

#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h>
#include <linux/slab.h>

#include "ccu_common.h"
#include "ccu_gate.h"
#include "ccu_reset.h"

static DEFINE_SPINLOCK(ccu_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));
}

/*
 * 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,
		    const struct sunxi_ccu_desc *desc)
{
+12 −0
Original line number Diff line number Diff line
@@ -83,6 +83,18 @@ struct sunxi_ccu_desc {

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,
		    const struct sunxi_ccu_desc *desc);