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(map: gear->regmap, |
35 | reg: gear->regbase + UNIPHIER_CLK_CPUGEAR_SET, |
36 | mask: gear->mask, val: index); |
37 | if (ret) |
38 | return ret; |
39 | |
40 | ret = regmap_write_bits(map: gear->regmap, |
41 | reg: 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(map: gear->regmap, |
61 | reg: gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, val: &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, size: sizeof(*gear), GFP_KERNEL); |
86 | if (!gear) |
87 | return ERR_PTR(error: -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, hw: &gear->hw); |
101 | if (ret) |
102 | return ERR_PTR(error: ret); |
103 | |
104 | return &gear->hw; |
105 | } |
106 | |