|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/clk-provider.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/gcd.h> | 
|  | #include <linux/spinlock.h> | 
|  |  | 
|  | #include "clk-cv18xx-ip.h" | 
|  |  | 
|  | /* GATE */ | 
|  | static inline struct cv1800_clk_gate *hw_to_cv1800_clk_gate(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); | 
|  |  | 
|  | return container_of(common, struct cv1800_clk_gate, common); | 
|  | } | 
|  |  | 
|  | static int gate_enable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw); | 
|  |  | 
|  | return cv1800_clk_setbit(&gate->common, &gate->gate); | 
|  | } | 
|  |  | 
|  | static void gate_disable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw); | 
|  |  | 
|  | cv1800_clk_clearbit(&gate->common, &gate->gate); | 
|  | } | 
|  |  | 
|  | static int gate_is_enabled(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw); | 
|  |  | 
|  | return cv1800_clk_checkbit(&gate->common, &gate->gate); | 
|  | } | 
|  |  | 
|  | static unsigned long gate_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | return parent_rate; | 
|  | } | 
|  |  | 
|  | static long gate_round_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long *parent_rate) | 
|  | { | 
|  | return *parent_rate; | 
|  | } | 
|  |  | 
|  | static int gate_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const struct clk_ops cv1800_clk_gate_ops = { | 
|  | .disable = gate_disable, | 
|  | .enable = gate_enable, | 
|  | .is_enabled = gate_is_enabled, | 
|  |  | 
|  | .recalc_rate = gate_recalc_rate, | 
|  | .round_rate = gate_round_rate, | 
|  | .set_rate = gate_set_rate, | 
|  | }; | 
|  |  | 
|  | /* DIV */ | 
|  | #define _DIV_EN_CLK_DIV_FACTOR_FIELD		BIT(3) | 
|  |  | 
|  | #define DIV_GET_EN_CLK_DIV_FACTOR(_reg) \ | 
|  | FIELD_GET(_DIV_EN_CLK_DIV_FACTOR_FIELD, _reg) | 
|  |  | 
|  | #define DIV_SET_EN_DIV_FACTOR(_reg) \ | 
|  | _CV1800_SET_FIELD(_reg, 1, _DIV_EN_CLK_DIV_FACTOR_FIELD) | 
|  |  | 
|  | static inline struct cv1800_clk_div *hw_to_cv1800_clk_div(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); | 
|  |  | 
|  | return container_of(common, struct cv1800_clk_div, common); | 
|  | } | 
|  |  | 
|  | static int div_enable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); | 
|  |  | 
|  | return cv1800_clk_setbit(&div->common, &div->gate); | 
|  | } | 
|  |  | 
|  | static void div_disable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); | 
|  |  | 
|  | cv1800_clk_clearbit(&div->common, &div->gate); | 
|  | } | 
|  |  | 
|  | static int div_is_enabled(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); | 
|  |  | 
|  | return cv1800_clk_checkbit(&div->common, &div->gate); | 
|  | } | 
|  |  | 
|  | static int div_helper_set_rate(struct cv1800_clk_common *common, | 
|  | struct cv1800_clk_regfield *div, | 
|  | unsigned long val) | 
|  | { | 
|  | unsigned long flags; | 
|  | u32 reg; | 
|  |  | 
|  | if (div->width == 0) | 
|  | return 0; | 
|  |  | 
|  | spin_lock_irqsave(common->lock, flags); | 
|  |  | 
|  | reg = readl(common->base + div->reg); | 
|  | reg = cv1800_clk_regfield_set(reg, val, div); | 
|  | if (div->initval > 0) | 
|  | reg = DIV_SET_EN_DIV_FACTOR(reg); | 
|  |  | 
|  | writel(reg, common->base + div->reg); | 
|  |  | 
|  | spin_unlock_irqrestore(common->lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static u32 div_helper_get_clockdiv(struct cv1800_clk_common *common, | 
|  | struct cv1800_clk_regfield *div) | 
|  | { | 
|  | u32 clockdiv = 1; | 
|  | u32 reg; | 
|  |  | 
|  | if (!div || div->initval < 0 || (div->width == 0 && div->initval <= 0)) | 
|  | return 1; | 
|  |  | 
|  | if (div->width == 0 && div->initval > 0) | 
|  | return div->initval; | 
|  |  | 
|  | reg = readl(common->base + div->reg); | 
|  |  | 
|  | if (div->initval == 0 || DIV_GET_EN_CLK_DIV_FACTOR(reg)) | 
|  | clockdiv = cv1800_clk_regfield_get(reg, div); | 
|  | else if (div->initval > 0) | 
|  | clockdiv = div->initval; | 
|  |  | 
|  | return clockdiv; | 
|  | } | 
|  |  | 
|  | static u32 div_helper_round_rate(struct cv1800_clk_regfield *div, | 
|  | struct clk_hw *hw, struct clk_hw *parent, | 
|  | unsigned long rate, unsigned long *prate) | 
|  | { | 
|  | if (div->width == 0) { | 
|  | if (div->initval <= 0) | 
|  | return DIV_ROUND_UP_ULL(*prate, 1); | 
|  | else | 
|  | return DIV_ROUND_UP_ULL(*prate, div->initval); | 
|  | } | 
|  |  | 
|  | return divider_round_rate_parent(hw, parent, rate, prate, NULL, | 
|  | div->width, div->flags); | 
|  | } | 
|  |  | 
|  | static long div_round_rate(struct clk_hw *parent, unsigned long *parent_rate, | 
|  | unsigned long rate, int id, void *data) | 
|  | { | 
|  | struct cv1800_clk_div *div = data; | 
|  |  | 
|  | return div_helper_round_rate(&div->div, &div->common.hw, parent, | 
|  | rate, parent_rate); | 
|  | } | 
|  |  | 
|  | static bool div_is_better_rate(struct cv1800_clk_common *common, | 
|  | unsigned long target, unsigned long now, | 
|  | unsigned long best) | 
|  | { | 
|  | if (common->features & CLK_DIVIDER_ROUND_CLOSEST) | 
|  | return abs_diff(target, now) < abs_diff(target, best); | 
|  |  | 
|  | return now <= target && now > best; | 
|  | } | 
|  |  | 
|  | static int mux_helper_determine_rate(struct cv1800_clk_common *common, | 
|  | struct clk_rate_request *req, | 
|  | long (*round)(struct clk_hw *, | 
|  | unsigned long *, | 
|  | unsigned long, | 
|  | int, | 
|  | void *), | 
|  | void *data) | 
|  | { | 
|  | unsigned long best_parent_rate = 0, best_rate = 0; | 
|  | struct clk_hw *best_parent, *hw = &common->hw; | 
|  | unsigned int i; | 
|  |  | 
|  | if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) { | 
|  | unsigned long adj_parent_rate; | 
|  |  | 
|  | best_parent = clk_hw_get_parent(hw); | 
|  | best_parent_rate = clk_hw_get_rate(best_parent); | 
|  |  | 
|  | best_rate = round(best_parent, &adj_parent_rate, | 
|  | req->rate, -1, data); | 
|  |  | 
|  | goto find; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < clk_hw_get_num_parents(hw); i++) { | 
|  | unsigned long tmp_rate, parent_rate; | 
|  | struct clk_hw *parent; | 
|  |  | 
|  | parent = clk_hw_get_parent_by_index(hw, i); | 
|  | if (!parent) | 
|  | continue; | 
|  |  | 
|  | parent_rate = clk_hw_get_rate(parent); | 
|  |  | 
|  | tmp_rate = round(parent, &parent_rate, req->rate, i, data); | 
|  |  | 
|  | if (tmp_rate == req->rate) { | 
|  | best_parent = parent; | 
|  | best_parent_rate = parent_rate; | 
|  | best_rate = tmp_rate; | 
|  | goto find; | 
|  | } | 
|  |  | 
|  | if (div_is_better_rate(common, req->rate, | 
|  | tmp_rate, best_rate)) { | 
|  | best_parent = parent; | 
|  | best_parent_rate = parent_rate; | 
|  | best_rate = tmp_rate; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (best_rate == 0) | 
|  | return -EINVAL; | 
|  |  | 
|  | find: | 
|  | req->best_parent_hw = best_parent; | 
|  | req->best_parent_rate = best_parent_rate; | 
|  | req->rate = best_rate; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int div_determine_rate(struct clk_hw *hw, | 
|  | struct clk_rate_request *req) | 
|  | { | 
|  | struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); | 
|  |  | 
|  | return mux_helper_determine_rate(&div->common, req, | 
|  | div_round_rate, div); | 
|  | } | 
|  |  | 
|  | static unsigned long div_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); | 
|  | unsigned long val; | 
|  |  | 
|  | val = div_helper_get_clockdiv(&div->common, &div->div); | 
|  | if (val == 0) | 
|  | return 0; | 
|  |  | 
|  | return divider_recalc_rate(hw, parent_rate, val, NULL, | 
|  | div->div.flags, div->div.width); | 
|  | } | 
|  |  | 
|  | static int div_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); | 
|  | unsigned long val; | 
|  |  | 
|  | val = divider_get_val(rate, parent_rate, NULL, | 
|  | div->div.width, div->div.flags); | 
|  |  | 
|  | return div_helper_set_rate(&div->common, &div->div, val); | 
|  | } | 
|  |  | 
|  | const struct clk_ops cv1800_clk_div_ops = { | 
|  | .disable = div_disable, | 
|  | .enable = div_enable, | 
|  | .is_enabled = div_is_enabled, | 
|  |  | 
|  | .determine_rate = div_determine_rate, | 
|  | .recalc_rate	= div_recalc_rate, | 
|  | .set_rate = div_set_rate, | 
|  | }; | 
|  |  | 
|  | static inline struct cv1800_clk_bypass_div * | 
|  | hw_to_cv1800_clk_bypass_div(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw); | 
|  |  | 
|  | return container_of(div, struct cv1800_clk_bypass_div, div); | 
|  | } | 
|  |  | 
|  | static long bypass_div_round_rate(struct clk_hw *parent, | 
|  | unsigned long *parent_rate, | 
|  | unsigned long rate, int id, void *data) | 
|  | { | 
|  | struct cv1800_clk_bypass_div *div = data; | 
|  |  | 
|  | if (id == -1) { | 
|  | if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) | 
|  | return *parent_rate; | 
|  | else | 
|  | return div_round_rate(parent, parent_rate, rate, | 
|  | -1, &div->div); | 
|  | } | 
|  |  | 
|  | if (id == 0) | 
|  | return *parent_rate; | 
|  |  | 
|  | return div_round_rate(parent, parent_rate, rate, id - 1, &div->div); | 
|  | } | 
|  |  | 
|  | static int bypass_div_determine_rate(struct clk_hw *hw, | 
|  | struct clk_rate_request *req) | 
|  | { | 
|  | struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); | 
|  |  | 
|  | return mux_helper_determine_rate(&div->div.common, req, | 
|  | bypass_div_round_rate, div); | 
|  | } | 
|  |  | 
|  | static unsigned long bypass_div_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); | 
|  |  | 
|  | if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) | 
|  | return parent_rate; | 
|  |  | 
|  | return div_recalc_rate(hw, parent_rate); | 
|  | } | 
|  |  | 
|  | static int bypass_div_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); | 
|  |  | 
|  | if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) | 
|  | return 0; | 
|  |  | 
|  | return div_set_rate(hw, rate, parent_rate); | 
|  | } | 
|  |  | 
|  | static u8 bypass_div_get_parent(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); | 
|  |  | 
|  | if (cv1800_clk_checkbit(&div->div.common, &div->bypass)) | 
|  | return 0; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int bypass_div_set_parent(struct clk_hw *hw, u8 index) | 
|  | { | 
|  | struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw); | 
|  |  | 
|  | if (index) | 
|  | return cv1800_clk_clearbit(&div->div.common, &div->bypass); | 
|  |  | 
|  | return cv1800_clk_setbit(&div->div.common, &div->bypass); | 
|  | } | 
|  |  | 
|  | const struct clk_ops cv1800_clk_bypass_div_ops = { | 
|  | .disable = div_disable, | 
|  | .enable = div_enable, | 
|  | .is_enabled = div_is_enabled, | 
|  |  | 
|  | .determine_rate = bypass_div_determine_rate, | 
|  | .recalc_rate = bypass_div_recalc_rate, | 
|  | .set_rate = bypass_div_set_rate, | 
|  |  | 
|  | .set_parent = bypass_div_set_parent, | 
|  | .get_parent = bypass_div_get_parent, | 
|  | }; | 
|  |  | 
|  | /* MUX */ | 
|  | static inline struct cv1800_clk_mux *hw_to_cv1800_clk_mux(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); | 
|  |  | 
|  | return container_of(common, struct cv1800_clk_mux, common); | 
|  | } | 
|  |  | 
|  | static int mux_enable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  |  | 
|  | return cv1800_clk_setbit(&mux->common, &mux->gate); | 
|  | } | 
|  |  | 
|  | static void mux_disable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  |  | 
|  | cv1800_clk_clearbit(&mux->common, &mux->gate); | 
|  | } | 
|  |  | 
|  | static int mux_is_enabled(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  |  | 
|  | return cv1800_clk_checkbit(&mux->common, &mux->gate); | 
|  | } | 
|  |  | 
|  | static long mux_round_rate(struct clk_hw *parent, unsigned long *parent_rate, | 
|  | unsigned long rate, int id, void *data) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = data; | 
|  |  | 
|  | return div_helper_round_rate(&mux->div, &mux->common.hw, parent, | 
|  | rate, parent_rate); | 
|  | } | 
|  |  | 
|  | static int mux_determine_rate(struct clk_hw *hw, | 
|  | struct clk_rate_request *req) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  |  | 
|  | return mux_helper_determine_rate(&mux->common, req, | 
|  | mux_round_rate, mux); | 
|  | } | 
|  |  | 
|  | static unsigned long mux_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  | unsigned long val; | 
|  |  | 
|  | val = div_helper_get_clockdiv(&mux->common, &mux->div); | 
|  | if (val == 0) | 
|  | return 0; | 
|  |  | 
|  | return divider_recalc_rate(hw, parent_rate, val, NULL, | 
|  | mux->div.flags, mux->div.width); | 
|  | } | 
|  |  | 
|  | static int mux_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  | unsigned long val; | 
|  |  | 
|  | val = divider_get_val(rate, parent_rate, NULL, | 
|  | mux->div.width, mux->div.flags); | 
|  |  | 
|  | return div_helper_set_rate(&mux->common, &mux->div, val); | 
|  | } | 
|  |  | 
|  | static u8 mux_get_parent(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  | u32 reg = readl(mux->common.base + mux->mux.reg); | 
|  |  | 
|  | return cv1800_clk_regfield_get(reg, &mux->mux); | 
|  | } | 
|  |  | 
|  | static int _mux_set_parent(struct cv1800_clk_mux *mux, u8 index) | 
|  | { | 
|  | u32 reg; | 
|  |  | 
|  | reg = readl(mux->common.base + mux->mux.reg); | 
|  | reg = cv1800_clk_regfield_set(reg, index, &mux->mux); | 
|  | writel(reg, mux->common.base + mux->mux.reg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int mux_set_parent(struct clk_hw *hw, u8 index) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  | unsigned long flags; | 
|  |  | 
|  | spin_lock_irqsave(mux->common.lock, flags); | 
|  |  | 
|  | _mux_set_parent(mux, index); | 
|  |  | 
|  | spin_unlock_irqrestore(mux->common.lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const struct clk_ops cv1800_clk_mux_ops = { | 
|  | .disable = mux_disable, | 
|  | .enable = mux_enable, | 
|  | .is_enabled = mux_is_enabled, | 
|  |  | 
|  | .determine_rate = mux_determine_rate, | 
|  | .recalc_rate = mux_recalc_rate, | 
|  | .set_rate = mux_set_rate, | 
|  |  | 
|  | .set_parent = mux_set_parent, | 
|  | .get_parent = mux_get_parent, | 
|  | }; | 
|  |  | 
|  | static inline struct cv1800_clk_bypass_mux * | 
|  | hw_to_cv1800_clk_bypass_mux(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw); | 
|  |  | 
|  | return container_of(mux, struct cv1800_clk_bypass_mux, mux); | 
|  | } | 
|  |  | 
|  | static long bypass_mux_round_rate(struct clk_hw *parent, | 
|  | unsigned long *parent_rate, | 
|  | unsigned long rate, int id, void *data) | 
|  | { | 
|  | struct cv1800_clk_bypass_mux *mux = data; | 
|  |  | 
|  | if (id == -1) { | 
|  | if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) | 
|  | return *parent_rate; | 
|  | else | 
|  | return mux_round_rate(parent, parent_rate, rate, | 
|  | -1, &mux->mux); | 
|  | } | 
|  |  | 
|  | if (id == 0) | 
|  | return *parent_rate; | 
|  |  | 
|  | return mux_round_rate(parent, parent_rate, rate, id - 1, &mux->mux); | 
|  | } | 
|  |  | 
|  | static int bypass_mux_determine_rate(struct clk_hw *hw, | 
|  | struct clk_rate_request *req) | 
|  | { | 
|  | struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); | 
|  |  | 
|  | return mux_helper_determine_rate(&mux->mux.common, req, | 
|  | bypass_mux_round_rate, mux); | 
|  | } | 
|  |  | 
|  | static unsigned long bypass_mux_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) | 
|  | return parent_rate; | 
|  |  | 
|  | return mux_recalc_rate(hw, parent_rate); | 
|  | } | 
|  |  | 
|  | static int bypass_mux_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) | 
|  | return 0; | 
|  |  | 
|  | return mux_set_rate(hw, rate, parent_rate); | 
|  | } | 
|  |  | 
|  | static u8 bypass_mux_get_parent(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass)) | 
|  | return 0; | 
|  |  | 
|  | return mux_get_parent(hw) + 1; | 
|  | } | 
|  |  | 
|  | static int bypass_mux_set_parent(struct clk_hw *hw, u8 index) | 
|  | { | 
|  | struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw); | 
|  |  | 
|  | if (index == 0) | 
|  | return cv1800_clk_setbit(&mux->mux.common, &mux->bypass); | 
|  |  | 
|  | return cv1800_clk_clearbit(&mux->mux.common, &mux->bypass); | 
|  | } | 
|  |  | 
|  | const struct clk_ops cv1800_clk_bypass_mux_ops = { | 
|  | .disable = mux_disable, | 
|  | .enable = mux_enable, | 
|  | .is_enabled = mux_is_enabled, | 
|  |  | 
|  | .determine_rate = bypass_mux_determine_rate, | 
|  | .recalc_rate = bypass_mux_recalc_rate, | 
|  | .set_rate = bypass_mux_set_rate, | 
|  |  | 
|  | .set_parent = bypass_mux_set_parent, | 
|  | .get_parent = bypass_mux_get_parent, | 
|  | }; | 
|  |  | 
|  | /* MMUX */ | 
|  | static inline struct cv1800_clk_mmux *hw_to_cv1800_clk_mmux(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); | 
|  |  | 
|  | return container_of(common, struct cv1800_clk_mmux, common); | 
|  | } | 
|  |  | 
|  | static u8 mmux_get_parent_id(struct cv1800_clk_mmux *mmux) | 
|  | { | 
|  | struct clk_hw *hw = &mmux->common.hw; | 
|  | struct clk_hw *parent = clk_hw_get_parent(hw); | 
|  | unsigned int i; | 
|  |  | 
|  | for (i = 0; i < clk_hw_get_num_parents(hw); i++) { | 
|  | if (parent == clk_hw_get_parent_by_index(hw, i)) | 
|  | return i; | 
|  | } | 
|  |  | 
|  | BUG(); | 
|  | } | 
|  |  | 
|  | static int mmux_enable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  |  | 
|  | return cv1800_clk_setbit(&mmux->common, &mmux->gate); | 
|  | } | 
|  |  | 
|  | static void mmux_disable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  |  | 
|  | cv1800_clk_clearbit(&mmux->common, &mmux->gate); | 
|  | } | 
|  |  | 
|  | static int mmux_is_enabled(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  |  | 
|  | return cv1800_clk_checkbit(&mmux->common, &mmux->gate); | 
|  | } | 
|  |  | 
|  | static long mmux_round_rate(struct clk_hw *parent, unsigned long *parent_rate, | 
|  | unsigned long rate, int id, void *data) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = data; | 
|  | s8 div_id; | 
|  |  | 
|  | if (id == -1) { | 
|  | if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) | 
|  | return *parent_rate; | 
|  |  | 
|  | id = mmux_get_parent_id(mmux); | 
|  | } | 
|  |  | 
|  | div_id = mmux->parent2sel[id]; | 
|  |  | 
|  | if (div_id < 0) | 
|  | return *parent_rate; | 
|  |  | 
|  | return div_helper_round_rate(&mmux->div[div_id], | 
|  | &mmux->common.hw, parent, | 
|  | rate, parent_rate); | 
|  | } | 
|  |  | 
|  | static int mmux_determine_rate(struct clk_hw *hw, | 
|  | struct clk_rate_request *req) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  |  | 
|  | return mux_helper_determine_rate(&mmux->common, req, | 
|  | mmux_round_rate, mmux); | 
|  | } | 
|  |  | 
|  | static unsigned long mmux_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  | unsigned long val; | 
|  | struct cv1800_clk_regfield *div; | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) | 
|  | return parent_rate; | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel)) | 
|  | div = &mmux->div[0]; | 
|  | else | 
|  | div = &mmux->div[1]; | 
|  |  | 
|  | val = div_helper_get_clockdiv(&mmux->common, div); | 
|  | if (val == 0) | 
|  | return 0; | 
|  |  | 
|  | return divider_recalc_rate(hw, parent_rate, val, NULL, | 
|  | div->flags, div->width); | 
|  | } | 
|  |  | 
|  | static int mmux_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  | struct cv1800_clk_regfield *div; | 
|  | unsigned long val; | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) | 
|  | return parent_rate; | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel)) | 
|  | div = &mmux->div[0]; | 
|  | else | 
|  | div = &mmux->div[1]; | 
|  |  | 
|  | val = divider_get_val(rate, parent_rate, NULL, | 
|  | div->width, div->flags); | 
|  |  | 
|  | return div_helper_set_rate(&mmux->common, div, val); | 
|  | } | 
|  |  | 
|  | static u8 mmux_get_parent(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  | struct cv1800_clk_regfield *mux; | 
|  | u32 reg; | 
|  | s8 clk_sel; | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass)) | 
|  | return 0; | 
|  |  | 
|  | if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel)) | 
|  | clk_sel = 0; | 
|  | else | 
|  | clk_sel = 1; | 
|  | mux = &mmux->mux[clk_sel]; | 
|  |  | 
|  | reg = readl(mmux->common.base + mux->reg); | 
|  |  | 
|  | return mmux->sel2parent[clk_sel][cv1800_clk_regfield_get(reg, mux)]; | 
|  | } | 
|  |  | 
|  | static int mmux_set_parent(struct clk_hw *hw, u8 index) | 
|  | { | 
|  | struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw); | 
|  | struct cv1800_clk_regfield *mux; | 
|  | unsigned long flags; | 
|  | u32 reg; | 
|  | s8 clk_sel = mmux->parent2sel[index]; | 
|  |  | 
|  | if (index == 0 || clk_sel == -1) { | 
|  | cv1800_clk_setbit(&mmux->common, &mmux->bypass); | 
|  | goto release; | 
|  | } | 
|  |  | 
|  | cv1800_clk_clearbit(&mmux->common, &mmux->bypass); | 
|  |  | 
|  | if (clk_sel) | 
|  | cv1800_clk_clearbit(&mmux->common, &mmux->clk_sel); | 
|  | else | 
|  | cv1800_clk_setbit(&mmux->common, &mmux->clk_sel); | 
|  |  | 
|  | spin_lock_irqsave(mmux->common.lock, flags); | 
|  |  | 
|  | mux = &mmux->mux[clk_sel]; | 
|  | reg = readl(mmux->common.base + mux->reg); | 
|  | reg = cv1800_clk_regfield_set(reg, index, mux); | 
|  |  | 
|  | writel(reg, mmux->common.base + mux->reg); | 
|  |  | 
|  | spin_unlock_irqrestore(mmux->common.lock, flags); | 
|  |  | 
|  | release: | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const struct clk_ops cv1800_clk_mmux_ops = { | 
|  | .disable = mmux_disable, | 
|  | .enable = mmux_enable, | 
|  | .is_enabled = mmux_is_enabled, | 
|  |  | 
|  | .determine_rate = mmux_determine_rate, | 
|  | .recalc_rate = mmux_recalc_rate, | 
|  | .set_rate = mmux_set_rate, | 
|  |  | 
|  | .set_parent = mmux_set_parent, | 
|  | .get_parent = mmux_get_parent, | 
|  | }; | 
|  |  | 
|  | /* AUDIO CLK */ | 
|  | static inline struct cv1800_clk_audio * | 
|  | hw_to_cv1800_clk_audio(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw); | 
|  |  | 
|  | return container_of(common, struct cv1800_clk_audio, common); | 
|  | } | 
|  |  | 
|  | static int aclk_enable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); | 
|  |  | 
|  | cv1800_clk_setbit(&aclk->common, &aclk->src_en); | 
|  | return cv1800_clk_setbit(&aclk->common, &aclk->output_en); | 
|  | } | 
|  |  | 
|  | static void aclk_disable(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); | 
|  |  | 
|  | cv1800_clk_clearbit(&aclk->common, &aclk->output_en); | 
|  | cv1800_clk_clearbit(&aclk->common, &aclk->src_en); | 
|  | } | 
|  |  | 
|  | static int aclk_is_enabled(struct clk_hw *hw) | 
|  | { | 
|  | struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); | 
|  |  | 
|  | return cv1800_clk_checkbit(&aclk->common, &aclk->output_en); | 
|  | } | 
|  |  | 
|  | static int aclk_determine_rate(struct clk_hw *hw, | 
|  | struct clk_rate_request *req) | 
|  | { | 
|  | struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); | 
|  |  | 
|  | req->rate = aclk->target_rate; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static unsigned long aclk_recalc_rate(struct clk_hw *hw, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); | 
|  | u64 rate = parent_rate; | 
|  | u64 factor = 2; | 
|  | u32 regval; | 
|  |  | 
|  | if (!cv1800_clk_checkbit(&aclk->common, &aclk->div_en)) | 
|  | return 0; | 
|  |  | 
|  | regval = readl(aclk->common.base + aclk->m.reg); | 
|  | factor *= cv1800_clk_regfield_get(regval, &aclk->m); | 
|  |  | 
|  | regval = readl(aclk->common.base + aclk->n.reg); | 
|  | rate *= cv1800_clk_regfield_get(regval, &aclk->n); | 
|  |  | 
|  | return DIV64_U64_ROUND_UP(rate, factor); | 
|  | } | 
|  |  | 
|  | static void aclk_determine_mn(unsigned long parent_rate, unsigned long rate, | 
|  | u32 *m, u32 *n) | 
|  | { | 
|  | u32 tm = parent_rate / 2; | 
|  | u32 tn = rate; | 
|  | u32 tcommon = gcd(tm, tn); | 
|  | *m = tm / tcommon; | 
|  | *n = tn / tcommon; | 
|  | } | 
|  |  | 
|  | static int aclk_set_rate(struct clk_hw *hw, unsigned long rate, | 
|  | unsigned long parent_rate) | 
|  | { | 
|  | struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw); | 
|  | unsigned long flags; | 
|  | u32 m, n; | 
|  |  | 
|  | aclk_determine_mn(parent_rate, rate, | 
|  | &m, &n); | 
|  |  | 
|  | spin_lock_irqsave(aclk->common.lock, flags); | 
|  |  | 
|  | writel(m, aclk->common.base + aclk->m.reg); | 
|  | writel(n, aclk->common.base + aclk->n.reg); | 
|  |  | 
|  | cv1800_clk_setbit(&aclk->common, &aclk->div_en); | 
|  | cv1800_clk_setbit(&aclk->common, &aclk->div_up); | 
|  |  | 
|  | spin_unlock_irqrestore(aclk->common.lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const struct clk_ops cv1800_clk_audio_ops = { | 
|  | .disable = aclk_disable, | 
|  | .enable = aclk_enable, | 
|  | .is_enabled = aclk_is_enabled, | 
|  |  | 
|  | .determine_rate = aclk_determine_rate, | 
|  | .recalc_rate = aclk_recalc_rate, | 
|  | .set_rate = aclk_set_rate, | 
|  | }; |