![Shawn Guo](/assets/img/avatar_default.png)
The bit 0 of PLL_CFG0 register is not powerdown on zx296718, but part of of postdiv2 field. The consequence is that functions like hw_to_idx() and zx_pll_enable() will end up tampering the postdiv2 of the PLL. Let's fix it by defining pd_bit 0xff which is obviously invalid for a bit position and having PLL driver check the validity before operating on the bit. Signed-off-by: Shawn Guo <shawn.guo@linaro.org> Reviewed-by: Jun Nie <jun.nie@linaro.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Michael Turquette <mturquette@baylibre.com>
196 lines
4.5 KiB
C
196 lines
4.5 KiB
C
/*
|
|
* Copyright 2015 Linaro Ltd.
|
|
* Copyright (C) 2014 ZTE Corporation.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __ZTE_CLK_H
|
|
#define __ZTE_CLK_H
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
#define PNAME(x) static const char *x[]
|
|
|
|
#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, \
|
|
}
|
|
|
|
struct zx_pll_config {
|
|
unsigned long rate;
|
|
u32 cfg0;
|
|
u32 cfg1;
|
|
};
|
|
|
|
struct clk_zx_pll {
|
|
struct clk_hw hw;
|
|
void __iomem *reg_base;
|
|
const struct zx_pll_config *lookup_table; /* order by rate asc */
|
|
int count;
|
|
spinlock_t *lock;
|
|
u8 pd_bit; /* power down bit */
|
|
u8 lock_bit; /* pll lock flag bit */
|
|
};
|
|
|
|
#define PLL_RATE(_rate, _cfg0, _cfg1) \
|
|
{ \
|
|
.rate = _rate, \
|
|
.cfg0 = _cfg0, \
|
|
.cfg1 = _cfg1, \
|
|
}
|
|
|
|
#define ZX_PLL(_name, _parent, _reg, _table, _pd, _lock) \
|
|
{ \
|
|
.reg_base = (void __iomem *) _reg, \
|
|
.lookup_table = _table, \
|
|
.count = ARRAY_SIZE(_table), \
|
|
.pd_bit = _pd, \
|
|
.lock_bit = _lock, \
|
|
.hw.init = CLK_HW_INIT(_name, _parent, &zx_pll_ops, \
|
|
CLK_GET_RATE_NOCACHE), \
|
|
}
|
|
|
|
/*
|
|
* The pd_bit is not available on ZX296718, so let's pass something
|
|
* bigger than 31, e.g. 0xff, to indicate that.
|
|
*/
|
|
#define ZX296718_PLL(_name, _parent, _reg, _table) \
|
|
ZX_PLL(_name, _parent, _reg, _table, 0xff, 30)
|
|
|
|
struct zx_clk_gate {
|
|
struct clk_gate gate;
|
|
u16 id;
|
|
};
|
|
|
|
#define GATE(_id, _name, _parent, _reg, _bit, _flag, _gflags) \
|
|
{ \
|
|
.gate = { \
|
|
.reg = (void __iomem *) _reg, \
|
|
.bit_idx = (_bit), \
|
|
.flags = _gflags, \
|
|
.lock = &clk_lock, \
|
|
.hw.init = CLK_HW_INIT(_name, \
|
|
_parent, \
|
|
&clk_gate_ops, \
|
|
_flag | CLK_IGNORE_UNUSED), \
|
|
}, \
|
|
.id = _id, \
|
|
}
|
|
|
|
struct zx_clk_fixed_factor {
|
|
struct clk_fixed_factor factor;
|
|
u16 id;
|
|
};
|
|
|
|
#define FFACTOR(_id, _name, _parent, _mult, _div, _flag) \
|
|
{ \
|
|
.factor = { \
|
|
.div = _div, \
|
|
.mult = _mult, \
|
|
.hw.init = CLK_HW_INIT(_name, \
|
|
_parent, \
|
|
&clk_fixed_factor_ops, \
|
|
_flag), \
|
|
}, \
|
|
.id = _id, \
|
|
}
|
|
|
|
struct zx_clk_mux {
|
|
struct clk_mux mux;
|
|
u16 id;
|
|
};
|
|
|
|
#define MUX_F(_id, _name, _parent, _reg, _shift, _width, _flag, _mflag) \
|
|
{ \
|
|
.mux = { \
|
|
.reg = (void __iomem *) _reg, \
|
|
.mask = BIT(_width) - 1, \
|
|
.shift = _shift, \
|
|
.flags = _mflag, \
|
|
.lock = &clk_lock, \
|
|
.hw.init = CLK_HW_INIT_PARENTS(_name, \
|
|
_parent, \
|
|
&clk_mux_ops, \
|
|
_flag), \
|
|
}, \
|
|
.id = _id, \
|
|
}
|
|
|
|
#define MUX(_id, _name, _parent, _reg, _shift, _width) \
|
|
MUX_F(_id, _name, _parent, _reg, _shift, _width, 0, 0)
|
|
|
|
struct zx_clk_div {
|
|
struct clk_divider div;
|
|
u16 id;
|
|
};
|
|
|
|
#define DIV_T(_id, _name, _parent, _reg, _shift, _width, _flag, _table) \
|
|
{ \
|
|
.div = { \
|
|
.reg = (void __iomem *) _reg, \
|
|
.shift = _shift, \
|
|
.width = _width, \
|
|
.flags = 0, \
|
|
.table = _table, \
|
|
.lock = &clk_lock, \
|
|
.hw.init = CLK_HW_INIT(_name, \
|
|
_parent, \
|
|
&clk_divider_ops, \
|
|
_flag), \
|
|
}, \
|
|
.id = _id, \
|
|
}
|
|
|
|
struct clk_zx_audio_divider {
|
|
struct clk_hw hw;
|
|
void __iomem *reg_base;
|
|
unsigned int rate_count;
|
|
spinlock_t *lock;
|
|
u16 id;
|
|
};
|
|
|
|
#define AUDIO_DIV(_id, _name, _parent, _reg) \
|
|
{ \
|
|
.reg_base = (void __iomem *) _reg, \
|
|
.lock = &clk_lock, \
|
|
.hw.init = CLK_HW_INIT(_name, \
|
|
_parent, \
|
|
&zx_audio_div_ops, \
|
|
0), \
|
|
.id = _id, \
|
|
}
|
|
|
|
struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
|
|
unsigned long flags, void __iomem *reg_base,
|
|
const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
|
|
|
|
struct clk_zx_audio {
|
|
struct clk_hw hw;
|
|
void __iomem *reg_base;
|
|
};
|
|
|
|
struct clk *clk_register_zx_audio(const char *name,
|
|
const char * const parent_name,
|
|
unsigned long flags, void __iomem *reg_base);
|
|
|
|
extern const struct clk_ops zx_pll_ops;
|
|
extern const struct clk_ops zx_audio_div_ops;
|
|
|
|
#endif
|