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

Commit 5ad67d3e authored by Mike Turquette's avatar Mike Turquette
Browse files

Merge tag 'v3.18-rockchip-cpuclk' of...

Merge tag 'v3.18-rockchip-cpuclk' of git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip into clk-next

CPU clock handling for Rockchip SoCs
parents 8791db53 0e5bdb3f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
obj-y	+= clk-rockchip.o
obj-y	+= clk.o
obj-y	+= clk-pll.o
obj-y	+= clk-cpu.o
obj-$(CONFIG_RESET_CONTROLLER)	+= softrst.o

obj-y	+= clk-rk3188.o
+329 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014 MundoReader S.L.
 * Author: Heiko Stuebner <heiko@sntech.de>
 *
 * based on clk/samsung/clk-cpu.c
 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
 * Author: Thomas Abraham <thomas.ab@samsung.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * A CPU clock is defined as a clock supplied to a CPU or a group of CPUs.
 * The CPU clock is typically derived from a hierarchy of clock
 * blocks which includes mux and divider blocks. There are a number of other
 * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
 * clock for CPU domain. The rates of these auxiliary clocks are related to the
 * CPU clock rate and this relation is usually specified in the hardware manual
 * of the SoC or supplied after the SoC characterization.
 *
 * The below implementation of the CPU clock allows the rate changes of the CPU
 * clock and the corresponding rate changes of the auxillary clocks of the CPU
 * domain. The platform clock driver provides a clock register configuration
 * for each configurable rate which is then used to program the clock hardware
 * registers to acheive a fast co-oridinated rate change for all the CPU domain
 * clocks.
 *
 * On a rate change request for the CPU clock, the rate change is propagated
 * upto the PLL supplying the clock to the CPU domain clock blocks. While the
 * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
 * alternate clock source. If required, the alternate clock source is divided
 * down in order to keep the output clock rate within the previous OPP limits.
 */

#include <linux/of.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/clk-provider.h>
#include "clk.h"

/**
 * struct rockchip_cpuclk: information about clock supplied to a CPU core.
 * @hw:		handle between ccf and cpu clock.
 * @alt_parent:	alternate parent clock to use when switching the speed
 *		of the primary parent clock.
 * @reg_base:	base register for cpu-clock values.
 * @clk_nb:	clock notifier registered for changes in clock speed of the
 *		primary parent clock.
 * @rate_count:	number of rates in the rate_table
 * @rate_table:	pll-rates and their associated dividers
 * @reg_data:	cpu-specific register settings
 * @lock:	clock lock
 */
struct rockchip_cpuclk {
	struct clk_hw				hw;

	struct clk_mux				cpu_mux;
	const struct clk_ops			*cpu_mux_ops;

	struct clk				*alt_parent;
	void __iomem				*reg_base;
	struct notifier_block			clk_nb;
	unsigned int				rate_count;
	struct rockchip_cpuclk_rate_table	*rate_table;
	const struct rockchip_cpuclk_reg_data	*reg_data;
	spinlock_t				*lock;
};

#define to_rockchip_cpuclk_hw(hw) container_of(hw, struct rockchip_cpuclk, hw)
#define to_rockchip_cpuclk_nb(nb) \
			container_of(nb, struct rockchip_cpuclk, clk_nb)

static const struct rockchip_cpuclk_rate_table *rockchip_get_cpuclk_settings(
			    struct rockchip_cpuclk *cpuclk, unsigned long rate)
{
	const struct rockchip_cpuclk_rate_table *rate_table =
							cpuclk->rate_table;
	int i;

	for (i = 0; i < cpuclk->rate_count; i++) {
		if (rate == rate_table[i].prate)
			return &rate_table[i];
	}

	return NULL;
}

static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw,
					unsigned long parent_rate)
{
	struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw);
	const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
	u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg);

	clksel0 >>= reg_data->div_core_shift;
	clksel0 &= reg_data->div_core_mask;
	return parent_rate / (clksel0 + 1);
}

static const struct clk_ops rockchip_cpuclk_ops = {
	.recalc_rate = rockchip_cpuclk_recalc_rate,
};

static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk,
				const struct rockchip_cpuclk_rate_table *rate)
{
	int i;

	/* alternate parent is active now. set the dividers */
	for (i = 0; i < ARRAY_SIZE(rate->divs); i++) {
		const struct rockchip_cpuclk_clksel *clksel = &rate->divs[i];

		if (!clksel->reg)
			continue;

		pr_debug("%s: setting reg 0x%x to 0x%x\n",
			 __func__, clksel->reg, clksel->val);
		writel(clksel->val , cpuclk->reg_base + clksel->reg);
	}
}

static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
					   struct clk_notifier_data *ndata)
{
	const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
	unsigned long alt_prate, alt_div;

	alt_prate = clk_get_rate(cpuclk->alt_parent);

	spin_lock(cpuclk->lock);

	/*
	 * If the old parent clock speed is less than the clock speed
	 * of the alternate parent, then it should be ensured that at no point
	 * the armclk speed is more than the old_rate until the dividers are
	 * set.
	 */
	if (alt_prate > ndata->old_rate) {
		/* calculate dividers */
		alt_div =  DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1;
		if (alt_div > reg_data->div_core_mask) {
			pr_warn("%s: limiting alt-divider %lu to %d\n",
				__func__, alt_div, reg_data->div_core_mask);
			alt_div = reg_data->div_core_mask;
		}

		/*
		 * Change parents and add dividers in a single transaction.
		 *
		 * NOTE: we do this in a single transaction so we're never
		 * dividing the primary parent by the extra dividers that were
		 * needed for the alt.
		 */
		pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n",
			 __func__, alt_div, alt_prate, ndata->old_rate);

		writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask,
					      reg_data->div_core_shift) |
		       HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
		       cpuclk->reg_base + reg_data->core_reg);
	} else {
		/* select alternate parent */
		writel(HIWORD_UPDATE(1, 1, reg_data->mux_core_shift),
			cpuclk->reg_base + reg_data->core_reg);
	}

	spin_unlock(cpuclk->lock);
	return 0;
}

static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
					    struct clk_notifier_data *ndata)
{
	const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
	const struct rockchip_cpuclk_rate_table *rate;

	rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
	if (!rate) {
		pr_err("%s: Invalid rate : %lu for cpuclk\n",
		       __func__, ndata->new_rate);
		return -EINVAL;
	}

	spin_lock(cpuclk->lock);

	if (ndata->old_rate < ndata->new_rate)
		rockchip_cpuclk_set_dividers(cpuclk, rate);

	/*
	 * post-rate change event, re-mux to primary parent and remove dividers.
	 *
	 * NOTE: we do this in a single transaction so we're never dividing the
	 * primary parent by the extra dividers that were needed for the alt.
	 */

	writel(HIWORD_UPDATE(0, reg_data->div_core_mask,
				reg_data->div_core_shift) |
	       HIWORD_UPDATE(0, 1, reg_data->mux_core_shift),
	       cpuclk->reg_base + reg_data->core_reg);

	if (ndata->old_rate > ndata->new_rate)
		rockchip_cpuclk_set_dividers(cpuclk, rate);

	spin_unlock(cpuclk->lock);
	return 0;
}

/*
 * This clock notifier is called when the frequency of the parent clock
 * of cpuclk is to be changed. This notifier handles the setting up all
 * the divider clocks, remux to temporary parent and handling the safe
 * frequency levels when using temporary parent.
 */
static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb,
					unsigned long event, void *data)
{
	struct clk_notifier_data *ndata = data;
	struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
	int ret = 0;

	pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
		 __func__, event, ndata->old_rate, ndata->new_rate);
	if (event == PRE_RATE_CHANGE)
		ret = rockchip_cpuclk_pre_rate_change(cpuclk, ndata);
	else if (event == POST_RATE_CHANGE)
		ret = rockchip_cpuclk_post_rate_change(cpuclk, ndata);

	return notifier_from_errno(ret);
}

struct clk *rockchip_clk_register_cpuclk(const char *name,
			const char **parent_names, u8 num_parents,
			const struct rockchip_cpuclk_reg_data *reg_data,
			const struct rockchip_cpuclk_rate_table *rates,
			int nrates, void __iomem *reg_base, spinlock_t *lock)
{
	struct rockchip_cpuclk *cpuclk;
	struct clk_init_data init;
	struct clk *clk, *cclk;
	int ret;

	if (num_parents != 2) {
		pr_err("%s: needs two parent clocks\n", __func__);
		return ERR_PTR(-EINVAL);
	}

	cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
	if (!cpuclk)
		return ERR_PTR(-ENOMEM);

	init.name = name;
	init.parent_names = &parent_names[0];
	init.num_parents = 1;
	init.ops = &rockchip_cpuclk_ops;

	/* only allow rate changes when we have a rate table */
	init.flags = (nrates > 0) ? CLK_SET_RATE_PARENT : 0;

	/* disallow automatic parent changes by ccf */
	init.flags |= CLK_SET_RATE_NO_REPARENT;

	init.flags |= CLK_GET_RATE_NOCACHE;

	cpuclk->reg_base = reg_base;
	cpuclk->lock = lock;
	cpuclk->reg_data = reg_data;
	cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
	cpuclk->hw.init = &init;

	cpuclk->alt_parent = __clk_lookup(parent_names[1]);
	if (!cpuclk->alt_parent) {
		pr_err("%s: could not lookup alternate parent\n",
		       __func__);
		ret = -EINVAL;
		goto free_cpuclk;
	}

	ret = clk_prepare_enable(cpuclk->alt_parent);
	if (ret) {
		pr_err("%s: could not enable alternate parent\n",
		       __func__);
		goto free_cpuclk;
	}

	clk = __clk_lookup(parent_names[0]);
	if (!clk) {
		pr_err("%s: could not lookup parent clock %s\n",
		       __func__, parent_names[0]);
		ret = -EINVAL;
		goto free_cpuclk;
	}

	ret = clk_notifier_register(clk, &cpuclk->clk_nb);
	if (ret) {
		pr_err("%s: failed to register clock notifier for %s\n",
				__func__, name);
		goto free_cpuclk;
	}

	if (nrates > 0) {
		cpuclk->rate_count = nrates;
		cpuclk->rate_table = kmemdup(rates,
					     sizeof(*rates) * nrates,
					     GFP_KERNEL);
		if (!cpuclk->rate_table) {
			pr_err("%s: could not allocate memory for cpuclk rates\n",
			       __func__);
			ret = -ENOMEM;
			goto unregister_notifier;
		}
	}

	cclk = clk_register(NULL, &cpuclk->hw);
	if (IS_ERR(clk)) {
		pr_err("%s: could not register cpuclk %s\n", __func__,	name);
		ret = PTR_ERR(clk);
		goto free_rate_table;
	}

	return cclk;

free_rate_table:
	kfree(cpuclk->rate_table);
unregister_notifier:
	clk_notifier_unregister(clk, &cpuclk->clk_nb);
free_cpuclk:
	kfree(cpuclk);
	return ERR_PTR(ret);
}
+13 −50
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ struct rockchip_clk_pll {
	const struct clk_ops	*pll_mux_ops;

	struct notifier_block	clk_nb;
	bool			rate_change_remuxed;

	void __iomem		*reg_base;
	int			lock_offset;
@@ -108,38 +107,6 @@ static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
	return -ETIMEDOUT;
}

/**
 * Set pll mux when changing the pll rate.
 * This makes sure to move the pll mux away from the actual pll before
 * changing its rate and back to the original parent after the change.
 */
static int rockchip_pll_notifier_cb(struct notifier_block *nb,
					unsigned long event, void *data)
{
	struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb);
	struct clk_mux *pll_mux = &pll->pll_mux;
	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
	int cur_parent;

	switch (event) {
	case PRE_RATE_CHANGE:
		cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
		if (cur_parent == PLL_MODE_NORM) {
			pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
			pll->rate_change_remuxed = 1;
		}
		break;
	case POST_RATE_CHANGE:
		if (pll->rate_change_remuxed) {
			pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
			pll->rate_change_remuxed = 0;
		}
		break;
	}

	return NOTIFY_OK;
}

/**
 * PLL used in RK3066, RK3188 and RK3288
 */
@@ -194,6 +161,10 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
	const struct rockchip_pll_rate_table *rate;
	unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
	struct regmap *grf = rockchip_clk_get_grf();
	struct clk_mux *pll_mux = &pll->pll_mux;
	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
	int rate_change_remuxed = 0;
	int cur_parent;
	int ret;

	if (IS_ERR(grf)) {
@@ -216,6 +187,12 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
	pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
		 __func__, rate->rate, rate->nr, rate->no, rate->nf);

	cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
	if (cur_parent == PLL_MODE_NORM) {
		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
		rate_change_remuxed = 1;
	}

	/* enter reset mode */
	writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
	       pll->reg_base + RK3066_PLLCON(3));
@@ -247,6 +224,9 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
		rockchip_rk3066_pll_set_rate(hw, old_rate, prate);
	}

	if (rate_change_remuxed)
		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);

	return ret;
}

@@ -310,7 +290,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
	struct clk_mux *pll_mux;
	struct clk *pll_clk, *mux_clk;
	char pll_name[20];
	int ret;

	if (num_parents != 2) {
		pr_err("%s: needs two parent clocks\n", __func__);
@@ -367,7 +346,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
	pll->lock_offset = grf_lock_offset;
	pll->lock_shift = lock_shift;
	pll->lock = lock;
	pll->clk_nb.notifier_call = rockchip_pll_notifier_cb;

	pll_clk = clk_register(NULL, &pll->hw);
	if (IS_ERR(pll_clk)) {
@@ -377,14 +355,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
		goto err_pll;
	}

	ret = clk_notifier_register(pll_clk, &pll->clk_nb);
	if (ret) {
		pr_err("%s: failed to register clock notifier for %s : %d\n",
				__func__, name, ret);
		mux_clk = ERR_PTR(ret);
		goto err_pll_notifier;
	}

	/* create the mux on top of the real pll */
	pll->pll_mux_ops = &clk_mux_ops;
	pll_mux = &pll->pll_mux;
@@ -417,13 +387,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
	return mux_clk;

err_mux:
	ret = clk_notifier_unregister(pll_clk, &pll->clk_nb);
	if (ret) {
		pr_err("%s: could not unregister clock notifier in error path : %d\n",
		       __func__, ret);
		return mux_clk;
	}
err_pll_notifier:
	clk_unregister(pll_clk);
err_pll:
	kfree(pll);
+146 −15
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <dt-bindings/clock/rk3188-cru-common.h>
#include "clk.h"

#define RK3066_GRF_SOC_STATUS	0x15c
#define RK3188_GRF_SOC_STATUS	0xac

enum rk3188_plls {
@@ -100,6 +101,98 @@ struct rockchip_pll_rate_table rk3188_pll_rates[] = {
	{ /* sentinel */ },
};

#define RK3066_DIV_CORE_PERIPH_MASK	0x3
#define RK3066_DIV_CORE_PERIPH_SHIFT	6
#define RK3066_DIV_ACLK_CORE_MASK	0x7
#define RK3066_DIV_ACLK_CORE_SHIFT	0
#define RK3066_DIV_ACLK_HCLK_MASK	0x3
#define RK3066_DIV_ACLK_HCLK_SHIFT	8
#define RK3066_DIV_ACLK_PCLK_MASK	0x3
#define RK3066_DIV_ACLK_PCLK_SHIFT	12
#define RK3066_DIV_AHB2APB_MASK		0x3
#define RK3066_DIV_AHB2APB_SHIFT	14

#define RK3066_CLKSEL0(_core_peri)					\
	{								\
		.reg = RK2928_CLKSEL_CON(0),				\
		.val = HIWORD_UPDATE(_core_peri, RK3066_DIV_CORE_PERIPH_MASK, \
				RK3066_DIV_CORE_PERIPH_SHIFT)		\
	}
#define RK3066_CLKSEL1(_aclk_core, _aclk_hclk, _aclk_pclk, _ahb2apb)	\
	{								\
		.reg = RK2928_CLKSEL_CON(1),				\
		.val = HIWORD_UPDATE(_aclk_core, RK3066_DIV_ACLK_CORE_MASK, \
				RK3066_DIV_ACLK_CORE_SHIFT) |		\
		       HIWORD_UPDATE(_aclk_hclk, RK3066_DIV_ACLK_HCLK_MASK, \
				RK3066_DIV_ACLK_HCLK_SHIFT) |		\
		       HIWORD_UPDATE(_aclk_pclk, RK3066_DIV_ACLK_PCLK_MASK, \
				RK3066_DIV_ACLK_PCLK_SHIFT) |		\
		       HIWORD_UPDATE(_ahb2apb, RK3066_DIV_AHB2APB_MASK,	\
				RK3066_DIV_AHB2APB_SHIFT),		\
	}

#define RK3066_CPUCLK_RATE(_prate, _core_peri, _acore, _ahclk, _apclk, _h2p) \
	{								\
		.prate = _prate,					\
		.divs = {						\
			RK3066_CLKSEL0(_core_peri),			\
			RK3066_CLKSEL1(_acore, _ahclk, _apclk, _h2p),	\
		},							\
	}

static struct rockchip_cpuclk_rate_table rk3066_cpuclk_rates[] __initdata = {
	RK3066_CPUCLK_RATE(1416000000, 2, 3, 1, 2, 1),
	RK3066_CPUCLK_RATE(1200000000, 2, 3, 1, 2, 1),
	RK3066_CPUCLK_RATE(1008000000, 2, 2, 1, 2, 1),
	RK3066_CPUCLK_RATE( 816000000, 2, 2, 1, 2, 1),
	RK3066_CPUCLK_RATE( 600000000, 1, 2, 1, 2, 1),
	RK3066_CPUCLK_RATE( 504000000, 1, 1, 1, 2, 1),
	RK3066_CPUCLK_RATE( 312000000, 0, 1, 1, 1, 0),
};

static const struct rockchip_cpuclk_reg_data rk3066_cpuclk_data = {
	.core_reg = RK2928_CLKSEL_CON(0),
	.div_core_shift = 0,
	.div_core_mask = 0x1f,
	.mux_core_shift = 8,
};

#define RK3188_DIV_ACLK_CORE_MASK	0x7
#define RK3188_DIV_ACLK_CORE_SHIFT	3

#define RK3188_CLKSEL1(_aclk_core)		\
	{					\
		.reg = RK2928_CLKSEL_CON(1),	\
		.val = HIWORD_UPDATE(_aclk_core, RK3188_DIV_ACLK_CORE_MASK,\
				 RK3188_DIV_ACLK_CORE_SHIFT) \
	}
#define RK3188_CPUCLK_RATE(_prate, _core_peri, _aclk_core)	\
	{							\
		.prate = _prate,				\
		.divs = {					\
			RK3066_CLKSEL0(_core_peri),		\
			RK3188_CLKSEL1(_aclk_core),		\
		},						\
	}

static struct rockchip_cpuclk_rate_table rk3188_cpuclk_rates[] __initdata = {
	RK3188_CPUCLK_RATE(1608000000, 2, 3),
	RK3188_CPUCLK_RATE(1416000000, 2, 3),
	RK3188_CPUCLK_RATE(1200000000, 2, 3),
	RK3188_CPUCLK_RATE(1008000000, 2, 3),
	RK3188_CPUCLK_RATE( 816000000, 2, 3),
	RK3188_CPUCLK_RATE( 600000000, 1, 3),
	RK3188_CPUCLK_RATE( 504000000, 1, 3),
	RK3188_CPUCLK_RATE( 312000000, 0, 1),
};

static const struct rockchip_cpuclk_reg_data rk3188_cpuclk_data = {
	.core_reg = RK2928_CLKSEL_CON(0),
	.div_core_shift = 9,
	.div_core_mask = 0x1f,
	.mux_core_shift = 8,
};

PNAME(mux_pll_p)		= { "xin24m", "xin32k" };
PNAME(mux_armclk_p)		= { "apll", "gpll_armclk" };
PNAME(mux_ddrphy_p)		= { "dpll", "gpll_ddr" };
@@ -173,17 +266,10 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
	GATE(0, "aclk_cpu", "aclk_cpu_pre", 0,
			RK2928_CLKGATE_CON(0), 3, GFLAGS),

	DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
	GATE(0, "atclk_cpu", "pclk_cpu_pre", 0,
			RK2928_CLKGATE_CON(0), 6, GFLAGS),
	GATE(0, "pclk_cpu", "pclk_cpu_pre", 0,
			RK2928_CLKGATE_CON(0), 5, GFLAGS),
	DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
	COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
			RK2928_CLKGATE_CON(4), 9, GFLAGS),
	GATE(0, "hclk_cpu", "hclk_cpu_pre", 0,
			RK2928_CLKGATE_CON(0), 4, GFLAGS),

@@ -412,10 +498,18 @@ static struct clk_div_table div_aclk_cpu_t[] = {
};

static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
	COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
			RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 0, 5, DFLAGS),
	DIVTBL(0, "aclk_cpu_pre", "armclk", 0,
			RK2928_CLKSEL_CON(1), 0, 3, DFLAGS, div_aclk_cpu_t),
			RK2928_CLKSEL_CON(1), 0, 3, DFLAGS | CLK_DIVIDER_READ_ONLY, div_aclk_cpu_t),
	DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
							    | CLK_DIVIDER_READ_ONLY),
	DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
							   | CLK_DIVIDER_READ_ONLY),
	COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO
							    | CLK_DIVIDER_READ_ONLY,
			RK2928_CLKGATE_CON(4), 9, GFLAGS),

	GATE(CORE_L2C, "core_l2c", "aclk_cpu", 0,
			RK2928_CLKGATE_CON(9), 4, GFLAGS),
@@ -524,8 +618,6 @@ PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1",
				    "gpll", "cpll" };

static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
	COMPOSITE_NOGATE(0, "armclk", mux_armclk_p, 0,
			RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS),
	COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0,
			RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
			div_rk3188_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS),
@@ -533,6 +625,13 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
	/* do not source aclk_cpu_pre from the apll, to keep complexity down */
	COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT,
			RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS),
	DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
	DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
	COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
			RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
			RK2928_CLKGATE_CON(4), 9, GFLAGS),

	GATE(CORE_L2C, "core_l2c", "armclk", 0,
			RK2928_CLKGATE_CON(9), 4, GFLAGS),
@@ -629,9 +728,6 @@ static void __init rk3188_common_clk_init(struct device_node *np)
		pr_warn("%s: could not register clock usb480m: %ld\n",
			__func__, PTR_ERR(clk));

	rockchip_clk_register_plls(rk3188_pll_clks,
				   ARRAY_SIZE(rk3188_pll_clks),
				   RK3188_GRF_SOC_STATUS);
	rockchip_clk_register_branches(common_clk_branches,
				  ARRAY_SIZE(common_clk_branches));
	rockchip_clk_protect_critical(rk3188_critical_clocks,
@@ -644,16 +740,51 @@ static void __init rk3188_common_clk_init(struct device_node *np)
static void __init rk3066a_clk_init(struct device_node *np)
{
	rk3188_common_clk_init(np);
	rockchip_clk_register_plls(rk3188_pll_clks,
				   ARRAY_SIZE(rk3188_pll_clks),
				   RK3066_GRF_SOC_STATUS);
	rockchip_clk_register_branches(rk3066a_clk_branches,
				  ARRAY_SIZE(rk3066a_clk_branches));
	rockchip_clk_register_armclk(ARMCLK, "armclk",
			mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
			&rk3066_cpuclk_data, rk3066_cpuclk_rates,
			ARRAY_SIZE(rk3066_cpuclk_rates));
}
CLK_OF_DECLARE(rk3066a_cru, "rockchip,rk3066a-cru", rk3066a_clk_init);

static void __init rk3188a_clk_init(struct device_node *np)
{
	struct clk *clk1, *clk2;
	unsigned long rate;
	int ret;

	rk3188_common_clk_init(np);
	rockchip_clk_register_plls(rk3188_pll_clks,
				   ARRAY_SIZE(rk3188_pll_clks),
				   RK3188_GRF_SOC_STATUS);
	rockchip_clk_register_branches(rk3188_clk_branches,
				  ARRAY_SIZE(rk3188_clk_branches));
	rockchip_clk_register_armclk(ARMCLK, "armclk",
				  mux_armclk_p, ARRAY_SIZE(mux_armclk_p),
				  &rk3188_cpuclk_data, rk3188_cpuclk_rates,
				  ARRAY_SIZE(rk3188_cpuclk_rates));

	/* reparent aclk_cpu_pre from apll */
	clk1 = __clk_lookup("aclk_cpu_pre");
	clk2 = __clk_lookup("gpll");
	if (clk1 && clk2) {
		rate = clk_get_rate(clk1);

		ret = clk_set_parent(clk1, clk2);
		if (ret < 0)
			pr_warn("%s: could not reparent aclk_cpu_pre to gpll\n",
				__func__);

		clk_set_rate(clk1, rate);
	} else {
		pr_warn("%s: missing clocks to reparent aclk_cpu_pre to gpll\n",
			__func__);
	}
}
CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);

+80 −13

File changed.

Preview size limit exceeded, changes collapsed.

Loading