linux/drivers/clk/uniphier/clk-uniphier-mux.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2016 Socionext Inc.
   3 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation; either version 2 of the License, or
   8 * (at your option) any later version.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15
  16#include <linux/clk-provider.h>
  17#include <linux/device.h>
  18#include <linux/regmap.h>
  19
  20#include "clk-uniphier.h"
  21
  22struct uniphier_clk_mux {
  23        struct clk_hw hw;
  24        struct regmap *regmap;
  25        unsigned int reg;
  26        const unsigned int *masks;
  27        const unsigned int *vals;
  28};
  29
  30#define to_uniphier_clk_mux(_hw) container_of(_hw, struct uniphier_clk_mux, hw)
  31
  32static int uniphier_clk_mux_set_parent(struct clk_hw *hw, u8 index)
  33{
  34        struct uniphier_clk_mux *mux = to_uniphier_clk_mux(hw);
  35
  36        return regmap_write_bits(mux->regmap, mux->reg, mux->masks[index],
  37                                 mux->vals[index]);
  38}
  39
  40static u8 uniphier_clk_mux_get_parent(struct clk_hw *hw)
  41{
  42        struct uniphier_clk_mux *mux = to_uniphier_clk_mux(hw);
  43        int num_parents = clk_hw_get_num_parents(hw);
  44        int ret;
  45        unsigned int val;
  46        u8 i;
  47
  48        ret = regmap_read(mux->regmap, mux->reg, &val);
  49        if (ret)
  50                return ret;
  51
  52        for (i = 0; i < num_parents; i++)
  53                if ((mux->masks[i] & val) == mux->vals[i])
  54                        return i;
  55
  56        return -EINVAL;
  57}
  58
  59static const struct clk_ops uniphier_clk_mux_ops = {
  60        .determine_rate = __clk_mux_determine_rate,
  61        .set_parent = uniphier_clk_mux_set_parent,
  62        .get_parent = uniphier_clk_mux_get_parent,
  63};
  64
  65struct clk_hw *uniphier_clk_register_mux(struct device *dev,
  66                                         struct regmap *regmap,
  67                                         const char *name,
  68                                const struct uniphier_clk_mux_data *data)
  69{
  70        struct uniphier_clk_mux *mux;
  71        struct clk_init_data init;
  72        int ret;
  73
  74        mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
  75        if (!mux)
  76                return ERR_PTR(-ENOMEM);
  77
  78        init.name = name;
  79        init.ops = &uniphier_clk_mux_ops;
  80        init.flags = CLK_SET_RATE_PARENT;
  81        init.parent_names = data->parent_names;
  82        init.num_parents = data->num_parents,
  83
  84        mux->regmap = regmap;
  85        mux->reg = data->reg;
  86        mux->masks = data->masks;
  87        mux->vals = data->vals;
  88        mux->hw.init = &init;
  89
  90        ret = devm_clk_hw_register(dev, &mux->hw);
  91        if (ret)
  92                return ERR_PTR(ret);
  93
  94        return &mux->hw;
  95}
  96