@@ -15,11 +15,13 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include "clk-mtk.h"
#include "clk-mux.h"
struct mtk_clk_mux {
struct clk_hw hw;
struct regmap *regmap;
+ struct regmap *vote_regmap;
const struct mtk_mux *data;
spinlock_t *lock;
bool reparent;
@@ -30,6 +32,46 @@ static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
return container_of(hw, struct mtk_clk_mux, hw);
}
+static int mtk_clk_mux_fenc_enable_setclr(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ unsigned long flags = 0;
+ u32 val = 0;
+ int i = 0;
+ int ret = 0;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+ else
+ __acquire(mux->lock);
+
+ regmap_write(mux->regmap, mux->data->clr_ofs, BIT(mux->data->gate_shift));
+
+ while (1) {
+ regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val);
+
+ if ((val & BIT(mux->data->fenc_shift)) != 0)
+ break;
+
+ if (i < MTK_WAIT_FENC_DONE_CNT) {
+ udelay(MTK_WAIT_FENC_DONE_US);
+ } else {
+ pr_err("%s wait fenc done timeout\n", clk_hw_get_name(hw));
+ ret = -EBUSY;
+ break;
+ }
+
+ i++;
+ }
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+ else
+ __release(mux->lock);
+
+ return ret;
+}
+
static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -70,6 +112,16 @@ static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
BIT(mux->data->gate_shift));
}
+static int mtk_clk_mux_fenc_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0;
+
+ regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val);
+
+ return (val & BIT(mux->data->fenc_shift)) != 0;
+}
+
static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -80,6 +132,106 @@ static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
return (val & BIT(mux->data->gate_shift)) == 0;
}
+static int mtk_clk_vote_mux_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0;
+
+ regmap_read(mux->vote_regmap, mux->data->vote_set_ofs, &val);
+
+ return (val & BIT(mux->data->gate_shift)) != 0;
+}
+
+static int mtk_clk_vote_mux_is_done(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0;
+
+ regmap_read(mux->vote_regmap, mux->data->vote_sta_ofs, &val);
+
+ return (val & BIT(mux->data->gate_shift)) != 0;
+}
+
+static int mtk_clk_mux_vote_fenc_enable(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0, val2 = 0;
+ bool is_done = false;
+ int i = 0;
+
+ regmap_write(mux->vote_regmap, mux->data->vote_set_ofs, BIT(mux->data->gate_shift));
+
+ while (!mtk_clk_vote_mux_is_enabled(hw)) {
+ if (i < MTK_WAIT_VOTE_PREPARE_CNT) {
+ udelay(MTK_WAIT_VOTE_PREPARE_US);
+ } else {
+ pr_err("%s mux prepare timeout(%x)\n", clk_hw_get_name(hw), val);
+ return -EBUSY;
+ }
+
+ i++;
+ }
+
+ i = 0;
+
+ while (1) {
+ if (!is_done)
+ regmap_read(mux->vote_regmap, mux->data->vote_sta_ofs, &val);
+
+ if (((val & BIT(mux->data->gate_shift)) != 0))
+ is_done = true;
+
+ if (is_done) {
+ regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val2);
+ if ((val2 & BIT(mux->data->fenc_shift)) != 0)
+ break;
+ }
+
+ if (i < MTK_WAIT_VOTE_DONE_CNT) {
+ udelay(MTK_WAIT_VOTE_DONE_US);
+ } else {
+ pr_err("%s mux enable timeout(%x %x)\n", clk_hw_get_name(hw), val, val2);
+ return -EBUSY;
+ }
+
+ i++;
+ }
+
+ return 0;
+}
+
+static void mtk_clk_mux_vote_disable(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ int i = 0;
+
+ regmap_write(mux->vote_regmap, mux->data->vote_clr_ofs, BIT(mux->data->gate_shift));
+
+ while (mtk_clk_vote_mux_is_enabled(hw)) {
+ if (i < MTK_WAIT_VOTE_PREPARE_CNT) {
+ udelay(MTK_WAIT_VOTE_PREPARE_US);
+ } else {
+ pr_err("%s mux unprepare timeout\n", clk_hw_get_name(hw));
+ return;
+ }
+
+ i++;
+ }
+
+ i = 0;
+
+ while (!mtk_clk_vote_mux_is_done(hw)) {
+ if (i < MTK_WAIT_VOTE_DONE_CNT) {
+ udelay(MTK_WAIT_VOTE_DONE_US);
+ } else {
+ pr_err("%s mux disable timeout\n", clk_hw_get_name(hw));
+ return;
+ }
+
+ i++;
+ }
+}
+
static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -151,6 +303,12 @@ static int mtk_clk_mux_determine_rate(struct clk_hw *hw,
return clk_mux_determine_rate_flags(hw, req, mux->data->flags);
}
+static void mtk_clk_mux_vote_fenc_disable_unused(struct clk_hw *hw)
+{
+ mtk_clk_mux_vote_fenc_enable(hw);
+ mtk_clk_mux_vote_disable(hw);
+}
+
const struct clk_ops mtk_mux_clr_set_upd_ops = {
.get_parent = mtk_clk_mux_get_parent,
.set_parent = mtk_clk_mux_set_parent_setclr_lock,
@@ -168,9 +326,31 @@ const struct clk_ops mtk_mux_gate_clr_set_upd_ops = {
};
EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops);
+const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops = {
+ .enable = mtk_clk_mux_fenc_enable_setclr,
+ .disable = mtk_clk_mux_disable_setclr,
+ .is_enabled = mtk_clk_mux_fenc_is_enabled,
+ .get_parent = mtk_clk_mux_get_parent,
+ .set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_gate_fenc_clr_set_upd_ops);
+
+const struct clk_ops mtk_mux_vote_fenc_ops = {
+ .enable = mtk_clk_mux_vote_fenc_enable,
+ .disable = mtk_clk_mux_vote_disable,
+ .is_enabled = mtk_clk_mux_fenc_is_enabled,
+ .get_parent = mtk_clk_mux_get_parent,
+ .set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
+ .disable_unused = mtk_clk_mux_vote_fenc_disable_unused,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_vote_fenc_ops);
+
static struct clk_hw *mtk_clk_register_mux(struct device *dev,
const struct mtk_mux *mux,
struct regmap *regmap,
+ struct regmap *vote_regmap,
spinlock_t *lock)
{
struct mtk_clk_mux *clk_mux;
@@ -185,9 +365,17 @@ static struct clk_hw *mtk_clk_register_mux(struct device *dev,
init.flags = mux->flags;
init.parent_names = mux->parent_names;
init.num_parents = mux->num_parents;
- init.ops = mux->ops;
+ if (mux->flags & CLK_USE_VOTE) {
+ if (vote_regmap)
+ init.ops = mux->ops;
+ else
+ init.ops = mux->dma_ops;
+ } else {
+ init.ops = mux->ops;
+ }
clk_mux->regmap = regmap;
+ clk_mux->vote_regmap = vote_regmap;
clk_mux->data = mux;
clk_mux->lock = lock;
clk_mux->hw.init = &init;
@@ -220,6 +408,7 @@ int mtk_clk_register_muxes(struct device *dev,
struct clk_hw_onecell_data *clk_data)
{
struct regmap *regmap;
+ struct regmap *vote_regmap = NULL;
struct clk_hw *hw;
int i;
@@ -238,8 +427,13 @@ int mtk_clk_register_muxes(struct device *dev,
continue;
}
- hw = mtk_clk_register_mux(dev, mux, regmap, lock);
+ if (mux->vote_comp) {
+ vote_regmap = syscon_regmap_lookup_by_phandle(node, mux->vote_comp);
+ if (IS_ERR(vote_regmap))
+ vote_regmap = NULL;
+ }
+ hw = mtk_clk_register_mux(dev, mux, regmap, vote_regmap, lock);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", mux->name,
hw);
@@ -21,6 +21,7 @@ struct mtk_mux {
int id;
const char *name;
const char * const *parent_names;
+ const char *vote_comp;
const u8 *parent_index;
unsigned int flags;
@@ -28,13 +29,19 @@ struct mtk_mux {
u32 set_ofs;
u32 clr_ofs;
u32 upd_ofs;
+ u32 vote_set_ofs;
+ u32 vote_clr_ofs;
+ u32 vote_sta_ofs;
+ u32 fenc_sta_mon_ofs;
u8 mux_shift;
u8 mux_width;
u8 gate_shift;
s8 upd_shift;
+ u8 fenc_shift;
const struct clk_ops *ops;
+ const struct clk_ops *dma_ops;
signed char num_parents;
};
@@ -77,6 +84,8 @@ struct mtk_mux {
extern const struct clk_ops mtk_mux_clr_set_upd_ops;
extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
+extern const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops;
+extern const struct clk_ops mtk_mux_vote_fenc_ops;
#define MUX_GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _mux_ofs, \
_mux_set_ofs, _mux_clr_ofs, _shift, _width, \
@@ -118,6 +127,76 @@ extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops;
0, _upd_ofs, _upd, CLK_SET_RATE_PARENT, \
mtk_mux_clr_set_upd_ops)
+#define MUX_MULT_VOTE_FENC_FLAGS(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, _vote_comp,\
+ _vote_sta_ofs, _vote_set_ofs, _vote_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .mux_ofs = _mux_ofs, \
+ .set_ofs = _mux_set_ofs, \
+ .clr_ofs = _mux_clr_ofs, \
+ .vote_comp = _vote_comp, \
+ .vote_sta_ofs = _vote_sta_ofs, \
+ .vote_set_ofs = _vote_set_ofs, \
+ .vote_clr_ofs = _vote_clr_ofs, \
+ .upd_ofs = _upd_ofs, \
+ .fenc_sta_mon_ofs = _fenc_sta_mon_ofs, \
+ .mux_shift = _shift, \
+ .mux_width = _width, \
+ .gate_shift = _gate, \
+ .upd_shift = _upd, \
+ .fenc_shift = _fenc, \
+ .parent_names = _parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ .flags = CLK_USE_VOTE | (_flags), \
+ .ops = &mtk_mux_vote_fenc_ops, \
+ .dma_ops = &mtk_mux_gate_fenc_clr_set_upd_ops, \
+ }
+
+#define MUX_MULT_VOTE_FENC(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, _vote_comp,\
+ _vote_sta_ofs, _vote_set_ofs, _vote_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc) \
+ MUX_MULT_VOTE_FENC_FLAGS(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, _vote_comp,\
+ _vote_sta_ofs, _vote_set_ofs, _vote_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, 0)
+
+#define MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, _flags) { \
+ .id = _id, \
+ .name = _name, \
+ .mux_ofs = _mux_ofs, \
+ .set_ofs = _mux_set_ofs, \
+ .clr_ofs = _mux_clr_ofs, \
+ .upd_ofs = _upd_ofs, \
+ .fenc_sta_mon_ofs = _fenc_sta_mon_ofs, \
+ .mux_shift = _shift, \
+ .mux_width = _width, \
+ .gate_shift = _gate, \
+ .upd_shift = _upd, \
+ .fenc_shift = _fenc, \
+ .parent_names = _parents, \
+ .num_parents = ARRAY_SIZE(_parents), \
+ .flags = _flags, \
+ .ops = &mtk_mux_gate_fenc_clr_set_upd_ops, \
+ }
+
+#define MUX_GATE_FENC_CLR_SET_UPD(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc) \
+ MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \
+ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \
+ _shift, _width, _gate, _upd_ofs, _upd, \
+ _fenc_sta_mon_ofs, _fenc, 0)
+
int mtk_clk_register_muxes(struct device *dev,
const struct mtk_mux *muxes,
int num, struct device_node *node,
Add data fields, defines and ops to support voting for mux. Signed-off-by: Guangjie Song <guangjie.song@mediatek.com> --- drivers/clk/mediatek/clk-mux.c | 198 ++++++++++++++++++++++++++++++++- drivers/clk/mediatek/clk-mux.h | 79 +++++++++++++ 2 files changed, 275 insertions(+), 2 deletions(-)