| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright (c) 2024 Neil Armstrong <neil.armstrong@linaro.org> | |
| */ | |
| #include <linux/module.h> | |
| #include "vclk.h" | |
| /* The VCLK gate has a supplementary reset bit to pulse after ungating */ | |
| static inline struct meson_vclk_gate_data * | |
| clk_get_meson_vclk_gate_data(struct clk_regmap *clk) | |
| { | |
| return (struct meson_vclk_gate_data *)clk->data; | |
| } | |
| static int meson_vclk_gate_enable(struct clk_hw *hw) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk); | |
| meson_parm_write(clk->map, &vclk->enable, 1); | |
| /* Do a reset pulse */ | |
| meson_parm_write(clk->map, &vclk->reset, 1); | |
| meson_parm_write(clk->map, &vclk->reset, 0); | |
| return 0; | |
| } | |
| static void meson_vclk_gate_disable(struct clk_hw *hw) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk); | |
| meson_parm_write(clk->map, &vclk->enable, 0); | |
| } | |
| static int meson_vclk_gate_is_enabled(struct clk_hw *hw) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk); | |
| return meson_parm_read(clk->map, &vclk->enable); | |
| } | |
| const struct clk_ops meson_vclk_gate_ops = { | |
| .init = clk_regmap_init, | |
| .enable = meson_vclk_gate_enable, | |
| .disable = meson_vclk_gate_disable, | |
| .is_enabled = meson_vclk_gate_is_enabled, | |
| }; | |
| EXPORT_SYMBOL_NS_GPL(meson_vclk_gate_ops, "CLK_MESON"); | |
| /* The VCLK Divider has supplementary reset & enable bits */ | |
| static inline struct meson_vclk_div_data * | |
| clk_get_meson_vclk_div_data(struct clk_regmap *clk) | |
| { | |
| return (struct meson_vclk_div_data *)clk->data; | |
| } | |
| static unsigned long meson_vclk_div_recalc_rate(struct clk_hw *hw, | |
| unsigned long prate) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); | |
| return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div), | |
| vclk->table, vclk->flags, vclk->div.width); | |
| } | |
| static int meson_vclk_div_determine_rate(struct clk_hw *hw, | |
| struct clk_rate_request *req) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); | |
| return divider_determine_rate(hw, req, vclk->table, vclk->div.width, | |
| vclk->flags); | |
| } | |
| static int meson_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate, | |
| unsigned long parent_rate) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); | |
| int ret; | |
| ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width, | |
| vclk->flags); | |
| if (ret < 0) | |
| return ret; | |
| meson_parm_write(clk->map, &vclk->div, ret); | |
| return 0; | |
| }; | |
| static int meson_vclk_div_enable(struct clk_hw *hw) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); | |
| /* Unreset the divider when ungating */ | |
| meson_parm_write(clk->map, &vclk->reset, 0); | |
| meson_parm_write(clk->map, &vclk->enable, 1); | |
| return 0; | |
| } | |
| static void meson_vclk_div_disable(struct clk_hw *hw) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); | |
| /* Reset the divider when gating */ | |
| meson_parm_write(clk->map, &vclk->enable, 0); | |
| meson_parm_write(clk->map, &vclk->reset, 1); | |
| } | |
| static int meson_vclk_div_is_enabled(struct clk_hw *hw) | |
| { | |
| struct clk_regmap *clk = to_clk_regmap(hw); | |
| struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk); | |
| return meson_parm_read(clk->map, &vclk->enable); | |
| } | |
| const struct clk_ops meson_vclk_div_ops = { | |
| .init = clk_regmap_init, | |
| .recalc_rate = meson_vclk_div_recalc_rate, | |
| .determine_rate = meson_vclk_div_determine_rate, | |
| .set_rate = meson_vclk_div_set_rate, | |
| .enable = meson_vclk_div_enable, | |
| .disable = meson_vclk_div_disable, | |
| .is_enabled = meson_vclk_div_is_enabled, | |
| }; | |
| EXPORT_SYMBOL_NS_GPL(meson_vclk_div_ops, "CLK_MESON"); | |
| MODULE_DESCRIPTION("Amlogic vclk clock driver"); | |
| MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); | |
| MODULE_LICENSE("GPL"); | |
| MODULE_IMPORT_NS("CLK_MESON"); |