1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Zynq UltraScale+ MPSoC mux |
4 | * |
5 | * Copyright (C) 2016-2018 Xilinx |
6 | */ |
7 | |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/slab.h> |
10 | #include "clk-zynqmp.h" |
11 | |
12 | /* |
13 | * DOC: basic adjustable multiplexer clock that cannot gate |
14 | * |
15 | * Traits of this clock: |
16 | * prepare - clk_prepare only ensures that parents are prepared |
17 | * enable - clk_enable only ensures that parents are enabled |
18 | * rate - rate is only affected by parent switching. No clk_set_rate support |
19 | * parent - parent is adjustable through clk_set_parent |
20 | */ |
21 | |
22 | /** |
23 | * struct zynqmp_clk_mux - multiplexer clock |
24 | * |
25 | * @hw: handle between common and hardware-specific interfaces |
26 | * @flags: hardware-specific flags |
27 | * @clk_id: Id of clock |
28 | */ |
29 | struct zynqmp_clk_mux { |
30 | struct clk_hw hw; |
31 | u8 flags; |
32 | u32 clk_id; |
33 | }; |
34 | |
35 | #define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw) |
36 | |
37 | /** |
38 | * zynqmp_clk_mux_get_parent() - Get parent of clock |
39 | * @hw: handle between common and hardware-specific interfaces |
40 | * |
41 | * Return: Parent index on success or number of parents in case of error |
42 | */ |
43 | static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw) |
44 | { |
45 | struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw); |
46 | const char *clk_name = clk_hw_get_name(hw); |
47 | u32 clk_id = mux->clk_id; |
48 | u32 val; |
49 | int ret; |
50 | |
51 | ret = zynqmp_pm_clock_getparent(clock_id: clk_id, parent_id: &val); |
52 | |
53 | if (ret) { |
54 | pr_debug("%s() getparent failed for clock: %s, ret = %d\n" , |
55 | __func__, clk_name, ret); |
56 | /* |
57 | * clk_core_get_parent_by_index() takes num_parents as incorrect |
58 | * index which is exactly what I want to return here |
59 | */ |
60 | return clk_hw_get_num_parents(hw); |
61 | } |
62 | |
63 | return val; |
64 | } |
65 | |
66 | /** |
67 | * zynqmp_clk_mux_set_parent() - Set parent of clock |
68 | * @hw: handle between common and hardware-specific interfaces |
69 | * @index: Parent index |
70 | * |
71 | * Return: 0 on success else error+reason |
72 | */ |
73 | static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index) |
74 | { |
75 | struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw); |
76 | const char *clk_name = clk_hw_get_name(hw); |
77 | u32 clk_id = mux->clk_id; |
78 | int ret; |
79 | |
80 | ret = zynqmp_pm_clock_setparent(clock_id: clk_id, parent_id: index); |
81 | |
82 | if (ret) |
83 | pr_debug("%s() set parent failed for clock: %s, ret = %d\n" , |
84 | __func__, clk_name, ret); |
85 | |
86 | return ret; |
87 | } |
88 | |
89 | static const struct clk_ops zynqmp_clk_mux_ops = { |
90 | .get_parent = zynqmp_clk_mux_get_parent, |
91 | .set_parent = zynqmp_clk_mux_set_parent, |
92 | .determine_rate = __clk_mux_determine_rate_closest, |
93 | }; |
94 | |
95 | static const struct clk_ops zynqmp_clk_mux_ro_ops = { |
96 | .get_parent = zynqmp_clk_mux_get_parent, |
97 | }; |
98 | |
99 | static inline unsigned long zynqmp_clk_map_mux_ccf_flags( |
100 | const u32 zynqmp_type_flag) |
101 | { |
102 | unsigned long ccf_flag = 0; |
103 | |
104 | if (zynqmp_type_flag & ZYNQMP_CLK_MUX_INDEX_ONE) |
105 | ccf_flag |= CLK_MUX_INDEX_ONE; |
106 | if (zynqmp_type_flag & ZYNQMP_CLK_MUX_INDEX_BIT) |
107 | ccf_flag |= CLK_MUX_INDEX_BIT; |
108 | if (zynqmp_type_flag & ZYNQMP_CLK_MUX_HIWORD_MASK) |
109 | ccf_flag |= CLK_MUX_HIWORD_MASK; |
110 | if (zynqmp_type_flag & ZYNQMP_CLK_MUX_READ_ONLY) |
111 | ccf_flag |= CLK_MUX_READ_ONLY; |
112 | if (zynqmp_type_flag & ZYNQMP_CLK_MUX_ROUND_CLOSEST) |
113 | ccf_flag |= CLK_MUX_ROUND_CLOSEST; |
114 | if (zynqmp_type_flag & ZYNQMP_CLK_MUX_BIG_ENDIAN) |
115 | ccf_flag |= CLK_MUX_BIG_ENDIAN; |
116 | |
117 | return ccf_flag; |
118 | } |
119 | |
120 | /** |
121 | * zynqmp_clk_register_mux() - Register a mux table with the clock |
122 | * framework |
123 | * @name: Name of this clock |
124 | * @clk_id: Id of this clock |
125 | * @parents: Name of this clock's parents |
126 | * @num_parents: Number of parents |
127 | * @nodes: Clock topology node |
128 | * |
129 | * Return: clock hardware of the registered clock mux |
130 | */ |
131 | struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id, |
132 | const char * const *parents, |
133 | u8 num_parents, |
134 | const struct clock_topology *nodes) |
135 | { |
136 | struct zynqmp_clk_mux *mux; |
137 | struct clk_hw *hw; |
138 | struct clk_init_data init; |
139 | int ret; |
140 | |
141 | mux = kzalloc(size: sizeof(*mux), GFP_KERNEL); |
142 | if (!mux) |
143 | return ERR_PTR(error: -ENOMEM); |
144 | |
145 | init.name = name; |
146 | if (nodes->type_flag & CLK_MUX_READ_ONLY) |
147 | init.ops = &zynqmp_clk_mux_ro_ops; |
148 | else |
149 | init.ops = &zynqmp_clk_mux_ops; |
150 | |
151 | init.flags = zynqmp_clk_map_common_ccf_flags(zynqmp_flag: nodes->flag); |
152 | |
153 | init.parent_names = parents; |
154 | init.num_parents = num_parents; |
155 | mux->flags = zynqmp_clk_map_mux_ccf_flags(zynqmp_type_flag: nodes->type_flag); |
156 | mux->hw.init = &init; |
157 | mux->clk_id = clk_id; |
158 | |
159 | hw = &mux->hw; |
160 | ret = clk_hw_register(NULL, hw); |
161 | if (ret) { |
162 | kfree(objp: mux); |
163 | hw = ERR_PTR(error: ret); |
164 | } |
165 | |
166 | return hw; |
167 | } |
168 | |