1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2014 Freescale Semiconductor, Inc. |
4 | */ |
5 | |
6 | #include <linux/clk-provider.h> |
7 | #include <linux/err.h> |
8 | #include <linux/io.h> |
9 | #include <linux/slab.h> |
10 | #include "clk.h" |
11 | |
12 | /** |
13 | * struct clk_gate_exclusive - i.MX specific gate clock which is mutually |
14 | * exclusive with other gate clocks |
15 | * |
16 | * @gate: the parent class |
17 | * @exclusive_mask: mask of gate bits which are mutually exclusive to this |
18 | * gate clock |
19 | * |
20 | * The imx exclusive gate clock is a subclass of basic clk_gate |
21 | * with an addtional mask to indicate which other gate bits in the same |
22 | * register is mutually exclusive to this gate clock. |
23 | */ |
24 | struct clk_gate_exclusive { |
25 | struct clk_gate gate; |
26 | u32 exclusive_mask; |
27 | }; |
28 | |
29 | static int clk_gate_exclusive_enable(struct clk_hw *hw) |
30 | { |
31 | struct clk_gate *gate = to_clk_gate(hw); |
32 | struct clk_gate_exclusive *exgate = container_of(gate, |
33 | struct clk_gate_exclusive, gate); |
34 | u32 val = readl(addr: gate->reg); |
35 | |
36 | if (val & exgate->exclusive_mask) |
37 | return -EBUSY; |
38 | |
39 | return clk_gate_ops.enable(hw); |
40 | } |
41 | |
42 | static void clk_gate_exclusive_disable(struct clk_hw *hw) |
43 | { |
44 | clk_gate_ops.disable(hw); |
45 | } |
46 | |
47 | static int clk_gate_exclusive_is_enabled(struct clk_hw *hw) |
48 | { |
49 | return clk_gate_ops.is_enabled(hw); |
50 | } |
51 | |
52 | static const struct clk_ops clk_gate_exclusive_ops = { |
53 | .enable = clk_gate_exclusive_enable, |
54 | .disable = clk_gate_exclusive_disable, |
55 | .is_enabled = clk_gate_exclusive_is_enabled, |
56 | }; |
57 | |
58 | struct clk_hw *imx_clk_hw_gate_exclusive(const char *name, const char *parent, |
59 | void __iomem *reg, u8 shift, u32 exclusive_mask) |
60 | { |
61 | struct clk_gate_exclusive *exgate; |
62 | struct clk_gate *gate; |
63 | struct clk_hw *hw; |
64 | struct clk_init_data init; |
65 | int ret; |
66 | |
67 | if (exclusive_mask == 0) |
68 | return ERR_PTR(error: -EINVAL); |
69 | |
70 | exgate = kzalloc(size: sizeof(*exgate), GFP_KERNEL); |
71 | if (!exgate) |
72 | return ERR_PTR(error: -ENOMEM); |
73 | gate = &exgate->gate; |
74 | |
75 | init.name = name; |
76 | init.ops = &clk_gate_exclusive_ops; |
77 | init.flags = CLK_SET_RATE_PARENT; |
78 | init.parent_names = parent ? &parent : NULL; |
79 | init.num_parents = parent ? 1 : 0; |
80 | |
81 | gate->reg = reg; |
82 | gate->bit_idx = shift; |
83 | gate->lock = &imx_ccm_lock; |
84 | gate->hw.init = &init; |
85 | exgate->exclusive_mask = exclusive_mask; |
86 | |
87 | hw = &gate->hw; |
88 | |
89 | ret = clk_hw_register(NULL, hw); |
90 | if (ret) { |
91 | kfree(objp: gate); |
92 | return ERR_PTR(error: ret); |
93 | } |
94 | |
95 | return hw; |
96 | } |
97 | |