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 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_GPL(clk_regmap_divider_ops);
 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_GPL(clk_regmap_divider_ro_ops);
 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_GPL(clk_regmap_mux_ops);
 178
 179const struct clk_ops clk_regmap_mux_ro_ops = {
 180        .get_parent = clk_regmap_mux_get_parent,
 181};
 182EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops);
 183
 184MODULE_DESCRIPTION("Amlogic regmap backed clock driver");
 185MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 186MODULE_LICENSE("GPL v2");
 187