linux/drivers/clk/uniphier/clk-uniphier-cpugear.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 Socionext Inc.
   4 *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
   5 */
   6
   7#include <linux/clk-provider.h>
   8#include <linux/device.h>
   9#include <linux/regmap.h>
  10
  11#include "clk-uniphier.h"
  12
  13#define UNIPHIER_CLK_CPUGEAR_STAT       0       /* status */
  14#define UNIPHIER_CLK_CPUGEAR_SET        4       /* set */
  15#define UNIPHIER_CLK_CPUGEAR_UPD        8       /* update */
  16#define   UNIPHIER_CLK_CPUGEAR_UPD_BIT  BIT(0)
  17
  18struct uniphier_clk_cpugear {
  19        struct clk_hw hw;
  20        struct regmap *regmap;
  21        unsigned int regbase;
  22        unsigned int mask;
  23};
  24
  25#define to_uniphier_clk_cpugear(_hw) \
  26                        container_of(_hw, struct uniphier_clk_cpugear, hw)
  27
  28static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index)
  29{
  30        struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  31        int ret;
  32        unsigned int val;
  33
  34        ret = regmap_write_bits(gear->regmap,
  35                                gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
  36                                gear->mask, index);
  37        if (ret)
  38                return ret;
  39
  40        ret = regmap_write_bits(gear->regmap,
  41                                gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
  42                                UNIPHIER_CLK_CPUGEAR_UPD_BIT,
  43                                UNIPHIER_CLK_CPUGEAR_UPD_BIT);
  44        if (ret)
  45                return ret;
  46
  47        return regmap_read_poll_timeout(gear->regmap,
  48                                gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
  49                                val, !(val & UNIPHIER_CLK_CPUGEAR_UPD_BIT),
  50                                0, 1);
  51}
  52
  53static u8 uniphier_clk_cpugear_get_parent(struct clk_hw *hw)
  54{
  55        struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
  56        int num_parents = clk_hw_get_num_parents(hw);
  57        int ret;
  58        unsigned int val;
  59
  60        ret = regmap_read(gear->regmap,
  61                          gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, &val);
  62        if (ret)
  63                return ret;
  64
  65        val &= gear->mask;
  66
  67        return val < num_parents ? val : -EINVAL;
  68}
  69
  70static const struct clk_ops uniphier_clk_cpugear_ops = {
  71        .determine_rate = __clk_mux_determine_rate,
  72        .set_parent = uniphier_clk_cpugear_set_parent,
  73        .get_parent = uniphier_clk_cpugear_get_parent,
  74};
  75
  76struct clk_hw *uniphier_clk_register_cpugear(struct device *dev,
  77                                         struct regmap *regmap,
  78                                         const char *name,
  79                                const struct uniphier_clk_cpugear_data *data)
  80{
  81        struct uniphier_clk_cpugear *gear;
  82        struct clk_init_data init;
  83        int ret;
  84
  85        gear = devm_kzalloc(dev, sizeof(*gear), GFP_KERNEL);
  86        if (!gear)
  87                return ERR_PTR(-ENOMEM);
  88
  89        init.name = name;
  90        init.ops = &uniphier_clk_cpugear_ops;
  91        init.flags = CLK_SET_RATE_PARENT;
  92        init.parent_names = data->parent_names;
  93        init.num_parents = data->num_parents;
  94
  95        gear->regmap = regmap;
  96        gear->regbase = data->regbase;
  97        gear->mask = data->mask;
  98        gear->hw.init = &init;
  99
 100        ret = devm_clk_hw_register(dev, &gear->hw);
 101        if (ret)
 102                return ERR_PTR(ret);
 103
 104        return &gear->hw;
 105}
 106