- pll fixes for GXBB, GXL and AXG
- use regmap in clock controllers for GXBB, GXL and AXG - general clock updates for Meson8, GXBB, GXL and AXG Based on the clk-helpers topic branch as a dependency. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJap5YzAAoJEHfc29rIyEnRtxgQAJ+KgOifsPex8pjqSm50akLx 4eQJA1xteu5TEipC66eSM0/EyHyVHhShaqYQcNZmNA0ckI7wZkii4+EXPvrCxlyR ZvEs98BpPZKsZQ58yrKQ44AHdFREDnMF+9vDzklRhKzp676s9o9+IxowfAxCIWlz e4mpMOTwqRYMPVN1WabHCjVsmwkMR0d1+1JpoV1uZHJXYBBc2Rrd0BhoL9Ma9uiC Rr9osS97O8/xsk1bn6o1CLHhvLylvDw9rY6jSh2FltM7S6kkgg4SDdIvXc3unx2i ZpxQP9+f/TslAvXRh5ejOq7xPvOtXXqvCDbU5Vs226n3rwpTEhHrjSFCsmPB+C/d 1+B2bEELTapDHiq0s3JY3GP9Ks58RWwJzsaOwvh/3TVr2hvsJeQaiVUDqsyJ7nLu QEaaIhGtImw1WFyxghoc6IU9JH7Y/w41GioGg8rQEAhs+iKI6RQ9ZnO5TFW7aelQ VUYnWWCY/amJYoPdoMqNZVsDXqCJO7JmhB79t2Ssp9pOkipSHspzTemuT/+aqhU4 GGS3iKl2SE42KYbu5EK+YdVynWPd72swtRAu985D7K+4UToyhbRSXFm6XBubt1fw 99BdC5yGcyr1ShPmHAjTlL8+GttN74c4af7mvZgPom15g6bZw22e3X2LRmT1mL7z OQbsseAj9SZzxMtm83rK =TpHi -----END PGP SIGNATURE----- Merge tag 'clk-for-v4.17-1' of https://github.com/BayLibre/clk-meson into clk-amlogic Pull amlogic clk driver updates from Neil Armstrong: - pll fixes for GXBB, GXL and AXG - use regmap in clock controllers for GXBB, GXL and AXG - general clock updates for Meson8, GXBB, GXL and AXG (Based on the clk-helpers topic branch as a dependency) * tag 'clk-for-v4.17-1' of https://github.com/BayLibre/clk-meson: (49 commits) clk: meson: clean-up clk81 clocks clk: meson: add fdiv clock gates clk: meson: add mpll pre-divider clk: meson: axg: add hifi pll clock clk: meson: axg: add hifi clock bindings clk: meson: add ROUND_CLOSEST to the pll driver clk: meson: add gp0 frac parameter for axg and gxl clk: meson: improve pll driver results with frac clk: meson: remove special gp0 lock loop clk: meson: poke pll CNTL last clk: meson: add fractional part of meson8b fixed_pll clk: meson: use hhi syscon if available clk: meson: remove obsolete cpu_clk clk: meson: rework meson8b cpu clock clk: meson: split divider and gate part of mpll clk: meson: migrate plls clocks to clk_regmap clk: meson: migrate the audio divider clock to clk_regmap clk: meson: migrate mplls clocks to clk_regmap clk: meson: add regmap helpers for parm clk: meson: migrate muxes to clk_regmap ...
This commit is contained in:
commit
186dcd4ab2
@ -28,12 +28,10 @@
|
||||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
#define div_mask(width) ((1 << (width)) - 1)
|
||||
|
||||
static unsigned int _get_table_maxdiv(const struct clk_div_table *table,
|
||||
u8 width)
|
||||
{
|
||||
unsigned int maxdiv = 0, mask = div_mask(width);
|
||||
unsigned int maxdiv = 0, mask = clk_div_mask(width);
|
||||
const struct clk_div_table *clkt;
|
||||
|
||||
for (clkt = table; clkt->div; clkt++)
|
||||
@ -57,12 +55,12 @@ static unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (flags & CLK_DIVIDER_ONE_BASED)
|
||||
return div_mask(width);
|
||||
return clk_div_mask(width);
|
||||
if (flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return 1 << div_mask(width);
|
||||
return 1 << clk_div_mask(width);
|
||||
if (table)
|
||||
return _get_table_maxdiv(table, width);
|
||||
return div_mask(width) + 1;
|
||||
return clk_div_mask(width) + 1;
|
||||
}
|
||||
|
||||
static unsigned int _get_table_div(const struct clk_div_table *table,
|
||||
@ -84,7 +82,7 @@ static unsigned int _get_div(const struct clk_div_table *table,
|
||||
if (flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return 1 << val;
|
||||
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
|
||||
return val ? val : div_mask(width) + 1;
|
||||
return val ? val : clk_div_mask(width) + 1;
|
||||
if (table)
|
||||
return _get_table_div(table, val);
|
||||
return val + 1;
|
||||
@ -109,7 +107,7 @@ static unsigned int _get_val(const struct clk_div_table *table,
|
||||
if (flags & CLK_DIVIDER_POWER_OF_TWO)
|
||||
return __ffs(div);
|
||||
if (flags & CLK_DIVIDER_MAX_AT_ZERO)
|
||||
return (div == div_mask(width) + 1) ? 0 : div;
|
||||
return (div == clk_div_mask(width) + 1) ? 0 : div;
|
||||
if (table)
|
||||
return _get_table_val(table, div);
|
||||
return div - 1;
|
||||
@ -141,7 +139,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
|
||||
unsigned int val;
|
||||
|
||||
val = clk_readl(divider->reg) >> divider->shift;
|
||||
val &= div_mask(divider->width);
|
||||
val &= clk_div_mask(divider->width);
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, divider->table,
|
||||
divider->flags, divider->width);
|
||||
@ -344,19 +342,43 @@ long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(divider_round_rate_parent);
|
||||
|
||||
long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
|
||||
unsigned long rate, unsigned long *prate,
|
||||
const struct clk_div_table *table, u8 width,
|
||||
unsigned long flags, unsigned int val)
|
||||
{
|
||||
int div;
|
||||
|
||||
div = _get_div(table, val, flags, width);
|
||||
|
||||
/* Even a read-only clock can propagate a rate change */
|
||||
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
|
||||
if (!parent)
|
||||
return -EINVAL;
|
||||
|
||||
*prate = clk_hw_round_rate(parent, rate * div);
|
||||
}
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)*prate, div);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(divider_ro_round_rate_parent);
|
||||
|
||||
|
||||
static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
int bestdiv;
|
||||
|
||||
/* if read only, just return current value */
|
||||
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
|
||||
bestdiv = clk_readl(divider->reg) >> divider->shift;
|
||||
bestdiv &= div_mask(divider->width);
|
||||
bestdiv = _get_div(divider->table, bestdiv, divider->flags,
|
||||
divider->width);
|
||||
return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
|
||||
u32 val;
|
||||
|
||||
val = clk_readl(divider->reg) >> divider->shift;
|
||||
val &= clk_div_mask(divider->width);
|
||||
|
||||
return divider_ro_round_rate(hw, rate, prate, divider->table,
|
||||
divider->width, divider->flags,
|
||||
val);
|
||||
}
|
||||
|
||||
return divider_round_rate(hw, rate, prate, divider->table,
|
||||
@ -376,7 +398,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
|
||||
|
||||
value = _get_val(table, div, flags, width);
|
||||
|
||||
return min_t(unsigned int, value, div_mask(width));
|
||||
return min_t(unsigned int, value, clk_div_mask(width));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(divider_get_val);
|
||||
|
||||
@ -399,10 +421,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
__acquire(divider->lock);
|
||||
|
||||
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
|
||||
val = div_mask(divider->width) << (divider->shift + 16);
|
||||
val = clk_div_mask(divider->width) << (divider->shift + 16);
|
||||
} else {
|
||||
val = clk_readl(divider->reg);
|
||||
val &= ~(div_mask(divider->width) << divider->shift);
|
||||
val &= ~(clk_div_mask(divider->width) << divider->shift);
|
||||
}
|
||||
val |= (u32)value << divider->shift;
|
||||
clk_writel(val, divider->reg);
|
||||
|
@ -26,35 +26,24 @@
|
||||
* parent - parent is adjustable through clk_set_parent
|
||||
*/
|
||||
|
||||
static u8 clk_mux_get_parent(struct clk_hw *hw)
|
||||
int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
|
||||
unsigned int val)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
int num_parents = clk_hw_get_num_parents(hw);
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* FIXME need a mux-specific flag to determine if val is bitwise or numeric
|
||||
* e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
|
||||
* to 0x7 (index starts at one)
|
||||
* OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
|
||||
* val = 0x4 really means "bit 2, index starts at bit 0"
|
||||
*/
|
||||
val = clk_readl(mux->reg) >> mux->shift;
|
||||
val &= mux->mask;
|
||||
|
||||
if (mux->table) {
|
||||
if (table) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_parents; i++)
|
||||
if (mux->table[i] == val)
|
||||
if (table[i] == val)
|
||||
return i;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (val && (mux->flags & CLK_MUX_INDEX_BIT))
|
||||
if (val && (flags & CLK_MUX_INDEX_BIT))
|
||||
val = ffs(val) - 1;
|
||||
|
||||
if (val && (mux->flags & CLK_MUX_INDEX_ONE))
|
||||
if (val && (flags & CLK_MUX_INDEX_ONE))
|
||||
val--;
|
||||
|
||||
if (val >= num_parents)
|
||||
@ -62,22 +51,43 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_mux_val_to_index);
|
||||
|
||||
unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
|
||||
{
|
||||
unsigned int val = index;
|
||||
|
||||
if (table) {
|
||||
val = table[index];
|
||||
} else {
|
||||
if (flags & CLK_MUX_INDEX_BIT)
|
||||
val = 1 << index;
|
||||
|
||||
if (flags & CLK_MUX_INDEX_ONE)
|
||||
val++;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_mux_index_to_val);
|
||||
|
||||
static u8 clk_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
u32 val;
|
||||
|
||||
val = clk_readl(mux->reg) >> mux->shift;
|
||||
val &= mux->mask;
|
||||
|
||||
return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
|
||||
}
|
||||
|
||||
static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
u32 val;
|
||||
u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
|
||||
unsigned long flags = 0;
|
||||
|
||||
if (mux->table) {
|
||||
index = mux->table[index];
|
||||
} else {
|
||||
if (mux->flags & CLK_MUX_INDEX_BIT)
|
||||
index = 1 << index;
|
||||
|
||||
if (mux->flags & CLK_MUX_INDEX_ONE)
|
||||
index++;
|
||||
}
|
||||
u32 reg;
|
||||
|
||||
if (mux->lock)
|
||||
spin_lock_irqsave(mux->lock, flags);
|
||||
@ -85,13 +95,14 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
__acquire(mux->lock);
|
||||
|
||||
if (mux->flags & CLK_MUX_HIWORD_MASK) {
|
||||
val = mux->mask << (mux->shift + 16);
|
||||
reg = mux->mask << (mux->shift + 16);
|
||||
} else {
|
||||
val = clk_readl(mux->reg);
|
||||
val &= ~(mux->mask << mux->shift);
|
||||
reg = clk_readl(mux->reg);
|
||||
reg &= ~(mux->mask << mux->shift);
|
||||
}
|
||||
val |= index << mux->shift;
|
||||
clk_writel(val, mux->reg);
|
||||
val = val << mux->shift;
|
||||
reg |= val;
|
||||
clk_writel(reg, mux->reg);
|
||||
|
||||
if (mux->lock)
|
||||
spin_unlock_irqrestore(mux->lock, flags);
|
||||
|
@ -1125,8 +1125,10 @@ static int clk_core_round_rate_nolock(struct clk_core *core,
|
||||
{
|
||||
lockdep_assert_held(&prepare_lock);
|
||||
|
||||
if (!core)
|
||||
if (!core) {
|
||||
req->rate = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
clk_core_init_rate_req(core, req);
|
||||
|
||||
@ -2927,6 +2929,17 @@ static int __clk_core_init(struct clk_core *core)
|
||||
core->orphan = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* optional platform-specific magic
|
||||
*
|
||||
* The .init callback is not used by any of the basic clock types, but
|
||||
* exists for weird hardware that must perform initialization magic.
|
||||
* Please consider other ways of solving initialization problems before
|
||||
* using this callback, as its use is discouraged.
|
||||
*/
|
||||
if (core->ops->init)
|
||||
core->ops->init(core->hw);
|
||||
|
||||
/*
|
||||
* Set clk's accuracy. The preferred method is to use
|
||||
* .recalc_accuracy. For simple clocks and lazy developers the default
|
||||
@ -2968,38 +2981,10 @@ static int __clk_core_init(struct clk_core *core)
|
||||
core->rate = core->req_rate = rate;
|
||||
|
||||
/*
|
||||
* walk the list of orphan clocks and reparent any that newly finds a
|
||||
* parent.
|
||||
* Enable CLK_IS_CRITICAL clocks so newly added critical clocks
|
||||
* don't get accidentally disabled when walking the orphan tree and
|
||||
* reparenting clocks
|
||||
*/
|
||||
hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
|
||||
struct clk_core *parent = __clk_init_parent(orphan);
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* we could call __clk_set_parent, but that would result in a
|
||||
* redundant call to the .set_rate op, if it exists
|
||||
*/
|
||||
if (parent) {
|
||||
/* update the clk tree topology */
|
||||
flags = clk_enable_lock();
|
||||
clk_reparent(orphan, parent);
|
||||
clk_enable_unlock(flags);
|
||||
__clk_recalc_accuracies(orphan);
|
||||
__clk_recalc_rates(orphan, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* optional platform-specific magic
|
||||
*
|
||||
* The .init callback is not used by any of the basic clock types, but
|
||||
* exists for weird hardware that must perform initialization magic.
|
||||
* Please consider other ways of solving initialization problems before
|
||||
* using this callback, as its use is discouraged.
|
||||
*/
|
||||
if (core->ops->init)
|
||||
core->ops->init(core->hw);
|
||||
|
||||
if (core->flags & CLK_IS_CRITICAL) {
|
||||
unsigned long flags;
|
||||
|
||||
@ -3010,6 +2995,28 @@ static int __clk_core_init(struct clk_core *core)
|
||||
clk_enable_unlock(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* walk the list of orphan clocks and reparent any that newly finds a
|
||||
* parent.
|
||||
*/
|
||||
hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
|
||||
struct clk_core *parent = __clk_init_parent(orphan);
|
||||
|
||||
/*
|
||||
* We need to use __clk_set_parent_before() and _after() to
|
||||
* to properly migrate any prepare/enable count of the orphan
|
||||
* clock. This is important for CLK_IS_CRITICAL clocks, which
|
||||
* are enabled during init but might not have a parent yet.
|
||||
*/
|
||||
if (parent) {
|
||||
/* update the clk tree topology */
|
||||
__clk_set_parent_before(orphan, parent);
|
||||
__clk_set_parent_after(orphan, parent, NULL);
|
||||
__clk_recalc_accuracies(orphan);
|
||||
__clk_recalc_rates(orphan, 0);
|
||||
}
|
||||
}
|
||||
|
||||
kref_init(&core->ref);
|
||||
out:
|
||||
clk_pm_runtime_put(core);
|
||||
|
@ -3,10 +3,15 @@ config COMMON_CLK_AMLOGIC
|
||||
depends on OF
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
|
||||
config COMMON_CLK_REGMAP_MESON
|
||||
bool
|
||||
select REGMAP
|
||||
|
||||
config COMMON_CLK_MESON8B
|
||||
bool
|
||||
depends on COMMON_CLK_AMLOGIC
|
||||
select RESET_CONTROLLER
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
help
|
||||
Support for the clock controller on AmLogic S802 (Meson8),
|
||||
S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
|
||||
@ -16,6 +21,8 @@ config COMMON_CLK_GXBB
|
||||
bool
|
||||
depends on COMMON_CLK_AMLOGIC
|
||||
select RESET_CONTROLLER
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for the clock controller on AmLogic S905 devices, aka gxbb.
|
||||
Say Y if you want peripherals and CPU frequency scaling to work.
|
||||
@ -24,6 +31,8 @@ config COMMON_CLK_AXG
|
||||
bool
|
||||
depends on COMMON_CLK_AMLOGIC
|
||||
select RESET_CONTROLLER
|
||||
select COMMON_CLK_REGMAP_MESON
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Support for the clock controller on AmLogic A113D devices, aka axg.
|
||||
Say Y if you want peripherals and CPU frequency scaling to work.
|
||||
|
@ -2,7 +2,8 @@
|
||||
# Makefile for Meson specific clk
|
||||
#
|
||||
|
||||
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o
|
||||
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
|
||||
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
|
||||
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-regmap.o gxbb-aoclk-32k.o
|
||||
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
|
||||
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
|
||||
obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -117,8 +117,18 @@
|
||||
#define CLKID_SD_EMMC_B_CLK0_DIV 62
|
||||
#define CLKID_SD_EMMC_C_CLK0_SEL 63
|
||||
#define CLKID_SD_EMMC_C_CLK0_DIV 64
|
||||
#define CLKID_MPLL0_DIV 65
|
||||
#define CLKID_MPLL1_DIV 66
|
||||
#define CLKID_MPLL2_DIV 67
|
||||
#define CLKID_MPLL3_DIV 68
|
||||
#define CLKID_MPLL_PREDIV 70
|
||||
#define CLKID_FCLK_DIV2_DIV 71
|
||||
#define CLKID_FCLK_DIV3_DIV 72
|
||||
#define CLKID_FCLK_DIV4_DIV 73
|
||||
#define CLKID_FCLK_DIV5_DIV 74
|
||||
#define CLKID_FCLK_DIV7_DIV 75
|
||||
|
||||
#define NR_CLKS 65
|
||||
#define NR_CLKS 76
|
||||
|
||||
/* include the CLKIDs that have been made part of the DT binding */
|
||||
#include <dt-bindings/clock/axg-clkc.h>
|
||||
|
@ -28,8 +28,11 @@
|
||||
#include <linux/clk-provider.h>
|
||||
#include "clkc.h"
|
||||
|
||||
#define to_meson_clk_audio_divider(_hw) container_of(_hw, \
|
||||
struct meson_clk_audio_divider, hw)
|
||||
static inline struct meson_clk_audio_div_data *
|
||||
meson_clk_audio_div_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct meson_clk_audio_div_data *)clk->data;
|
||||
}
|
||||
|
||||
static int _div_round(unsigned long parent_rate, unsigned long rate,
|
||||
unsigned long flags)
|
||||
@ -45,15 +48,9 @@ static int _get_val(unsigned long parent_rate, unsigned long rate)
|
||||
return DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
|
||||
}
|
||||
|
||||
static int _valid_divider(struct clk_hw *hw, int divider)
|
||||
static int _valid_divider(unsigned int width, int divider)
|
||||
{
|
||||
struct meson_clk_audio_divider *adiv =
|
||||
to_meson_clk_audio_divider(hw);
|
||||
int max_divider;
|
||||
u8 width;
|
||||
|
||||
width = adiv->div.width;
|
||||
max_divider = 1 << width;
|
||||
int max_divider = 1 << width;
|
||||
|
||||
return clamp(divider, 1, max_divider);
|
||||
}
|
||||
@ -61,14 +58,11 @@ static int _valid_divider(struct clk_hw *hw, int divider)
|
||||
static unsigned long audio_divider_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_audio_divider *adiv =
|
||||
to_meson_clk_audio_divider(hw);
|
||||
struct parm *p;
|
||||
unsigned long reg, divider;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
|
||||
unsigned long divider;
|
||||
|
||||
p = &adiv->div;
|
||||
reg = readl(adiv->base + p->reg_off);
|
||||
divider = PARM_GET(p->width, p->shift, reg) + 1;
|
||||
divider = meson_parm_read(clk->map, &adiv->div);
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)parent_rate, divider);
|
||||
}
|
||||
@ -77,14 +71,14 @@ static long audio_divider_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct meson_clk_audio_divider *adiv =
|
||||
to_meson_clk_audio_divider(hw);
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
|
||||
unsigned long max_prate;
|
||||
int divider;
|
||||
|
||||
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
|
||||
divider = _div_round(*parent_rate, rate, adiv->flags);
|
||||
divider = _valid_divider(hw, divider);
|
||||
divider = _valid_divider(adiv->div.width, divider);
|
||||
return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
|
||||
}
|
||||
|
||||
@ -93,7 +87,7 @@ static long audio_divider_round_rate(struct clk_hw *hw,
|
||||
|
||||
/* Get the corresponding rounded down divider */
|
||||
divider = max_prate / rate;
|
||||
divider = _valid_divider(hw, divider);
|
||||
divider = _valid_divider(adiv->div.width, divider);
|
||||
|
||||
/* Get actual rate of the parent */
|
||||
*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
|
||||
@ -106,28 +100,11 @@ static int audio_divider_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_audio_divider *adiv =
|
||||
to_meson_clk_audio_divider(hw);
|
||||
struct parm *p;
|
||||
unsigned long reg, flags = 0;
|
||||
int val;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
|
||||
int val = _get_val(parent_rate, rate);
|
||||
|
||||
val = _get_val(parent_rate, rate);
|
||||
|
||||
if (adiv->lock)
|
||||
spin_lock_irqsave(adiv->lock, flags);
|
||||
else
|
||||
__acquire(adiv->lock);
|
||||
|
||||
p = &adiv->div;
|
||||
reg = readl(adiv->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, val);
|
||||
writel(reg, adiv->base + p->reg_off);
|
||||
|
||||
if (adiv->lock)
|
||||
spin_unlock_irqrestore(adiv->lock, flags);
|
||||
else
|
||||
__release(adiv->lock);
|
||||
meson_parm_write(clk->map, &adiv->div, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,178 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CPU clock path:
|
||||
*
|
||||
* +-[/N]-----|3|
|
||||
* MUX2 +--[/3]-+----------|2| MUX1
|
||||
* [sys_pll]---|1| |--[/2]------------|1|-|1|
|
||||
* | |---+------------------|0| | |----- [a5_clk]
|
||||
* +--|0| | |
|
||||
* [xtal]---+-------------------------------|0|
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#define MESON_CPU_CLK_CNTL1 0x00
|
||||
#define MESON_CPU_CLK_CNTL 0x40
|
||||
|
||||
#define MESON_CPU_CLK_MUX1 BIT(7)
|
||||
#define MESON_CPU_CLK_MUX2 BIT(0)
|
||||
|
||||
#define MESON_N_WIDTH 9
|
||||
#define MESON_N_SHIFT 20
|
||||
#define MESON_SEL_WIDTH 2
|
||||
#define MESON_SEL_SHIFT 2
|
||||
|
||||
#include "clkc.h"
|
||||
|
||||
#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw)
|
||||
#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb)
|
||||
|
||||
static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
|
||||
|
||||
return divider_round_rate(hw, rate, prate, clk_cpu->div_table,
|
||||
MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST);
|
||||
}
|
||||
|
||||
static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
|
||||
unsigned int div, sel, N = 0;
|
||||
u32 reg;
|
||||
|
||||
div = DIV_ROUND_UP(parent_rate, rate);
|
||||
|
||||
if (div <= 3) {
|
||||
sel = div - 1;
|
||||
} else {
|
||||
sel = 3;
|
||||
N = div / 2;
|
||||
}
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
|
||||
reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N);
|
||||
writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
|
||||
reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel);
|
||||
writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw);
|
||||
unsigned int N, sel;
|
||||
unsigned int div = 1;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1);
|
||||
N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg);
|
||||
|
||||
reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL);
|
||||
sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg);
|
||||
|
||||
if (sel < 3)
|
||||
div = sel + 1;
|
||||
else
|
||||
div = 2 * N;
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
/* FIXME MUX1 & MUX2 should be struct clk_hw objects */
|
||||
static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu,
|
||||
struct clk_notifier_data *ndata)
|
||||
{
|
||||
u32 cpu_clk_cntl;
|
||||
|
||||
/* switch MUX1 to xtal */
|
||||
cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1;
|
||||
writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
udelay(100);
|
||||
|
||||
/* switch MUX2 to sys-pll */
|
||||
cpu_clk_cntl |= MESON_CPU_CLK_MUX2;
|
||||
writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME MUX1 & MUX2 should be struct clk_hw objects */
|
||||
static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu,
|
||||
struct clk_notifier_data *ndata)
|
||||
{
|
||||
u32 cpu_clk_cntl;
|
||||
|
||||
/* switch MUX1 to divisors' output */
|
||||
cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
cpu_clk_cntl |= MESON_CPU_CLK_MUX1;
|
||||
writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off
|
||||
+ MESON_CPU_CLK_CNTL);
|
||||
udelay(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This clock notifier is called when the frequency of the of the parent
|
||||
* PLL clock is to be changed. We use the xtal input as temporary parent
|
||||
* while the PLL frequency is stabilized.
|
||||
*/
|
||||
int meson_clk_cpu_notifier_cb(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb);
|
||||
int ret = 0;
|
||||
|
||||
if (event == PRE_RATE_CHANGE)
|
||||
ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata);
|
||||
else if (event == POST_RATE_CHANGE)
|
||||
ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata);
|
||||
|
||||
return notifier_from_errno(ret);
|
||||
}
|
||||
|
||||
const struct clk_ops meson_clk_cpu_ops = {
|
||||
.recalc_rate = meson_clk_cpu_recalc_rate,
|
||||
.round_rate = meson_clk_cpu_round_rate,
|
||||
.set_rate = meson_clk_cpu_set_rate,
|
||||
};
|
@ -68,11 +68,15 @@
|
||||
#define N2_MIN 4
|
||||
#define N2_MAX 511
|
||||
|
||||
#define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw)
|
||||
static inline struct meson_clk_mpll_data *
|
||||
meson_clk_mpll_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct meson_clk_mpll_data *)clk->data;
|
||||
}
|
||||
|
||||
static long rate_from_params(unsigned long parent_rate,
|
||||
unsigned long sdm,
|
||||
unsigned long n2)
|
||||
unsigned int sdm,
|
||||
unsigned int n2)
|
||||
{
|
||||
unsigned long divisor = (SDM_DEN * n2) + sdm;
|
||||
|
||||
@ -84,8 +88,8 @@ static long rate_from_params(unsigned long parent_rate,
|
||||
|
||||
static void params_from_rate(unsigned long requested_rate,
|
||||
unsigned long parent_rate,
|
||||
unsigned long *sdm,
|
||||
unsigned long *n2)
|
||||
unsigned int *sdm,
|
||||
unsigned int *n2)
|
||||
{
|
||||
uint64_t div = parent_rate;
|
||||
unsigned long rem = do_div(div, requested_rate);
|
||||
@ -105,31 +109,23 @@ static void params_from_rate(unsigned long requested_rate,
|
||||
static unsigned long mpll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
|
||||
struct parm *p;
|
||||
unsigned long reg, sdm, n2;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
|
||||
unsigned int sdm, n2;
|
||||
long rate;
|
||||
|
||||
p = &mpll->sdm;
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
sdm = PARM_GET(p->width, p->shift, reg);
|
||||
|
||||
p = &mpll->n2;
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
n2 = PARM_GET(p->width, p->shift, reg);
|
||||
sdm = meson_parm_read(clk->map, &mpll->sdm);
|
||||
n2 = meson_parm_read(clk->map, &mpll->n2);
|
||||
|
||||
rate = rate_from_params(parent_rate, sdm, n2);
|
||||
if (rate < 0)
|
||||
return 0;
|
||||
|
||||
return rate;
|
||||
return rate < 0 ? 0 : rate;
|
||||
}
|
||||
|
||||
static long mpll_round_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long sdm, n2;
|
||||
unsigned int sdm, n2;
|
||||
|
||||
params_from_rate(rate, *parent_rate, &sdm, &n2);
|
||||
return rate_from_params(*parent_rate, sdm, n2);
|
||||
@ -139,9 +135,9 @@ static int mpll_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
|
||||
struct parm *p;
|
||||
unsigned long reg, sdm, n2;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_mpll_data *mpll = meson_clk_mpll_data(clk);
|
||||
unsigned int sdm, n2;
|
||||
unsigned long flags = 0;
|
||||
|
||||
params_from_rate(rate, parent_rate, &sdm, &n2);
|
||||
@ -151,27 +147,20 @@ static int mpll_set_rate(struct clk_hw *hw,
|
||||
else
|
||||
__acquire(mpll->lock);
|
||||
|
||||
p = &mpll->sdm;
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, sdm);
|
||||
writel(reg, mpll->base + p->reg_off);
|
||||
/* Enable and set the fractional part */
|
||||
meson_parm_write(clk->map, &mpll->sdm, sdm);
|
||||
meson_parm_write(clk->map, &mpll->sdm_en, 1);
|
||||
|
||||
p = &mpll->sdm_en;
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, 1);
|
||||
writel(reg, mpll->base + p->reg_off);
|
||||
/* Set additional fractional part enable if required */
|
||||
if (MESON_PARM_APPLICABLE(&mpll->ssen))
|
||||
meson_parm_write(clk->map, &mpll->ssen, 1);
|
||||
|
||||
p = &mpll->ssen;
|
||||
if (p->width != 0) {
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, 1);
|
||||
writel(reg, mpll->base + p->reg_off);
|
||||
}
|
||||
/* Set the integer divider part */
|
||||
meson_parm_write(clk->map, &mpll->n2, n2);
|
||||
|
||||
p = &mpll->n2;
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, n2);
|
||||
writel(reg, mpll->base + p->reg_off);
|
||||
/* Set the magic misc bit if required */
|
||||
if (MESON_PARM_APPLICABLE(&mpll->misc))
|
||||
meson_parm_write(clk->map, &mpll->misc, 1);
|
||||
|
||||
if (mpll->lock)
|
||||
spin_unlock_irqrestore(mpll->lock, flags);
|
||||
@ -181,67 +170,13 @@ static int mpll_set_rate(struct clk_hw *hw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mpll_enable_core(struct clk_hw *hw, int enable)
|
||||
{
|
||||
struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
|
||||
struct parm *p;
|
||||
unsigned long reg;
|
||||
unsigned long flags = 0;
|
||||
|
||||
if (mpll->lock)
|
||||
spin_lock_irqsave(mpll->lock, flags);
|
||||
else
|
||||
__acquire(mpll->lock);
|
||||
|
||||
p = &mpll->en;
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, enable ? 1 : 0);
|
||||
writel(reg, mpll->base + p->reg_off);
|
||||
|
||||
if (mpll->lock)
|
||||
spin_unlock_irqrestore(mpll->lock, flags);
|
||||
else
|
||||
__release(mpll->lock);
|
||||
}
|
||||
|
||||
|
||||
static int mpll_enable(struct clk_hw *hw)
|
||||
{
|
||||
mpll_enable_core(hw, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mpll_disable(struct clk_hw *hw)
|
||||
{
|
||||
mpll_enable_core(hw, 0);
|
||||
}
|
||||
|
||||
static int mpll_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct meson_clk_mpll *mpll = to_meson_clk_mpll(hw);
|
||||
struct parm *p;
|
||||
unsigned long reg;
|
||||
int en;
|
||||
|
||||
p = &mpll->en;
|
||||
reg = readl(mpll->base + p->reg_off);
|
||||
en = PARM_GET(p->width, p->shift, reg);
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
const struct clk_ops meson_clk_mpll_ro_ops = {
|
||||
.recalc_rate = mpll_recalc_rate,
|
||||
.round_rate = mpll_round_rate,
|
||||
.is_enabled = mpll_is_enabled,
|
||||
};
|
||||
|
||||
const struct clk_ops meson_clk_mpll_ops = {
|
||||
.recalc_rate = mpll_recalc_rate,
|
||||
.round_rate = mpll_round_rate,
|
||||
.set_rate = mpll_set_rate,
|
||||
.enable = mpll_enable,
|
||||
.disable = mpll_disable,
|
||||
.is_enabled = mpll_is_enabled,
|
||||
};
|
||||
|
@ -2,6 +2,9 @@
|
||||
* Copyright (c) 2015 Endless Mobile, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
*
|
||||
* Copyright (c) 2018 Baylibre, SAS.
|
||||
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
@ -27,13 +30,14 @@
|
||||
* | |
|
||||
* FREF VCO
|
||||
*
|
||||
* out = (in * M / N) >> OD
|
||||
* out = in * (m + frac / frac_max) / (n << sum(ods))
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
@ -41,209 +45,213 @@
|
||||
|
||||
#include "clkc.h"
|
||||
|
||||
#define MESON_PLL_RESET BIT(29)
|
||||
#define MESON_PLL_LOCK BIT(31)
|
||||
static inline struct meson_clk_pll_data *
|
||||
meson_clk_pll_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct meson_clk_pll_data *)clk->data;
|
||||
}
|
||||
|
||||
#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
|
||||
static unsigned long __pll_params_to_rate(unsigned long parent_rate,
|
||||
const struct pll_rate_table *pllt,
|
||||
u16 frac,
|
||||
struct meson_clk_pll_data *pll)
|
||||
{
|
||||
u64 rate = (u64)parent_rate * pllt->m;
|
||||
unsigned int od = pllt->od + pllt->od2 + pllt->od3;
|
||||
|
||||
if (frac && MESON_PARM_APPLICABLE(&pll->frac)) {
|
||||
u64 frac_rate = (u64)parent_rate * frac;
|
||||
|
||||
rate += DIV_ROUND_UP_ULL(frac_rate,
|
||||
(1 << pll->frac.width));
|
||||
}
|
||||
|
||||
return DIV_ROUND_UP_ULL(rate, pllt->n << od);
|
||||
}
|
||||
|
||||
static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_pll *pll = to_meson_clk_pll(hw);
|
||||
struct parm *p;
|
||||
unsigned long parent_rate_mhz = parent_rate / 1000000;
|
||||
unsigned long rate_mhz;
|
||||
u16 n, m, frac = 0, od, od2 = 0;
|
||||
u32 reg;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
|
||||
struct pll_rate_table pllt;
|
||||
u16 frac;
|
||||
|
||||
p = &pll->n;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
n = PARM_GET(p->width, p->shift, reg);
|
||||
pllt.n = meson_parm_read(clk->map, &pll->n);
|
||||
pllt.m = meson_parm_read(clk->map, &pll->m);
|
||||
pllt.od = meson_parm_read(clk->map, &pll->od);
|
||||
|
||||
p = &pll->m;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
m = PARM_GET(p->width, p->shift, reg);
|
||||
pllt.od2 = MESON_PARM_APPLICABLE(&pll->od2) ?
|
||||
meson_parm_read(clk->map, &pll->od2) :
|
||||
0;
|
||||
|
||||
p = &pll->od;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
od = PARM_GET(p->width, p->shift, reg);
|
||||
pllt.od3 = MESON_PARM_APPLICABLE(&pll->od3) ?
|
||||
meson_parm_read(clk->map, &pll->od3) :
|
||||
0;
|
||||
|
||||
p = &pll->od2;
|
||||
if (p->width) {
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
od2 = PARM_GET(p->width, p->shift, reg);
|
||||
frac = MESON_PARM_APPLICABLE(&pll->frac) ?
|
||||
meson_parm_read(clk->map, &pll->frac) :
|
||||
0;
|
||||
|
||||
return __pll_params_to_rate(parent_rate, &pllt, frac, pll);
|
||||
}
|
||||
|
||||
static u16 __pll_params_with_frac(unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
const struct pll_rate_table *pllt,
|
||||
struct meson_clk_pll_data *pll)
|
||||
{
|
||||
u16 frac_max = (1 << pll->frac.width);
|
||||
u64 val = (u64)rate * pllt->n;
|
||||
|
||||
val <<= pllt->od + pllt->od2 + pllt->od3;
|
||||
|
||||
if (pll->flags & CLK_MESON_PLL_ROUND_CLOSEST)
|
||||
val = DIV_ROUND_CLOSEST_ULL(val * frac_max, parent_rate);
|
||||
else
|
||||
val = div_u64(val * frac_max, parent_rate);
|
||||
|
||||
val -= pllt->m * frac_max;
|
||||
|
||||
return min((u16)val, (u16)(frac_max - 1));
|
||||
}
|
||||
|
||||
static const struct pll_rate_table *
|
||||
meson_clk_get_pll_settings(unsigned long rate,
|
||||
struct meson_clk_pll_data *pll)
|
||||
{
|
||||
const struct pll_rate_table *table = pll->table;
|
||||
unsigned int i = 0;
|
||||
|
||||
if (!table)
|
||||
return NULL;
|
||||
|
||||
/* Find the first table element exceeding rate */
|
||||
while (table[i].rate && table[i].rate <= rate)
|
||||
i++;
|
||||
|
||||
if (i != 0) {
|
||||
if (MESON_PARM_APPLICABLE(&pll->frac) ||
|
||||
!(pll->flags & CLK_MESON_PLL_ROUND_CLOSEST) ||
|
||||
(abs(rate - table[i - 1].rate) <
|
||||
abs(rate - table[i].rate)))
|
||||
i--;
|
||||
}
|
||||
|
||||
p = &pll->frac;
|
||||
if (p->width) {
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
frac = PARM_GET(p->width, p->shift, reg);
|
||||
rate_mhz = (parent_rate_mhz * m + \
|
||||
(parent_rate_mhz * frac >> 12)) * 2 / n;
|
||||
rate_mhz = rate_mhz >> od >> od2;
|
||||
} else
|
||||
rate_mhz = (parent_rate_mhz * m / n) >> od >> od2;
|
||||
|
||||
return rate_mhz * 1000000;
|
||||
return (struct pll_rate_table *)&table[i];
|
||||
}
|
||||
|
||||
static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct meson_clk_pll *pll = to_meson_clk_pll(hw);
|
||||
const struct pll_rate_table *rate_table = pll->rate_table;
|
||||
int i;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
|
||||
const struct pll_rate_table *pllt =
|
||||
meson_clk_get_pll_settings(rate, pll);
|
||||
u16 frac;
|
||||
|
||||
for (i = 0; i < pll->rate_count; i++) {
|
||||
if (rate <= rate_table[i].rate)
|
||||
return rate_table[i].rate;
|
||||
}
|
||||
if (!pllt)
|
||||
return meson_clk_pll_recalc_rate(hw, *parent_rate);
|
||||
|
||||
/* else return the smallest value */
|
||||
return rate_table[0].rate;
|
||||
if (!MESON_PARM_APPLICABLE(&pll->frac)
|
||||
|| rate == pllt->rate)
|
||||
return pllt->rate;
|
||||
|
||||
/*
|
||||
* The rate provided by the setting is not an exact match, let's
|
||||
* try to improve the result using the fractional parameter
|
||||
*/
|
||||
frac = __pll_params_with_frac(rate, *parent_rate, pllt, pll);
|
||||
|
||||
return __pll_params_to_rate(*parent_rate, pllt, frac, pll);
|
||||
}
|
||||
|
||||
static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll,
|
||||
unsigned long rate)
|
||||
{
|
||||
const struct pll_rate_table *rate_table = pll->rate_table;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pll->rate_count; i++) {
|
||||
if (rate == rate_table[i].rate)
|
||||
return &rate_table[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Specific wait loop for GXL/GXM GP0 PLL */
|
||||
static int meson_clk_pll_wait_lock_reset(struct meson_clk_pll *pll,
|
||||
struct parm *p_n)
|
||||
{
|
||||
int delay = 100;
|
||||
u32 reg;
|
||||
|
||||
while (delay > 0) {
|
||||
reg = readl(pll->base + p_n->reg_off);
|
||||
writel(reg | MESON_PLL_RESET, pll->base + p_n->reg_off);
|
||||
udelay(10);
|
||||
writel(reg & ~MESON_PLL_RESET, pll->base + p_n->reg_off);
|
||||
|
||||
/* This delay comes from AMLogic tree clk-gp0-gxl driver */
|
||||
mdelay(1);
|
||||
|
||||
reg = readl(pll->base + p_n->reg_off);
|
||||
if (reg & MESON_PLL_LOCK)
|
||||
return 0;
|
||||
delay--;
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll,
|
||||
struct parm *p_n)
|
||||
static int meson_clk_pll_wait_lock(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
|
||||
int delay = 24000000;
|
||||
u32 reg;
|
||||
|
||||
while (delay > 0) {
|
||||
reg = readl(pll->base + p_n->reg_off);
|
||||
|
||||
if (reg & MESON_PLL_LOCK)
|
||||
do {
|
||||
/* Is the clock locked now ? */
|
||||
if (meson_parm_read(clk->map, &pll->l))
|
||||
return 0;
|
||||
|
||||
delay--;
|
||||
}
|
||||
} while (delay > 0);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void meson_clk_pll_init_params(struct meson_clk_pll *pll)
|
||||
static void meson_clk_pll_init(struct clk_hw *hw)
|
||||
{
|
||||
int i;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
|
||||
|
||||
for (i = 0 ; i < pll->params.params_count ; ++i)
|
||||
writel(pll->params.params_table[i].value,
|
||||
pll->base + pll->params.params_table[i].reg_off);
|
||||
if (pll->init_count) {
|
||||
meson_parm_write(clk->map, &pll->rst, 1);
|
||||
regmap_multi_reg_write(clk->map, pll->init_regs,
|
||||
pll->init_count);
|
||||
meson_parm_write(clk->map, &pll->rst, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct meson_clk_pll *pll = to_meson_clk_pll(hw);
|
||||
struct parm *p;
|
||||
const struct pll_rate_table *rate_set;
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct meson_clk_pll_data *pll = meson_clk_pll_data(clk);
|
||||
const struct pll_rate_table *pllt;
|
||||
unsigned long old_rate;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
u16 frac = 0;
|
||||
|
||||
if (parent_rate == 0 || rate == 0)
|
||||
return -EINVAL;
|
||||
|
||||
old_rate = rate;
|
||||
|
||||
rate_set = meson_clk_get_pll_settings(pll, rate);
|
||||
if (!rate_set)
|
||||
pllt = meson_clk_get_pll_settings(rate, pll);
|
||||
if (!pllt)
|
||||
return -EINVAL;
|
||||
|
||||
/* Initialize the PLL in a clean state if specified */
|
||||
if (pll->params.params_count)
|
||||
meson_clk_pll_init_params(pll);
|
||||
/* Put the pll in reset to write the params */
|
||||
meson_parm_write(clk->map, &pll->rst, 1);
|
||||
|
||||
/* PLL reset */
|
||||
p = &pll->n;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
/* If no_init_reset is provided, avoid resetting at this point */
|
||||
if (!pll->params.no_init_reset)
|
||||
writel(reg | MESON_PLL_RESET, pll->base + p->reg_off);
|
||||
meson_parm_write(clk->map, &pll->n, pllt->n);
|
||||
meson_parm_write(clk->map, &pll->m, pllt->m);
|
||||
meson_parm_write(clk->map, &pll->od, pllt->od);
|
||||
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->n);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
if (MESON_PARM_APPLICABLE(&pll->od2))
|
||||
meson_parm_write(clk->map, &pll->od2, pllt->od2);
|
||||
|
||||
p = &pll->m;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->m);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
if (MESON_PARM_APPLICABLE(&pll->od3))
|
||||
meson_parm_write(clk->map, &pll->od3, pllt->od3);
|
||||
|
||||
p = &pll->od;
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->od);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
|
||||
p = &pll->od2;
|
||||
if (p->width) {
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->od2);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
if (MESON_PARM_APPLICABLE(&pll->frac)) {
|
||||
frac = __pll_params_with_frac(rate, parent_rate, pllt, pll);
|
||||
meson_parm_write(clk->map, &pll->frac, frac);
|
||||
}
|
||||
|
||||
p = &pll->frac;
|
||||
if (p->width) {
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
reg = PARM_SET(p->width, p->shift, reg, rate_set->frac);
|
||||
writel(reg, pll->base + p->reg_off);
|
||||
}
|
||||
/* make sure the reset is cleared at this point */
|
||||
meson_parm_write(clk->map, &pll->rst, 0);
|
||||
|
||||
p = &pll->n;
|
||||
/* If clear_reset_for_lock is provided, remove the reset bit here */
|
||||
if (pll->params.clear_reset_for_lock) {
|
||||
reg = readl(pll->base + p->reg_off);
|
||||
writel(reg & ~MESON_PLL_RESET, pll->base + p->reg_off);
|
||||
}
|
||||
|
||||
/* If reset_lock_loop, use a special loop including resetting */
|
||||
if (pll->params.reset_lock_loop)
|
||||
ret = meson_clk_pll_wait_lock_reset(pll, p);
|
||||
else
|
||||
ret = meson_clk_pll_wait_lock(pll, p);
|
||||
if (ret) {
|
||||
if (meson_clk_pll_wait_lock(hw)) {
|
||||
pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
|
||||
__func__, old_rate);
|
||||
/*
|
||||
* FIXME: Do we really need/want this HACK ?
|
||||
* It looks unsafe. what happens if the clock gets into a
|
||||
* broken state and we can't lock back on the old_rate ? Looks
|
||||
* like an infinite recursion is possible
|
||||
*/
|
||||
meson_clk_pll_set_rate(hw, old_rate, parent_rate);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops meson_clk_pll_ops = {
|
||||
.init = meson_clk_pll_init,
|
||||
.recalc_rate = meson_clk_pll_recalc_rate,
|
||||
.round_rate = meson_clk_pll_round_rate,
|
||||
.set_rate = meson_clk_pll_set_rate,
|
||||
|
166
drivers/clk/meson/clk-regmap.c
Normal file
166
drivers/clk/meson/clk-regmap.c
Normal file
@ -0,0 +1,166 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018 BayLibre, SAS.
|
||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
|
||||
#include "clk-regmap.h"
|
||||
|
||||
static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
|
||||
int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
|
||||
|
||||
set ^= enable;
|
||||
|
||||
return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx),
|
||||
set ? BIT(gate->bit_idx) : 0);
|
||||
}
|
||||
|
||||
static int clk_regmap_gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
return clk_regmap_gate_endisable(hw, 1);
|
||||
}
|
||||
|
||||
static void clk_regmap_gate_disable(struct clk_hw *hw)
|
||||
{
|
||||
clk_regmap_gate_endisable(hw, 0);
|
||||
}
|
||||
|
||||
static int clk_regmap_gate_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(clk->map, gate->offset, &val);
|
||||
if (gate->flags & CLK_GATE_SET_TO_DISABLE)
|
||||
val ^= BIT(gate->bit_idx);
|
||||
|
||||
val &= BIT(gate->bit_idx);
|
||||
|
||||
return val ? 1 : 0;
|
||||
}
|
||||
|
||||
const struct clk_ops clk_regmap_gate_ops = {
|
||||
.enable = clk_regmap_gate_enable,
|
||||
.disable = clk_regmap_gate_disable,
|
||||
.is_enabled = clk_regmap_gate_is_enabled,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_gate_ops);
|
||||
|
||||
static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long prate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(clk->map, div->offset, &val);
|
||||
if (ret)
|
||||
/* Gives a hint that something is wrong */
|
||||
return 0;
|
||||
|
||||
val >>= div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
return divider_recalc_rate(hw, prate, val, div->table, div->flags,
|
||||
div->width);
|
||||
}
|
||||
|
||||
static long clk_regmap_div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* if read only, just return current value */
|
||||
if (div->flags & CLK_DIVIDER_READ_ONLY) {
|
||||
ret = regmap_read(clk->map, div->offset, &val);
|
||||
if (ret)
|
||||
/* Gives a hint that something is wrong */
|
||||
return 0;
|
||||
|
||||
val >>= div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
|
||||
return divider_ro_round_rate(hw, rate, prate, div->table,
|
||||
div->width, div->flags, val);
|
||||
}
|
||||
|
||||
return divider_round_rate(hw, rate, prate, div->table, div->width,
|
||||
div->flags);
|
||||
}
|
||||
|
||||
static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = divider_get_val(rate, parent_rate, div->table, div->width,
|
||||
div->flags);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = (unsigned int)ret << div->shift;
|
||||
return regmap_update_bits(clk->map, div->offset,
|
||||
clk_div_mask(div->width) << div->shift, val);
|
||||
};
|
||||
|
||||
/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */
|
||||
|
||||
const struct clk_ops clk_regmap_divider_ops = {
|
||||
.recalc_rate = clk_regmap_div_recalc_rate,
|
||||
.round_rate = clk_regmap_div_round_rate,
|
||||
.set_rate = clk_regmap_div_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_divider_ops);
|
||||
|
||||
const struct clk_ops clk_regmap_divider_ro_ops = {
|
||||
.recalc_rate = clk_regmap_div_recalc_rate,
|
||||
.round_rate = clk_regmap_div_round_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops);
|
||||
|
||||
static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(clk->map, mux->offset, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val >>= mux->shift;
|
||||
val &= mux->mask;
|
||||
return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
|
||||
}
|
||||
|
||||
static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_regmap *clk = to_clk_regmap(hw);
|
||||
struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
|
||||
unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index);
|
||||
|
||||
return regmap_update_bits(clk->map, mux->offset,
|
||||
mux->mask << mux->shift,
|
||||
val << mux->shift);
|
||||
}
|
||||
|
||||
const struct clk_ops clk_regmap_mux_ops = {
|
||||
.get_parent = clk_regmap_mux_get_parent,
|
||||
.set_parent = clk_regmap_mux_set_parent,
|
||||
.determine_rate = __clk_mux_determine_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
|
||||
|
||||
const struct clk_ops clk_regmap_mux_ro_ops = {
|
||||
.get_parent = clk_regmap_mux_get_parent,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
|
111
drivers/clk/meson/clk-regmap.h
Normal file
111
drivers/clk/meson/clk-regmap.h
Normal file
@ -0,0 +1,111 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2018 BayLibre, SAS.
|
||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
|
||||
|
||||
#ifndef __CLK_REGMAP_H
|
||||
#define __CLK_REGMAP_H
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/**
|
||||
* struct clk_regmap - regmap backed clock
|
||||
*
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @map: pointer to the regmap structure controlling the clock
|
||||
* @data: data specific to the clock type
|
||||
*
|
||||
* Clock which is controlled by regmap backed registers. The actual type of
|
||||
* of the clock is controlled by the clock_ops and data.
|
||||
*/
|
||||
struct clk_regmap {
|
||||
struct clk_hw hw;
|
||||
struct regmap *map;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw)
|
||||
|
||||
/**
|
||||
* struct clk_regmap_gate_data - regmap backed gate specific data
|
||||
*
|
||||
* @offset: offset of the register controlling gate
|
||||
* @bit_idx: single bit controlling gate
|
||||
* @flags: hardware-specific flags
|
||||
*
|
||||
* Flags:
|
||||
* Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
|
||||
*/
|
||||
struct clk_regmap_gate_data {
|
||||
unsigned int offset;
|
||||
u8 bit_idx;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
static inline struct clk_regmap_gate_data *
|
||||
clk_get_regmap_gate_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct clk_regmap_gate_data *)clk->data;
|
||||
}
|
||||
|
||||
extern const struct clk_ops clk_regmap_gate_ops;
|
||||
|
||||
/**
|
||||
* struct clk_regmap_div_data - regmap backed adjustable divider specific data
|
||||
*
|
||||
* @offset: offset of the register controlling the divider
|
||||
* @shift: shift to the divider bit field
|
||||
* @width: width of the divider bit field
|
||||
* @table: array of value/divider pairs, last entry should have div = 0
|
||||
*
|
||||
* Flags:
|
||||
* Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
|
||||
*/
|
||||
struct clk_regmap_div_data {
|
||||
unsigned int offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 flags;
|
||||
const struct clk_div_table *table;
|
||||
};
|
||||
|
||||
static inline struct clk_regmap_div_data *
|
||||
clk_get_regmap_div_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct clk_regmap_div_data *)clk->data;
|
||||
}
|
||||
|
||||
extern const struct clk_ops clk_regmap_divider_ops;
|
||||
extern const struct clk_ops clk_regmap_divider_ro_ops;
|
||||
|
||||
/**
|
||||
* struct clk_regmap_mux_data - regmap backed multiplexer clock specific data
|
||||
*
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @offset: offset of theregister controlling multiplexer
|
||||
* @table: array of parent indexed register values
|
||||
* @shift: shift to multiplexer bit field
|
||||
* @mask: mask of mutliplexer bit field
|
||||
* @flags: hardware-specific flags
|
||||
*
|
||||
* Flags:
|
||||
* Same as clk_divider except CLK_MUX_HIWORD_MASK which is ignored
|
||||
*/
|
||||
struct clk_regmap_mux_data {
|
||||
unsigned int offset;
|
||||
u32 *table;
|
||||
u32 mask;
|
||||
u8 shift;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
static inline struct clk_regmap_mux_data *
|
||||
clk_get_regmap_mux_data(struct clk_regmap *clk)
|
||||
{
|
||||
return (struct clk_regmap_mux_data *)clk->data;
|
||||
}
|
||||
|
||||
extern const struct clk_ops clk_regmap_mux_ops;
|
||||
extern const struct clk_ops clk_regmap_mux_ro_ops;
|
||||
|
||||
#endif /* __CLK_REGMAP_H */
|
@ -18,6 +18,9 @@
|
||||
#ifndef __CLKC_H
|
||||
#define __CLKC_H
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include "clk-regmap.h"
|
||||
|
||||
#define PMASK(width) GENMASK(width - 1, 0)
|
||||
#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift)
|
||||
#define CLRPMASK(width, shift) (~SETPMASK(width, shift))
|
||||
@ -35,13 +38,29 @@ struct parm {
|
||||
u8 width;
|
||||
};
|
||||
|
||||
static inline unsigned int meson_parm_read(struct regmap *map, struct parm *p)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(map, p->reg_off, &val);
|
||||
return PARM_GET(p->width, p->shift, val);
|
||||
}
|
||||
|
||||
static inline void meson_parm_write(struct regmap *map, struct parm *p,
|
||||
unsigned int val)
|
||||
{
|
||||
regmap_update_bits(map, p->reg_off, SETPMASK(p->width, p->shift),
|
||||
val << p->shift);
|
||||
}
|
||||
|
||||
|
||||
struct pll_rate_table {
|
||||
unsigned long rate;
|
||||
u16 m;
|
||||
u16 n;
|
||||
u16 od;
|
||||
u16 od2;
|
||||
u16 frac;
|
||||
u16 od3;
|
||||
};
|
||||
|
||||
#define PLL_RATE(_r, _m, _n, _od) \
|
||||
@ -50,97 +69,53 @@ struct pll_rate_table {
|
||||
.m = (_m), \
|
||||
.n = (_n), \
|
||||
.od = (_od), \
|
||||
} \
|
||||
|
||||
#define PLL_FRAC_RATE(_r, _m, _n, _od, _od2, _frac) \
|
||||
{ \
|
||||
.rate = (_r), \
|
||||
.m = (_m), \
|
||||
.n = (_n), \
|
||||
.od = (_od), \
|
||||
.od2 = (_od2), \
|
||||
.frac = (_frac), \
|
||||
} \
|
||||
|
||||
struct pll_params_table {
|
||||
unsigned int reg_off;
|
||||
unsigned int value;
|
||||
};
|
||||
|
||||
#define PLL_PARAM(_reg, _val) \
|
||||
{ \
|
||||
.reg_off = (_reg), \
|
||||
.value = (_val), \
|
||||
}
|
||||
|
||||
struct pll_setup_params {
|
||||
struct pll_params_table *params_table;
|
||||
unsigned int params_count;
|
||||
/* Workaround for GP0, do not reset before configuring */
|
||||
bool no_init_reset;
|
||||
/* Workaround for GP0, unreset right before checking for lock */
|
||||
bool clear_reset_for_lock;
|
||||
/* Workaround for GXL GP0, reset in the lock checking loop */
|
||||
bool reset_lock_loop;
|
||||
};
|
||||
#define CLK_MESON_PLL_ROUND_CLOSEST BIT(0)
|
||||
|
||||
struct meson_clk_pll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
struct meson_clk_pll_data {
|
||||
struct parm m;
|
||||
struct parm n;
|
||||
struct parm frac;
|
||||
struct parm od;
|
||||
struct parm od2;
|
||||
const struct pll_setup_params params;
|
||||
const struct pll_rate_table *rate_table;
|
||||
unsigned int rate_count;
|
||||
spinlock_t *lock;
|
||||
struct parm od3;
|
||||
struct parm l;
|
||||
struct parm rst;
|
||||
const struct reg_sequence *init_regs;
|
||||
unsigned int init_count;
|
||||
const struct pll_rate_table *table;
|
||||
u8 flags;
|
||||
};
|
||||
|
||||
#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw)
|
||||
|
||||
struct meson_clk_cpu {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
u16 reg_off;
|
||||
struct notifier_block clk_nb;
|
||||
const struct clk_div_table *div_table;
|
||||
};
|
||||
|
||||
int meson_clk_cpu_notifier_cb(struct notifier_block *nb, unsigned long event,
|
||||
void *data);
|
||||
|
||||
struct meson_clk_mpll {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
struct meson_clk_mpll_data {
|
||||
struct parm sdm;
|
||||
struct parm sdm_en;
|
||||
struct parm n2;
|
||||
struct parm en;
|
||||
struct parm ssen;
|
||||
struct parm misc;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
struct meson_clk_audio_divider {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
struct meson_clk_audio_div_data {
|
||||
struct parm div;
|
||||
u8 flags;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define MESON_GATE(_name, _reg, _bit) \
|
||||
struct clk_gate _name = { \
|
||||
.reg = (void __iomem *) _reg, \
|
||||
.bit_idx = (_bit), \
|
||||
.lock = &meson_clk_lock, \
|
||||
.hw.init = &(struct clk_init_data) { \
|
||||
.name = #_name, \
|
||||
.ops = &clk_gate_ops, \
|
||||
struct clk_regmap _name = { \
|
||||
.data = &(struct clk_regmap_gate_data){ \
|
||||
.offset = (_reg), \
|
||||
.bit_idx = (_bit), \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data) { \
|
||||
.name = #_name, \
|
||||
.ops = &clk_regmap_gate_ops, \
|
||||
.parent_names = (const char *[]){ "clk81" }, \
|
||||
.num_parents = 1, \
|
||||
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
|
||||
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
|
||||
}, \
|
||||
};
|
||||
|
||||
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 BayLibre, SAS.
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/regmap.h>
|
||||
#include "gxbb-aoclk.h"
|
||||
|
||||
static int aoclk_gate_regmap_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw);
|
||||
|
||||
return regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0,
|
||||
BIT(gate->bit_idx), BIT(gate->bit_idx));
|
||||
}
|
||||
|
||||
static void aoclk_gate_regmap_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw);
|
||||
|
||||
regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0,
|
||||
BIT(gate->bit_idx), 0);
|
||||
}
|
||||
|
||||
static int aoclk_gate_regmap_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(gate->regmap, AO_RTI_GEN_CNTL_REG0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (val & BIT(gate->bit_idx)) != 0;
|
||||
}
|
||||
|
||||
const struct clk_ops meson_aoclk_gate_regmap_ops = {
|
||||
.enable = aoclk_gate_regmap_enable,
|
||||
.disable = aoclk_gate_regmap_disable,
|
||||
.is_enabled = aoclk_gate_regmap_is_enabled,
|
||||
};
|
@ -62,10 +62,9 @@
|
||||
#include <linux/delay.h>
|
||||
#include <dt-bindings/clock/gxbb-aoclkc.h>
|
||||
#include <dt-bindings/reset/gxbb-aoclkc.h>
|
||||
#include "clk-regmap.h"
|
||||
#include "gxbb-aoclk.h"
|
||||
|
||||
static DEFINE_SPINLOCK(gxbb_aoclk_lock);
|
||||
|
||||
struct gxbb_aoclk_reset_controller {
|
||||
struct reset_controller_dev reset;
|
||||
unsigned int *data;
|
||||
@ -87,12 +86,14 @@ static const struct reset_control_ops gxbb_aoclk_reset_ops = {
|
||||
};
|
||||
|
||||
#define GXBB_AO_GATE(_name, _bit) \
|
||||
static struct aoclk_gate_regmap _name##_ao = { \
|
||||
.bit_idx = (_bit), \
|
||||
.lock = &gxbb_aoclk_lock, \
|
||||
static struct clk_regmap _name##_ao = { \
|
||||
.data = &(struct clk_regmap_gate_data) { \
|
||||
.offset = AO_RTI_GEN_CNTL_REG0, \
|
||||
.bit_idx = (_bit), \
|
||||
}, \
|
||||
.hw.init = &(struct clk_init_data) { \
|
||||
.name = #_name "_ao", \
|
||||
.ops = &meson_aoclk_gate_regmap_ops, \
|
||||
.ops = &clk_regmap_gate_ops, \
|
||||
.parent_names = (const char *[]){ "clk81" }, \
|
||||
.num_parents = 1, \
|
||||
.flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \
|
||||
@ -107,7 +108,6 @@ GXBB_AO_GATE(uart2, 5);
|
||||
GXBB_AO_GATE(ir_blaster, 6);
|
||||
|
||||
static struct aoclk_cec_32k cec_32k_ao = {
|
||||
.lock = &gxbb_aoclk_lock,
|
||||
.hw.init = &(struct clk_init_data) {
|
||||
.name = "cec_32k_ao",
|
||||
.ops = &meson_aoclk_cec_32k_ops,
|
||||
@ -126,7 +126,7 @@ static unsigned int gxbb_aoclk_reset[] = {
|
||||
[RESET_AO_IR_BLASTER] = 23,
|
||||
};
|
||||
|
||||
static struct aoclk_gate_regmap *gxbb_aoclk_gate[] = {
|
||||
static struct clk_regmap *gxbb_aoclk_gate[] = {
|
||||
[CLKID_AO_REMOTE] = &remote_ao,
|
||||
[CLKID_AO_I2C_MASTER] = &i2c_master_ao,
|
||||
[CLKID_AO_I2C_SLAVE] = &i2c_slave_ao,
|
||||
@ -177,10 +177,10 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev)
|
||||
* Populate regmap and register all clks
|
||||
*/
|
||||
for (clkid = 0; clkid < ARRAY_SIZE(gxbb_aoclk_gate); clkid++) {
|
||||
gxbb_aoclk_gate[clkid]->regmap = regmap;
|
||||
gxbb_aoclk_gate[clkid]->map = regmap;
|
||||
|
||||
ret = devm_clk_hw_register(dev,
|
||||
gxbb_aoclk_onecell_data.hws[clkid]);
|
||||
gxbb_aoclk_onecell_data.hws[clkid]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -17,22 +17,11 @@
|
||||
#define AO_RTC_ALT_CLK_CNTL0 0x94
|
||||
#define AO_RTC_ALT_CLK_CNTL1 0x98
|
||||
|
||||
struct aoclk_gate_regmap {
|
||||
struct clk_hw hw;
|
||||
unsigned bit_idx;
|
||||
struct regmap *regmap;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define to_aoclk_gate_regmap(_hw) \
|
||||
container_of(_hw, struct aoclk_gate_regmap, hw)
|
||||
|
||||
extern const struct clk_ops meson_aoclk_gate_regmap_ops;
|
||||
|
||||
struct aoclk_cec_32k {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -194,8 +194,18 @@
|
||||
#define CLKID_VPU_1_DIV 130
|
||||
#define CLKID_VAPB_0_DIV 134
|
||||
#define CLKID_VAPB_1_DIV 137
|
||||
#define CLKID_HDMI_PLL_PRE_MULT 141
|
||||
#define CLKID_MPLL0_DIV 142
|
||||
#define CLKID_MPLL1_DIV 143
|
||||
#define CLKID_MPLL2_DIV 144
|
||||
#define CLKID_MPLL_PREDIV 145
|
||||
#define CLKID_FCLK_DIV2_DIV 146
|
||||
#define CLKID_FCLK_DIV3_DIV 147
|
||||
#define CLKID_FCLK_DIV4_DIV 148
|
||||
#define CLKID_FCLK_DIV5_DIV 149
|
||||
#define CLKID_FCLK_DIV7_DIV 150
|
||||
|
||||
#define NR_CLKS 141
|
||||
#define NR_CLKS 151
|
||||
|
||||
/* include the CLKIDs that have been made part of the DT binding */
|
||||
#include <dt-bindings/clock/gxbb-clkc.h>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -69,7 +69,22 @@
|
||||
* will remain defined here.
|
||||
*/
|
||||
|
||||
#define CLK_NR_CLKS 96
|
||||
#define CLKID_MPLL0_DIV 96
|
||||
#define CLKID_MPLL1_DIV 97
|
||||
#define CLKID_MPLL2_DIV 98
|
||||
#define CLKID_CPU_IN_SEL 99
|
||||
#define CLKID_CPU_DIV2 100
|
||||
#define CLKID_CPU_DIV3 101
|
||||
#define CLKID_CPU_SCALE_DIV 102
|
||||
#define CLKID_CPU_SCALE_OUT_SEL 103
|
||||
#define CLKID_MPLL_PREDIV 104
|
||||
#define CLKID_FCLK_DIV2_DIV 105
|
||||
#define CLKID_FCLK_DIV3_DIV 106
|
||||
#define CLKID_FCLK_DIV4_DIV 107
|
||||
#define CLKID_FCLK_DIV5_DIV 108
|
||||
#define CLKID_FCLK_DIV7_DIV 109
|
||||
|
||||
#define CLK_NR_CLKS 110
|
||||
|
||||
/*
|
||||
* include the CLKID and RESETID that have
|
||||
|
@ -28,22 +28,14 @@ static long div_round_ro_rate(struct clk_hw *hw, unsigned long rate,
|
||||
{
|
||||
struct clk_regmap_div *divider = to_clk_regmap_div(hw);
|
||||
struct clk_regmap *clkr = ÷r->clkr;
|
||||
u32 div;
|
||||
struct clk_hw *hw_parent = clk_hw_get_parent(hw);
|
||||
u32 val;
|
||||
|
||||
regmap_read(clkr->regmap, divider->reg, &div);
|
||||
div >>= divider->shift;
|
||||
div &= BIT(divider->width) - 1;
|
||||
div += 1;
|
||||
regmap_read(clkr->regmap, divider->reg, &val);
|
||||
val >>= divider->shift;
|
||||
val &= BIT(divider->width) - 1;
|
||||
|
||||
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
|
||||
if (!hw_parent)
|
||||
return -EINVAL;
|
||||
|
||||
*prate = clk_hw_round_rate(hw_parent, rate * div);
|
||||
}
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)*prate, div);
|
||||
return divider_ro_round_rate(hw, rate, prate, NULL, divider->width,
|
||||
CLK_DIVIDER_ROUND_CLOSEST, val);
|
||||
}
|
||||
|
||||
static long div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -67,5 +67,6 @@
|
||||
#define CLKID_AO_I2C 58
|
||||
#define CLKID_SD_EMMC_B_CLK0 59
|
||||
#define CLKID_SD_EMMC_C_CLK0 60
|
||||
#define CLKID_HIFI_PLL 69
|
||||
|
||||
#endif /* __AXG_CLKC_H */
|
||||
|
@ -399,6 +399,7 @@ struct clk_divider {
|
||||
spinlock_t *lock;
|
||||
};
|
||||
|
||||
#define clk_div_mask(width) ((1 << (width)) - 1)
|
||||
#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)
|
||||
|
||||
#define CLK_DIVIDER_ONE_BASED BIT(0)
|
||||
@ -419,6 +420,10 @@ long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
|
||||
unsigned long rate, unsigned long *prate,
|
||||
const struct clk_div_table *table,
|
||||
u8 width, unsigned long flags);
|
||||
long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
|
||||
unsigned long rate, unsigned long *prate,
|
||||
const struct clk_div_table *table, u8 width,
|
||||
unsigned long flags, unsigned int val);
|
||||
int divider_get_val(unsigned long rate, unsigned long parent_rate,
|
||||
const struct clk_div_table *table, u8 width,
|
||||
unsigned long flags);
|
||||
@ -449,8 +454,9 @@ void clk_hw_unregister_divider(struct clk_hw *hw);
|
||||
*
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @reg: register controlling multiplexer
|
||||
* @table: array of register values corresponding to the parent index
|
||||
* @shift: shift to multiplexer bit field
|
||||
* @width: width of mutliplexer bit field
|
||||
* @mask: mask of mutliplexer bit field
|
||||
* @flags: hardware-specific flags
|
||||
* @lock: register lock
|
||||
*
|
||||
@ -510,6 +516,10 @@ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
|
||||
void __iomem *reg, u8 shift, u32 mask,
|
||||
u8 clk_mux_flags, u32 *table, spinlock_t *lock);
|
||||
|
||||
int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
|
||||
unsigned int val);
|
||||
unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index);
|
||||
|
||||
void clk_unregister_mux(struct clk *clk);
|
||||
void clk_hw_unregister_mux(struct clk_hw *hw);
|
||||
|
||||
@ -774,6 +784,17 @@ static inline long divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
rate, prate, table, width, flags);
|
||||
}
|
||||
|
||||
static inline long divider_ro_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate,
|
||||
const struct clk_div_table *table,
|
||||
u8 width, unsigned long flags,
|
||||
unsigned int val)
|
||||
{
|
||||
return divider_ro_round_rate_parent(hw, clk_hw_get_parent(hw),
|
||||
rate, prate, table, width, flags,
|
||||
val);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME clock api without lock protection
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user