clk: uniphier: add CPU-gear change (cpufreq) support
authorMasahiro Yamada <yamada.masahiro@socionext.com>
Wed, 7 Dec 2016 01:32:32 +0000 (10:32 +0900)
committerStephen Boyd <sboyd@codeaurora.org>
Thu, 8 Dec 2016 00:17:01 +0000 (16:17 -0800)
Core support code for CPU frequency changes, which will be used by
the generic cpufreq driver.

The register view is different from the generic clk-mux; it has
a separate status register, and an update bit to load the register
setting.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
drivers/clk/uniphier/Makefile
drivers/clk/uniphier/clk-uniphier-core.c
drivers/clk/uniphier/clk-uniphier-cpugear.c [new file with mode: 0644]
drivers/clk/uniphier/clk-uniphier.h

index f27b360..665d1d6 100644 (file)
@@ -1,8 +1,11 @@
 obj-y  += clk-uniphier-core.o
+
+obj-y  += clk-uniphier-cpugear.o
 obj-y  += clk-uniphier-fixed-factor.o
 obj-y  += clk-uniphier-fixed-rate.o
 obj-y  += clk-uniphier-gate.o
 obj-y  += clk-uniphier-mux.o
+
 obj-y  += clk-uniphier-sys.o
 obj-y  += clk-uniphier-mio.o
 obj-y  += clk-uniphier-peri.o
index 26c53f7..0007218 100644 (file)
@@ -27,6 +27,9 @@ static struct clk_hw *uniphier_clk_register(struct device *dev,
                                        const struct uniphier_clk_data *data)
 {
        switch (data->type) {
+       case UNIPHIER_CLK_TYPE_CPUGEAR:
+               return uniphier_clk_register_cpugear(dev, regmap, data->name,
+                                                    &data->data.cpugear);
        case UNIPHIER_CLK_TYPE_FIXED_FACTOR:
                return uniphier_clk_register_fixed_factor(dev, data->name,
                                                          &data->data.factor);
diff --git a/drivers/clk/uniphier/clk-uniphier-cpugear.c b/drivers/clk/uniphier/clk-uniphier-cpugear.c
new file mode 100644 (file)
index 0000000..9bff26e
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+#include "clk-uniphier.h"
+
+#define UNIPHIER_CLK_CPUGEAR_STAT      0       /* status */
+#define UNIPHIER_CLK_CPUGEAR_SET       4       /* set */
+#define UNIPHIER_CLK_CPUGEAR_UPD       8       /* update */
+#define   UNIPHIER_CLK_CPUGEAR_UPD_BIT BIT(0)
+
+struct uniphier_clk_cpugear {
+       struct clk_hw hw;
+       struct regmap *regmap;
+       unsigned int regbase;
+       unsigned int mask;
+};
+
+#define to_uniphier_clk_cpugear(_hw) \
+                       container_of(_hw, struct uniphier_clk_cpugear, hw)
+
+static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
+       int ret;
+       unsigned int val;
+
+       ret = regmap_write_bits(gear->regmap,
+                               gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
+                               gear->mask, index);
+       if (ret)
+               return ret;
+
+       ret = regmap_write_bits(gear->regmap,
+                               gear->regbase + UNIPHIER_CLK_CPUGEAR_SET,
+                               UNIPHIER_CLK_CPUGEAR_UPD_BIT,
+                               UNIPHIER_CLK_CPUGEAR_UPD_BIT);
+       if (ret)
+               return ret;
+
+       return regmap_read_poll_timeout(gear->regmap,
+                               gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD,
+                               val, !(val & UNIPHIER_CLK_CPUGEAR_UPD_BIT),
+                               0, 1);
+}
+
+static u8 uniphier_clk_cpugear_get_parent(struct clk_hw *hw)
+{
+       struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw);
+       int num_parents = clk_hw_get_num_parents(hw);
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(gear->regmap,
+                         gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, &val);
+       if (ret)
+               return ret;
+
+       val &= gear->mask;
+
+       return val < num_parents ? val : -EINVAL;
+}
+
+static const struct clk_ops uniphier_clk_cpugear_ops = {
+       .determine_rate = __clk_mux_determine_rate,
+       .set_parent = uniphier_clk_cpugear_set_parent,
+       .get_parent = uniphier_clk_cpugear_get_parent,
+};
+
+struct clk_hw *uniphier_clk_register_cpugear(struct device *dev,
+                                        struct regmap *regmap,
+                                        const char *name,
+                               const struct uniphier_clk_cpugear_data *data)
+{
+       struct uniphier_clk_cpugear *gear;
+       struct clk_init_data init;
+       int ret;
+
+       gear = devm_kzalloc(dev, sizeof(*gear), GFP_KERNEL);
+       if (!gear)
+               return ERR_PTR(-ENOMEM);
+
+       init.name = name;
+       init.ops = &uniphier_clk_cpugear_ops;
+       init.flags = CLK_SET_RATE_PARENT;
+       init.parent_names = data->parent_names;
+       init.num_parents = data->num_parents,
+
+       gear->regmap = regmap;
+       gear->regbase = data->regbase;
+       gear->mask = data->mask;
+       gear->hw.init = &init;
+
+       ret = devm_clk_hw_register(dev, &gear->hw);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return &gear->hw;
+}
index 0244dba..9707b0f 100644 (file)
@@ -20,15 +20,24 @@ struct clk_hw;
 struct device;
 struct regmap;
 
-#define UNIPHIER_CLK_MUX_MAX_PARENTS   8
+#define UNIPHIER_CLK_CPUGEAR_MAX_PARENTS       16
+#define UNIPHIER_CLK_MUX_MAX_PARENTS           8
 
 enum uniphier_clk_type {
+       UNIPHIER_CLK_TYPE_CPUGEAR,
        UNIPHIER_CLK_TYPE_FIXED_FACTOR,
        UNIPHIER_CLK_TYPE_FIXED_RATE,
        UNIPHIER_CLK_TYPE_GATE,
        UNIPHIER_CLK_TYPE_MUX,
 };
 
+struct uniphier_clk_cpugear_data {
+       const char *parent_names[UNIPHIER_CLK_CPUGEAR_MAX_PARENTS];
+       unsigned int num_parents;
+       unsigned int regbase;
+       unsigned int mask;
+};
+
 struct uniphier_clk_fixed_factor_data {
        const char *parent_name;
        unsigned int mult;
@@ -58,6 +67,7 @@ struct uniphier_clk_data {
        enum uniphier_clk_type type;
        int idx;
        union {
+               struct uniphier_clk_cpugear_data cpugear;
                struct uniphier_clk_fixed_factor_data factor;
                struct uniphier_clk_fixed_rate_data rate;
                struct uniphier_clk_gate_data gate;
@@ -90,7 +100,10 @@ struct uniphier_clk_data {
                },                                              \
        }
 
-
+struct clk_hw *uniphier_clk_register_cpugear(struct device *dev,
+                                            struct regmap *regmap,
+                                            const char *name,
+                               const struct uniphier_clk_cpugear_data *data);
 struct clk_hw *uniphier_clk_register_fixed_factor(struct device *dev,
                                                  const char *name,
                        const struct uniphier_clk_fixed_factor_data *data);