diff options
Diffstat (limited to 'drivers/clk/meson/clk-regmap.c')
| -rw-r--r-- | drivers/clk/meson/clk-regmap.c | 166 | 
1 files changed, 166 insertions, 0 deletions
| diff --git a/drivers/clk/meson/clk-regmap.c b/drivers/clk/meson/clk-regmap.c new file mode 100644 index 000000000000..3645fdb62343 --- /dev/null +++ b/drivers/clk/meson/clk-regmap.c @@ -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); | 
