Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / clk / uniphier / clk-uniphier-cpugear.c
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
18 struct 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
28 static 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
53 static 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
70 static 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
76 struct 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 }