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 | struct uniphier_clk_gate { |
14 | struct clk_hw hw; |
15 | struct regmap *regmap; |
16 | unsigned int reg; |
17 | unsigned int bit; |
18 | }; |
19 | |
20 | #define to_uniphier_clk_gate(_hw) \ |
21 | container_of(_hw, struct uniphier_clk_gate, hw) |
22 | |
23 | static int uniphier_clk_gate_endisable(struct clk_hw *hw, int enable) |
24 | { |
25 | struct uniphier_clk_gate *gate = to_uniphier_clk_gate(hw); |
26 | |
27 | return regmap_write_bits(map: gate->regmap, reg: gate->reg, BIT(gate->bit), |
28 | val: enable ? BIT(gate->bit) : 0); |
29 | } |
30 | |
31 | static int uniphier_clk_gate_enable(struct clk_hw *hw) |
32 | { |
33 | return uniphier_clk_gate_endisable(hw, enable: 1); |
34 | } |
35 | |
36 | static void uniphier_clk_gate_disable(struct clk_hw *hw) |
37 | { |
38 | if (uniphier_clk_gate_endisable(hw, enable: 0) < 0) |
39 | pr_warn("failed to disable clk\n" ); |
40 | } |
41 | |
42 | static int uniphier_clk_gate_is_enabled(struct clk_hw *hw) |
43 | { |
44 | struct uniphier_clk_gate *gate = to_uniphier_clk_gate(hw); |
45 | unsigned int val; |
46 | |
47 | if (regmap_read(map: gate->regmap, reg: gate->reg, val: &val) < 0) |
48 | pr_warn("is_enabled() may return wrong result\n" ); |
49 | |
50 | return !!(val & BIT(gate->bit)); |
51 | } |
52 | |
53 | static const struct clk_ops uniphier_clk_gate_ops = { |
54 | .enable = uniphier_clk_gate_enable, |
55 | .disable = uniphier_clk_gate_disable, |
56 | .is_enabled = uniphier_clk_gate_is_enabled, |
57 | }; |
58 | |
59 | struct clk_hw *uniphier_clk_register_gate(struct device *dev, |
60 | struct regmap *regmap, |
61 | const char *name, |
62 | const struct uniphier_clk_gate_data *data) |
63 | { |
64 | struct uniphier_clk_gate *gate; |
65 | struct clk_init_data init; |
66 | int ret; |
67 | |
68 | gate = devm_kzalloc(dev, size: sizeof(*gate), GFP_KERNEL); |
69 | if (!gate) |
70 | return ERR_PTR(error: -ENOMEM); |
71 | |
72 | init.name = name; |
73 | init.ops = &uniphier_clk_gate_ops; |
74 | init.flags = data->parent_name ? CLK_SET_RATE_PARENT : 0; |
75 | init.parent_names = data->parent_name ? &data->parent_name : NULL; |
76 | init.num_parents = data->parent_name ? 1 : 0; |
77 | |
78 | gate->regmap = regmap; |
79 | gate->reg = data->reg; |
80 | gate->bit = data->bit; |
81 | gate->hw.init = &init; |
82 | |
83 | ret = devm_clk_hw_register(dev, hw: &gate->hw); |
84 | if (ret) |
85 | return ERR_PTR(error: ret); |
86 | |
87 | return &gate->hw; |
88 | } |
89 | |