2016-06-30 03:05:23 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Maxime Ripard. All rights reserved.
|
|
|
|
*
|
|
|
|
* This software is licensed under the terms of the GNU General Public
|
|
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
|
|
* may be copied, distributed, and modified under those terms.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _COMMON_H_
|
|
|
|
#define _COMMON_H_
|
|
|
|
|
|
|
|
#include <linux/compiler.h>
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
|
|
|
|
#define CCU_FEATURE_FRACTIONAL BIT(0)
|
|
|
|
#define CCU_FEATURE_VARIABLE_PREDIV BIT(1)
|
|
|
|
#define CCU_FEATURE_FIXED_PREDIV BIT(2)
|
|
|
|
#define CCU_FEATURE_FIXED_POSTDIV BIT(3)
|
2017-01-20 05:49:26 +08:00
|
|
|
#define CCU_FEATURE_ALL_PREDIV BIT(4)
|
2017-01-28 20:22:33 +08:00
|
|
|
#define CCU_FEATURE_LOCK_REG BIT(5)
|
clk: sunxi-ng: Add interface to query or configure MMC timing modes.
Starting with the A83T SoC, Allwinner introduced a new timing mode for
its MMC clocks. The new mode changes how the MMC controller sample and
output clocks are delayed to match chip and board specifics. There are
two controls for this, one on the CCU side controlling how the clocks
behave, and one in the MMC controller controlling what inputs to take
and how to route them.
In the old mode, the MMC clock had 2 child clocks providing the output
and sample clocks, which could be delayed by a number of clock cycles
measured from the MMC clock's parent.
With the new mode, the 2 delay clocks are no longer active. Instead,
the delays and associated controls are moved into the MMC controller.
The output of the MMC clock is also halved.
The difference in how things are wired between the modes means that the
clock controls and the MMC controls must match. To achieve this in a
clear, explicit way, we introduce two functions for the MMC driver to
use: one queries the hardware for the current mode set, and the other
allows the MMC driver to request a mode.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
2017-07-24 21:58:56 +08:00
|
|
|
#define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6)
|
|
|
|
|
|
|
|
/* MMC timing mode switch bit */
|
|
|
|
#define CCU_MMC_NEW_TIMING_MODE BIT(30)
|
2016-06-30 03:05:23 +08:00
|
|
|
|
|
|
|
struct device_node;
|
|
|
|
|
|
|
|
#define CLK_HW_INIT(_name, _parent, _ops, _flags) \
|
|
|
|
&(struct clk_init_data) { \
|
|
|
|
.flags = _flags, \
|
|
|
|
.name = _name, \
|
|
|
|
.parent_names = (const char *[]) { _parent }, \
|
|
|
|
.num_parents = 1, \
|
|
|
|
.ops = _ops, \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags) \
|
|
|
|
&(struct clk_init_data) { \
|
|
|
|
.flags = _flags, \
|
|
|
|
.name = _name, \
|
|
|
|
.parent_names = _parents, \
|
|
|
|
.num_parents = ARRAY_SIZE(_parents), \
|
|
|
|
.ops = _ops, \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CLK_FIXED_FACTOR(_struct, _name, _parent, \
|
|
|
|
_div, _mult, _flags) \
|
|
|
|
struct clk_fixed_factor _struct = { \
|
|
|
|
.div = _div, \
|
|
|
|
.mult = _mult, \
|
|
|
|
.hw.init = CLK_HW_INIT(_name, \
|
|
|
|
_parent, \
|
|
|
|
&clk_fixed_factor_ops, \
|
|
|
|
_flags), \
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ccu_common {
|
|
|
|
void __iomem *base;
|
|
|
|
u16 reg;
|
2017-01-28 20:22:33 +08:00
|
|
|
u16 lock_reg;
|
2017-01-20 05:49:26 +08:00
|
|
|
u32 prediv;
|
2016-06-30 03:05:23 +08:00
|
|
|
|
|
|
|
unsigned long features;
|
|
|
|
spinlock_t *lock;
|
|
|
|
struct clk_hw hw;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw)
|
|
|
|
{
|
|
|
|
return container_of(hw, struct ccu_common, hw);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sunxi_ccu_desc {
|
|
|
|
struct ccu_common **ccu_clks;
|
|
|
|
unsigned long num_ccu_clks;
|
|
|
|
|
|
|
|
struct clk_hw_onecell_data *hw_clks;
|
|
|
|
|
|
|
|
struct ccu_reset_map *resets;
|
|
|
|
unsigned long num_resets;
|
|
|
|
};
|
|
|
|
|
|
|
|
void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock);
|
|
|
|
|
clk: sunxi-ng: Add clk notifier to gate then ungate PLL clocks
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 is especially the case if the divider changed is
the pre-divider, which affects the reference frequency.
This patch introduces a clk notifier callback that will gate and
then ungate a clk after a rate change, effectively resetting it,
so it continues to work, despite any possible lockups. Care must
be taken to reparent any consumers to other temporary clocks during
the rate change, and that this notifier callback must be the first
to be registered.
This is intended to fix occasional lockups with cpufreq on newer
Allwinner SoCs, such as the A33 and the H3. Previously it was
thought that reparenting the cpu clock away from the PLL while
it stabilized was enough, as this worked quite well on the A31.
On the A33, hangs have been observed after cpufreq was recently
introduced. With the H3, a more thorough test [1] showed that
reparenting alone isn't enough. The system still locks up unless
the dividers are limited to 1.
A hunch was if the PLL was stuck in some unknown state, perhaps
gating then ungating it would bring it back to normal. Tests
done by Icenowy Zheng using Ondrej's test firmware shows this
to be a valid solution.
[1] http://www.spinics.net/lists/arm-kernel/msg552501.html
Reported-by: Ondrej Jirman <megous@megous.com>
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Tested-by: Icenowy Zheng <icenowy@aosc.io>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
2017-04-13 10:13:52 +08:00
|
|
|
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);
|
|
|
|
|
2016-06-30 03:05:23 +08:00
|
|
|
int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
|
|
|
|
const struct sunxi_ccu_desc *desc);
|
|
|
|
|
|
|
|
#endif /* _COMMON_H_ */
|