188 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018 BayLibre, SAS.4 * Author: Jerome Brunet <jbrunet@baylibre.com>5 */6 7#include <linux/module.h>8#include "clk-regmap.h"9 10static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable)11{12 struct clk_regmap *clk = to_clk_regmap(hw);13 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);14 int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;15 16 set ^= enable;17 18 return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx),19 set ? BIT(gate->bit_idx) : 0);20}21 22static int clk_regmap_gate_enable(struct clk_hw *hw)23{24 return clk_regmap_gate_endisable(hw, 1);25}26 27static void clk_regmap_gate_disable(struct clk_hw *hw)28{29 clk_regmap_gate_endisable(hw, 0);30}31 32static int clk_regmap_gate_is_enabled(struct clk_hw *hw)33{34 struct clk_regmap *clk = to_clk_regmap(hw);35 struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk);36 unsigned int val;37 38 regmap_read(clk->map, gate->offset, &val);39 if (gate->flags & CLK_GATE_SET_TO_DISABLE)40 val ^= BIT(gate->bit_idx);41 42 val &= BIT(gate->bit_idx);43 44 return val ? 1 : 0;45}46 47const struct clk_ops clk_regmap_gate_ops = {48 .enable = clk_regmap_gate_enable,49 .disable = clk_regmap_gate_disable,50 .is_enabled = clk_regmap_gate_is_enabled,51};52EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ops, CLK_MESON);53 54const struct clk_ops clk_regmap_gate_ro_ops = {55 .is_enabled = clk_regmap_gate_is_enabled,56};57EXPORT_SYMBOL_NS_GPL(clk_regmap_gate_ro_ops, CLK_MESON);58 59static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw,60 unsigned long prate)61{62 struct clk_regmap *clk = to_clk_regmap(hw);63 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);64 unsigned int val;65 int ret;66 67 ret = regmap_read(clk->map, div->offset, &val);68 if (ret)69 /* Gives a hint that something is wrong */70 return 0;71 72 val >>= div->shift;73 val &= clk_div_mask(div->width);74 return divider_recalc_rate(hw, prate, val, div->table, div->flags,75 div->width);76}77 78static int clk_regmap_div_determine_rate(struct clk_hw *hw,79 struct clk_rate_request *req)80{81 struct clk_regmap *clk = to_clk_regmap(hw);82 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);83 unsigned int val;84 int ret;85 86 /* if read only, just return current value */87 if (div->flags & CLK_DIVIDER_READ_ONLY) {88 ret = regmap_read(clk->map, div->offset, &val);89 if (ret)90 return ret;91 92 val >>= div->shift;93 val &= clk_div_mask(div->width);94 95 return divider_ro_determine_rate(hw, req, div->table,96 div->width, div->flags, val);97 }98 99 return divider_determine_rate(hw, req, div->table, div->width,100 div->flags);101}102 103static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate,104 unsigned long parent_rate)105{106 struct clk_regmap *clk = to_clk_regmap(hw);107 struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);108 unsigned int val;109 int ret;110 111 ret = divider_get_val(rate, parent_rate, div->table, div->width,112 div->flags);113 if (ret < 0)114 return ret;115 116 val = (unsigned int)ret << div->shift;117 return regmap_update_bits(clk->map, div->offset,118 clk_div_mask(div->width) << div->shift, val);119};120 121/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */122 123const struct clk_ops clk_regmap_divider_ops = {124 .recalc_rate = clk_regmap_div_recalc_rate,125 .determine_rate = clk_regmap_div_determine_rate,126 .set_rate = clk_regmap_div_set_rate,127};128EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ops, CLK_MESON);129 130const struct clk_ops clk_regmap_divider_ro_ops = {131 .recalc_rate = clk_regmap_div_recalc_rate,132 .determine_rate = clk_regmap_div_determine_rate,133};134EXPORT_SYMBOL_NS_GPL(clk_regmap_divider_ro_ops, CLK_MESON);135 136static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)137{138 struct clk_regmap *clk = to_clk_regmap(hw);139 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);140 unsigned int val;141 int ret;142 143 ret = regmap_read(clk->map, mux->offset, &val);144 if (ret)145 return ret;146 147 val >>= mux->shift;148 val &= mux->mask;149 return clk_mux_val_to_index(hw, mux->table, mux->flags, val);150}151 152static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)153{154 struct clk_regmap *clk = to_clk_regmap(hw);155 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);156 unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index);157 158 return regmap_update_bits(clk->map, mux->offset,159 mux->mask << mux->shift,160 val << mux->shift);161}162 163static int clk_regmap_mux_determine_rate(struct clk_hw *hw,164 struct clk_rate_request *req)165{166 struct clk_regmap *clk = to_clk_regmap(hw);167 struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);168 169 return clk_mux_determine_rate_flags(hw, req, mux->flags);170}171 172const struct clk_ops clk_regmap_mux_ops = {173 .get_parent = clk_regmap_mux_get_parent,174 .set_parent = clk_regmap_mux_set_parent,175 .determine_rate = clk_regmap_mux_determine_rate,176};177EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ops, CLK_MESON);178 179const struct clk_ops clk_regmap_mux_ro_ops = {180 .get_parent = clk_regmap_mux_get_parent,181};182EXPORT_SYMBOL_NS_GPL(clk_regmap_mux_ro_ops, CLK_MESON);183 184MODULE_DESCRIPTION("Amlogic regmap backed clock driver");185MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");186MODULE_LICENSE("GPL");187MODULE_IMPORT_NS(CLK_MESON);188