linux/drivers/clk/meson/clk-regmap.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   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_GPL(clk_regmap_gate_ops);
  53
  54const struct clk_ops clk_regmap_gate_ro_ops = {
  55        .is_enabled = clk_regmap_gate_is_enabled,
  56};
  57EXPORT_SYMBOL_GPL(clk_regmap_gate_ro_ops);
  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 long clk_regmap_div_round_rate(struct clk_hw *hw, unsigned long rate,
  79                                      unsigned long *prate)
  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                        /* Gives a hint that something is wrong */
  91                        return 0;
  92
  93                val >>= div->shift;
  94                val &= clk_div_mask(div->width);
  95
  96                return divider_ro_round_rate(hw, rate, prate, div->table,
  97                                             div->width, div->flags, val);
  98        }
  99
 100        return divider_round_rate(hw, rate, prate, div->table, div->width,
 101                                  div->flags);
 102}
 103
 104static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate,
 105                                   unsigned long parent_rate)
 106{
 107        struct clk_regmap *clk = to_clk_regmap(hw);
 108        struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk);
 109        unsigned int val;
 110        int ret;
 111
 112        ret = divider_get_val(rate, parent_rate, div->table, div->width,
 113                              div->flags);
 114        if (ret < 0)
 115                return ret;
 116
 117        val = (unsigned int)ret << div->shift;
 118        return regmap_update_bits(clk->map, div->offset,
 119                                  clk_div_mask(div->width) << div->shift, val);
 120};
 121
 122/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */
 123
 124const struct clk_ops clk_regmap_divider_ops = {
 125        .recalc_rate = clk_regmap_div_recalc_rate,
 126        .round_rate = clk_regmap_div_round_rate,
 127        .set_rate = clk_regmap_div_set_rate,
 128};
 129EXPORT_SYMBOL_GPL(clk_regmap_divider_ops);
 130
 131const struct clk_ops clk_regmap_divider_ro_ops = {
 132        .recalc_rate = clk_regmap_div_recalc_rate,
 133        .round_rate = clk_regmap_div_round_rate,
 134};
 135EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops);
 136
 137static u8 clk_regmap_mux_get_parent(struct clk_hw *hw)
 138{
 139        struct clk_regmap *clk = to_clk_regmap(hw);
 140        struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
 141        unsigned int val;
 142        int ret;
 143
 144        ret = regmap_read(clk->map, mux->offset, &val);
 145        if (ret)
 146                return ret;
 147
 148        val >>= mux->shift;
 149        val &= mux->mask;
 150        return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
 151}
 152
 153static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index)
 154{
 155        struct clk_regmap *clk = to_clk_regmap(hw);
 156        struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
 157        unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index);
 158
 159        return regmap_update_bits(clk->map, mux->offset,
 160                                  mux->mask << mux->shift,
 161                                  val << mux->shift);
 162}
 163
 164static int clk_regmap_mux_determine_rate(struct clk_hw *hw,
 165                                         struct clk_rate_request *req)
 166{
 167        struct clk_regmap *clk = to_clk_regmap(hw);
 168        struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk);
 169
 170        return clk_mux_determine_rate_flags(hw, req, mux->flags);
 171}
 172
 173const struct clk_ops clk_regmap_mux_ops = {
 174        .get_parent = clk_regmap_mux_get_parent,
 175        .set_parent = clk_regmap_mux_set_parent,
 176        .determine_rate = clk_regmap_mux_determine_rate,
 177};
 178EXPORT_SYMBOL_GPL(clk_regmap_mux_ops);
 179
 180const struct clk_ops clk_regmap_mux_ro_ops = {
 181        .get_parent = clk_regmap_mux_get_parent,
 182};
 183EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
 184
 185MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
 186MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 187MODULE_LICENSE("GPL v2");
 188