xref: /linux/drivers/clk/mvebu/cp110-system-controller.c (revision 57ecc7a0d3f843e4c02f4a9a67b9da76a7f48b5d)
1d3da3eaeSThomas Petazzoni /*
2d3da3eaeSThomas Petazzoni  * Marvell Armada CP110 System Controller
3d3da3eaeSThomas Petazzoni  *
4d3da3eaeSThomas Petazzoni  * Copyright (C) 2016 Marvell
5d3da3eaeSThomas Petazzoni  *
6d3da3eaeSThomas Petazzoni  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
7d3da3eaeSThomas Petazzoni  *
8d3da3eaeSThomas Petazzoni  * This file is licensed under the terms of the GNU General Public
9d3da3eaeSThomas Petazzoni  * License version 2.  This program is licensed "as is" without any
10d3da3eaeSThomas Petazzoni  * warranty of any kind, whether express or implied.
11d3da3eaeSThomas Petazzoni  */
12d3da3eaeSThomas Petazzoni 
13d3da3eaeSThomas Petazzoni /*
14d3da3eaeSThomas Petazzoni  * CP110 has 5 core clocks:
15d3da3eaeSThomas Petazzoni  *
16d3da3eaeSThomas Petazzoni  *  - APLL		(1 Ghz)
17d3da3eaeSThomas Petazzoni  *    - PPv2 core	(1/3 APLL)
18d3da3eaeSThomas Petazzoni  *    - EIP		(1/2 APLL)
19d3da3eaeSThomas Petazzoni  *      - Core		(1/2 EIP)
20d3da3eaeSThomas Petazzoni  *
21d3da3eaeSThomas Petazzoni  *  - NAND clock, which is either:
22d3da3eaeSThomas Petazzoni  *    - Equal to the core clock
23d3da3eaeSThomas Petazzoni  *    - 2/5 APLL
24d3da3eaeSThomas Petazzoni  *
25d3da3eaeSThomas Petazzoni  * CP110 has 32 gatable clocks, for the various peripherals in the
26d3da3eaeSThomas Petazzoni  * IP. They have fairly complicated parent/child relationships.
27d3da3eaeSThomas Petazzoni  */
28d3da3eaeSThomas Petazzoni 
29d3da3eaeSThomas Petazzoni #define pr_fmt(fmt) "cp110-system-controller: " fmt
30d3da3eaeSThomas Petazzoni 
31d3da3eaeSThomas Petazzoni #include <linux/clk-provider.h>
32d3da3eaeSThomas Petazzoni #include <linux/mfd/syscon.h>
33d3da3eaeSThomas Petazzoni #include <linux/module.h>
34d3da3eaeSThomas Petazzoni #include <linux/of.h>
35d3da3eaeSThomas Petazzoni #include <linux/of_address.h>
36d3da3eaeSThomas Petazzoni #include <linux/platform_device.h>
37d3da3eaeSThomas Petazzoni #include <linux/regmap.h>
38d3da3eaeSThomas Petazzoni #include <linux/slab.h>
39d3da3eaeSThomas Petazzoni 
40d3da3eaeSThomas Petazzoni #define CP110_PM_CLOCK_GATING_REG	0x220
41d3da3eaeSThomas Petazzoni #define CP110_NAND_FLASH_CLK_CTRL_REG	0x700
42d3da3eaeSThomas Petazzoni #define    NF_CLOCK_SEL_400_MASK	BIT(0)
43d3da3eaeSThomas Petazzoni 
44d3da3eaeSThomas Petazzoni enum {
45d3da3eaeSThomas Petazzoni 	CP110_CLK_TYPE_CORE,
46d3da3eaeSThomas Petazzoni 	CP110_CLK_TYPE_GATABLE,
47d3da3eaeSThomas Petazzoni };
48d3da3eaeSThomas Petazzoni 
49d3da3eaeSThomas Petazzoni #define CP110_MAX_CORE_CLOCKS		5
50d3da3eaeSThomas Petazzoni #define CP110_MAX_GATABLE_CLOCKS	32
51d3da3eaeSThomas Petazzoni 
52d3da3eaeSThomas Petazzoni #define CP110_CLK_NUM \
53d3da3eaeSThomas Petazzoni 	(CP110_MAX_CORE_CLOCKS + CP110_MAX_GATABLE_CLOCKS)
54d3da3eaeSThomas Petazzoni 
55d3da3eaeSThomas Petazzoni #define CP110_CORE_APLL			0
56d3da3eaeSThomas Petazzoni #define CP110_CORE_PPV2			1
57d3da3eaeSThomas Petazzoni #define CP110_CORE_EIP			2
58d3da3eaeSThomas Petazzoni #define CP110_CORE_CORE			3
59d3da3eaeSThomas Petazzoni #define CP110_CORE_NAND			4
60d3da3eaeSThomas Petazzoni 
61d3da3eaeSThomas Petazzoni /* A number of gatable clocks need special handling */
62d3da3eaeSThomas Petazzoni #define CP110_GATE_AUDIO		0
63d3da3eaeSThomas Petazzoni #define CP110_GATE_COMM_UNIT		1
64d3da3eaeSThomas Petazzoni #define CP110_GATE_NAND			2
65d3da3eaeSThomas Petazzoni #define CP110_GATE_PPV2			3
66d3da3eaeSThomas Petazzoni #define CP110_GATE_SDIO			4
67d3da3eaeSThomas Petazzoni #define CP110_GATE_XOR1			7
68d3da3eaeSThomas Petazzoni #define CP110_GATE_XOR0			8
69d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_X1_0		11
70d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_X1_1		12
71d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_X4		13
72d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_XOR		14
73d3da3eaeSThomas Petazzoni #define CP110_GATE_SATA			15
74d3da3eaeSThomas Petazzoni #define CP110_GATE_SATA_USB		16
75d3da3eaeSThomas Petazzoni #define CP110_GATE_MAIN			17
76d3da3eaeSThomas Petazzoni #define CP110_GATE_SDMMC		18
77d3da3eaeSThomas Petazzoni #define CP110_GATE_SLOW_IO		21
78d3da3eaeSThomas Petazzoni #define CP110_GATE_USB3H0		22
79d3da3eaeSThomas Petazzoni #define CP110_GATE_USB3H1		23
80d3da3eaeSThomas Petazzoni #define CP110_GATE_USB3DEV		24
81d3da3eaeSThomas Petazzoni #define CP110_GATE_EIP150		25
82d3da3eaeSThomas Petazzoni #define CP110_GATE_EIP197		26
83d3da3eaeSThomas Petazzoni 
84d3da3eaeSThomas Petazzoni struct cp110_gate_clk {
85d3da3eaeSThomas Petazzoni 	struct clk_hw hw;
86d3da3eaeSThomas Petazzoni 	struct regmap *regmap;
87d3da3eaeSThomas Petazzoni 	u8 bit_idx;
88d3da3eaeSThomas Petazzoni };
89d3da3eaeSThomas Petazzoni 
90*57ecc7a0SMarcin Wojtas #define to_cp110_gate_clk(hw) container_of(hw, struct cp110_gate_clk, hw)
91d3da3eaeSThomas Petazzoni 
92d3da3eaeSThomas Petazzoni static int cp110_gate_enable(struct clk_hw *hw)
93d3da3eaeSThomas Petazzoni {
94d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
95d3da3eaeSThomas Petazzoni 
96d3da3eaeSThomas Petazzoni 	regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
97d3da3eaeSThomas Petazzoni 			   BIT(gate->bit_idx), BIT(gate->bit_idx));
98d3da3eaeSThomas Petazzoni 
99d3da3eaeSThomas Petazzoni 	return 0;
100d3da3eaeSThomas Petazzoni }
101d3da3eaeSThomas Petazzoni 
102d3da3eaeSThomas Petazzoni static void cp110_gate_disable(struct clk_hw *hw)
103d3da3eaeSThomas Petazzoni {
104d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
105d3da3eaeSThomas Petazzoni 
106d3da3eaeSThomas Petazzoni 	regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
107d3da3eaeSThomas Petazzoni 			   BIT(gate->bit_idx), 0);
108d3da3eaeSThomas Petazzoni }
109d3da3eaeSThomas Petazzoni 
110d3da3eaeSThomas Petazzoni static int cp110_gate_is_enabled(struct clk_hw *hw)
111d3da3eaeSThomas Petazzoni {
112d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
113d3da3eaeSThomas Petazzoni 	u32 val;
114d3da3eaeSThomas Petazzoni 
115d3da3eaeSThomas Petazzoni 	regmap_read(gate->regmap, CP110_PM_CLOCK_GATING_REG, &val);
116d3da3eaeSThomas Petazzoni 
117d3da3eaeSThomas Petazzoni 	return val & BIT(gate->bit_idx);
118d3da3eaeSThomas Petazzoni }
119d3da3eaeSThomas Petazzoni 
120d3da3eaeSThomas Petazzoni static const struct clk_ops cp110_gate_ops = {
121d3da3eaeSThomas Petazzoni 	.enable = cp110_gate_enable,
122d3da3eaeSThomas Petazzoni 	.disable = cp110_gate_disable,
123d3da3eaeSThomas Petazzoni 	.is_enabled = cp110_gate_is_enabled,
124d3da3eaeSThomas Petazzoni };
125d3da3eaeSThomas Petazzoni 
126*57ecc7a0SMarcin Wojtas static struct clk_hw *cp110_register_gate(const char *name,
127d3da3eaeSThomas Petazzoni 					  const char *parent_name,
128d3da3eaeSThomas Petazzoni 					  struct regmap *regmap, u8 bit_idx)
129d3da3eaeSThomas Petazzoni {
130d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate;
131*57ecc7a0SMarcin Wojtas 	struct clk_hw *hw;
132d3da3eaeSThomas Petazzoni 	struct clk_init_data init;
133*57ecc7a0SMarcin Wojtas 	int ret;
134d3da3eaeSThomas Petazzoni 
135d3da3eaeSThomas Petazzoni 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
136d3da3eaeSThomas Petazzoni 	if (!gate)
137d3da3eaeSThomas Petazzoni 		return ERR_PTR(-ENOMEM);
138d3da3eaeSThomas Petazzoni 
139ad715b26SMarcin Wojtas 	memset(&init, 0, sizeof(init));
140ad715b26SMarcin Wojtas 
141d3da3eaeSThomas Petazzoni 	init.name = name;
142d3da3eaeSThomas Petazzoni 	init.ops = &cp110_gate_ops;
143d3da3eaeSThomas Petazzoni 	init.parent_names = &parent_name;
144d3da3eaeSThomas Petazzoni 	init.num_parents = 1;
145d3da3eaeSThomas Petazzoni 
146d3da3eaeSThomas Petazzoni 	gate->regmap = regmap;
147d3da3eaeSThomas Petazzoni 	gate->bit_idx = bit_idx;
148d3da3eaeSThomas Petazzoni 	gate->hw.init = &init;
149d3da3eaeSThomas Petazzoni 
150*57ecc7a0SMarcin Wojtas 	hw = &gate->hw;
151*57ecc7a0SMarcin Wojtas 	ret = clk_hw_register(NULL, hw);
152*57ecc7a0SMarcin Wojtas 	if (ret) {
153d3da3eaeSThomas Petazzoni 		kfree(gate);
154*57ecc7a0SMarcin Wojtas 		hw = ERR_PTR(ret);
155d3da3eaeSThomas Petazzoni 	}
156d3da3eaeSThomas Petazzoni 
157*57ecc7a0SMarcin Wojtas 	return hw;
158*57ecc7a0SMarcin Wojtas }
159*57ecc7a0SMarcin Wojtas 
160*57ecc7a0SMarcin Wojtas static void cp110_unregister_gate(struct clk_hw *hw)
161d3da3eaeSThomas Petazzoni {
162*57ecc7a0SMarcin Wojtas 	clk_hw_unregister(hw);
163d3da3eaeSThomas Petazzoni 	kfree(to_cp110_gate_clk(hw));
164d3da3eaeSThomas Petazzoni }
165d3da3eaeSThomas Petazzoni 
166*57ecc7a0SMarcin Wojtas static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec,
167*57ecc7a0SMarcin Wojtas 				       void *data)
168d3da3eaeSThomas Petazzoni {
169*57ecc7a0SMarcin Wojtas 	struct clk_hw_onecell_data *clk_data = data;
170d3da3eaeSThomas Petazzoni 	unsigned int type = clkspec->args[0];
171d3da3eaeSThomas Petazzoni 	unsigned int idx = clkspec->args[1];
172d3da3eaeSThomas Petazzoni 
173d3da3eaeSThomas Petazzoni 	if (type == CP110_CLK_TYPE_CORE) {
174d3da3eaeSThomas Petazzoni 		if (idx > CP110_MAX_CORE_CLOCKS)
175d3da3eaeSThomas Petazzoni 			return ERR_PTR(-EINVAL);
176*57ecc7a0SMarcin Wojtas 		return clk_data->hws[idx];
177d3da3eaeSThomas Petazzoni 	} else if (type == CP110_CLK_TYPE_GATABLE) {
178d3da3eaeSThomas Petazzoni 		if (idx > CP110_MAX_GATABLE_CLOCKS)
179d3da3eaeSThomas Petazzoni 			return ERR_PTR(-EINVAL);
180*57ecc7a0SMarcin Wojtas 		return clk_data->hws[CP110_MAX_CORE_CLOCKS + idx];
181d3da3eaeSThomas Petazzoni 	}
182d3da3eaeSThomas Petazzoni 
183d3da3eaeSThomas Petazzoni 	return ERR_PTR(-EINVAL);
184d3da3eaeSThomas Petazzoni }
185d3da3eaeSThomas Petazzoni 
186d3da3eaeSThomas Petazzoni static int cp110_syscon_clk_probe(struct platform_device *pdev)
187d3da3eaeSThomas Petazzoni {
188d3da3eaeSThomas Petazzoni 	struct regmap *regmap;
189d3da3eaeSThomas Petazzoni 	struct device_node *np = pdev->dev.of_node;
190d3da3eaeSThomas Petazzoni 	const char *ppv2_name, *apll_name, *core_name, *eip_name, *nand_name;
191*57ecc7a0SMarcin Wojtas 	struct clk_hw_onecell_data *cp110_clk_data;
192*57ecc7a0SMarcin Wojtas 	struct clk_hw *hw, **cp110_clks;
193d3da3eaeSThomas Petazzoni 	u32 nand_clk_ctrl;
194d3da3eaeSThomas Petazzoni 	int i, ret;
195d3da3eaeSThomas Petazzoni 
196d3da3eaeSThomas Petazzoni 	regmap = syscon_node_to_regmap(np);
197d3da3eaeSThomas Petazzoni 	if (IS_ERR(regmap))
198d3da3eaeSThomas Petazzoni 		return PTR_ERR(regmap);
199d3da3eaeSThomas Petazzoni 
200d3da3eaeSThomas Petazzoni 	ret = regmap_read(regmap, CP110_NAND_FLASH_CLK_CTRL_REG,
201d3da3eaeSThomas Petazzoni 			  &nand_clk_ctrl);
202d3da3eaeSThomas Petazzoni 	if (ret)
203d3da3eaeSThomas Petazzoni 		return ret;
204d3da3eaeSThomas Petazzoni 
205*57ecc7a0SMarcin Wojtas 	cp110_clk_data = devm_kzalloc(&pdev->dev, sizeof(*cp110_clk_data) +
206*57ecc7a0SMarcin Wojtas 				      sizeof(struct clk_hw *) * CP110_CLK_NUM,
207a0245eb7SMarcin Wojtas 				      GFP_KERNEL);
208a0245eb7SMarcin Wojtas 	if (!cp110_clk_data)
209a0245eb7SMarcin Wojtas 		return -ENOMEM;
210a0245eb7SMarcin Wojtas 
211*57ecc7a0SMarcin Wojtas 	cp110_clks = cp110_clk_data->hws;
212*57ecc7a0SMarcin Wojtas 	cp110_clk_data->num = CP110_CLK_NUM;
213a0245eb7SMarcin Wojtas 
214*57ecc7a0SMarcin Wojtas 	/* Register the APLL which is the root of the hw tree */
215d3da3eaeSThomas Petazzoni 	of_property_read_string_index(np, "core-clock-output-names",
216d3da3eaeSThomas Petazzoni 				      CP110_CORE_APLL, &apll_name);
217*57ecc7a0SMarcin Wojtas 	hw = clk_hw_register_fixed_rate(NULL, apll_name, NULL, 0,
218d3da3eaeSThomas Petazzoni 					1000 * 1000 * 1000);
219*57ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
220*57ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
221d3da3eaeSThomas Petazzoni 		goto fail0;
222d3da3eaeSThomas Petazzoni 	}
223d3da3eaeSThomas Petazzoni 
224*57ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_APLL] = hw;
225d3da3eaeSThomas Petazzoni 
226d3da3eaeSThomas Petazzoni 	/* PPv2 is APLL/3 */
227d3da3eaeSThomas Petazzoni 	of_property_read_string_index(np, "core-clock-output-names",
228d3da3eaeSThomas Petazzoni 				      CP110_CORE_PPV2, &ppv2_name);
229*57ecc7a0SMarcin Wojtas 	hw = clk_hw_register_fixed_factor(NULL, ppv2_name, apll_name, 0, 1, 3);
230*57ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
231*57ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
232d3da3eaeSThomas Petazzoni 		goto fail1;
233d3da3eaeSThomas Petazzoni 	}
234d3da3eaeSThomas Petazzoni 
235*57ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_PPV2] = hw;
236d3da3eaeSThomas Petazzoni 
237d3da3eaeSThomas Petazzoni 	/* EIP clock is APLL/2 */
238d3da3eaeSThomas Petazzoni 	of_property_read_string_index(np, "core-clock-output-names",
239d3da3eaeSThomas Petazzoni 				      CP110_CORE_EIP, &eip_name);
240*57ecc7a0SMarcin Wojtas 	hw = clk_hw_register_fixed_factor(NULL, eip_name, apll_name, 0, 1, 2);
241*57ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
242*57ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
243d3da3eaeSThomas Petazzoni 		goto fail2;
244d3da3eaeSThomas Petazzoni 	}
245d3da3eaeSThomas Petazzoni 
246*57ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_EIP] = hw;
247d3da3eaeSThomas Petazzoni 
248d3da3eaeSThomas Petazzoni 	/* Core clock is EIP/2 */
249d3da3eaeSThomas Petazzoni 	of_property_read_string_index(np, "core-clock-output-names",
250d3da3eaeSThomas Petazzoni 				      CP110_CORE_CORE, &core_name);
251*57ecc7a0SMarcin Wojtas 	hw = clk_hw_register_fixed_factor(NULL, core_name, eip_name, 0, 1, 2);
252*57ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
253*57ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
254d3da3eaeSThomas Petazzoni 		goto fail3;
255d3da3eaeSThomas Petazzoni 	}
256d3da3eaeSThomas Petazzoni 
257*57ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_CORE] = hw;
258d3da3eaeSThomas Petazzoni 
259d3da3eaeSThomas Petazzoni 	/* NAND can be either APLL/2.5 or core clock */
260d3da3eaeSThomas Petazzoni 	of_property_read_string_index(np, "core-clock-output-names",
261d3da3eaeSThomas Petazzoni 				      CP110_CORE_NAND, &nand_name);
262d3da3eaeSThomas Petazzoni 	if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK)
263*57ecc7a0SMarcin Wojtas 		hw = clk_hw_register_fixed_factor(NULL, nand_name,
264d3da3eaeSThomas Petazzoni 						   apll_name, 0, 2, 5);
265d3da3eaeSThomas Petazzoni 	else
266*57ecc7a0SMarcin Wojtas 		hw = clk_hw_register_fixed_factor(NULL, nand_name,
267d3da3eaeSThomas Petazzoni 						   core_name, 0, 1, 1);
268*57ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
269*57ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
270d3da3eaeSThomas Petazzoni 		goto fail4;
271d3da3eaeSThomas Petazzoni 	}
272d3da3eaeSThomas Petazzoni 
273*57ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_NAND] = hw;
274d3da3eaeSThomas Petazzoni 
275d3da3eaeSThomas Petazzoni 	for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
276d3da3eaeSThomas Petazzoni 		const char *parent, *name;
277d3da3eaeSThomas Petazzoni 		int ret;
278d3da3eaeSThomas Petazzoni 
279d3da3eaeSThomas Petazzoni 		ret = of_property_read_string_index(np,
280d3da3eaeSThomas Petazzoni 						    "gate-clock-output-names",
281d3da3eaeSThomas Petazzoni 						    i, &name);
282d3da3eaeSThomas Petazzoni 		/* Reached the end of the list? */
283d3da3eaeSThomas Petazzoni 		if (ret < 0)
284d3da3eaeSThomas Petazzoni 			break;
285d3da3eaeSThomas Petazzoni 
286d3da3eaeSThomas Petazzoni 		if (!strcmp(name, "none"))
287d3da3eaeSThomas Petazzoni 			continue;
288d3da3eaeSThomas Petazzoni 
289d3da3eaeSThomas Petazzoni 		switch (i) {
290d3da3eaeSThomas Petazzoni 		case CP110_GATE_AUDIO:
291d3da3eaeSThomas Petazzoni 		case CP110_GATE_COMM_UNIT:
292d3da3eaeSThomas Petazzoni 		case CP110_GATE_EIP150:
293d3da3eaeSThomas Petazzoni 		case CP110_GATE_EIP197:
294d3da3eaeSThomas Petazzoni 		case CP110_GATE_SLOW_IO:
295d3da3eaeSThomas Petazzoni 			of_property_read_string_index(np,
296d3da3eaeSThomas Petazzoni 						      "gate-clock-output-names",
297d3da3eaeSThomas Petazzoni 						      CP110_GATE_MAIN, &parent);
298d3da3eaeSThomas Petazzoni 			break;
299d3da3eaeSThomas Petazzoni 		case CP110_GATE_NAND:
300d3da3eaeSThomas Petazzoni 			parent = nand_name;
301d3da3eaeSThomas Petazzoni 			break;
302d3da3eaeSThomas Petazzoni 		case CP110_GATE_PPV2:
303d3da3eaeSThomas Petazzoni 			parent = ppv2_name;
304d3da3eaeSThomas Petazzoni 			break;
305d3da3eaeSThomas Petazzoni 		case CP110_GATE_SDIO:
306d3da3eaeSThomas Petazzoni 			of_property_read_string_index(np,
307d3da3eaeSThomas Petazzoni 						      "gate-clock-output-names",
308d3da3eaeSThomas Petazzoni 						      CP110_GATE_SDMMC, &parent);
309d3da3eaeSThomas Petazzoni 			break;
310d3da3eaeSThomas Petazzoni 		case CP110_GATE_XOR1:
311d3da3eaeSThomas Petazzoni 		case CP110_GATE_XOR0:
312d3da3eaeSThomas Petazzoni 		case CP110_GATE_PCIE_X1_0:
313d3da3eaeSThomas Petazzoni 		case CP110_GATE_PCIE_X1_1:
314d3da3eaeSThomas Petazzoni 		case CP110_GATE_PCIE_X4:
315d3da3eaeSThomas Petazzoni 			of_property_read_string_index(np,
316d3da3eaeSThomas Petazzoni 						      "gate-clock-output-names",
317d3da3eaeSThomas Petazzoni 						      CP110_GATE_PCIE_XOR, &parent);
318d3da3eaeSThomas Petazzoni 			break;
319d3da3eaeSThomas Petazzoni 		case CP110_GATE_SATA:
320d3da3eaeSThomas Petazzoni 		case CP110_GATE_USB3H0:
321d3da3eaeSThomas Petazzoni 		case CP110_GATE_USB3H1:
322d3da3eaeSThomas Petazzoni 		case CP110_GATE_USB3DEV:
323d3da3eaeSThomas Petazzoni 			of_property_read_string_index(np,
324d3da3eaeSThomas Petazzoni 						      "gate-clock-output-names",
325d3da3eaeSThomas Petazzoni 						      CP110_GATE_SATA_USB, &parent);
326d3da3eaeSThomas Petazzoni 			break;
327d3da3eaeSThomas Petazzoni 		default:
328d3da3eaeSThomas Petazzoni 			parent = core_name;
329d3da3eaeSThomas Petazzoni 			break;
330d3da3eaeSThomas Petazzoni 		}
331d3da3eaeSThomas Petazzoni 
332*57ecc7a0SMarcin Wojtas 		hw = cp110_register_gate(name, parent, regmap, i);
333*57ecc7a0SMarcin Wojtas 		if (IS_ERR(hw)) {
334*57ecc7a0SMarcin Wojtas 			ret = PTR_ERR(hw);
335d3da3eaeSThomas Petazzoni 			goto fail_gate;
336d3da3eaeSThomas Petazzoni 		}
337d3da3eaeSThomas Petazzoni 
338*57ecc7a0SMarcin Wojtas 		cp110_clks[CP110_MAX_CORE_CLOCKS + i] = hw;
339d3da3eaeSThomas Petazzoni 	}
340d3da3eaeSThomas Petazzoni 
341*57ecc7a0SMarcin Wojtas 	ret = of_clk_add_hw_provider(np, cp110_of_clk_get, cp110_clk_data);
342d3da3eaeSThomas Petazzoni 	if (ret)
343d3da3eaeSThomas Petazzoni 		goto fail_clk_add;
344d3da3eaeSThomas Petazzoni 
345a0245eb7SMarcin Wojtas 	platform_set_drvdata(pdev, cp110_clks);
346a0245eb7SMarcin Wojtas 
347d3da3eaeSThomas Petazzoni 	return 0;
348d3da3eaeSThomas Petazzoni 
349d3da3eaeSThomas Petazzoni fail_clk_add:
350d3da3eaeSThomas Petazzoni fail_gate:
351d3da3eaeSThomas Petazzoni 	for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
352*57ecc7a0SMarcin Wojtas 		hw = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
353d3da3eaeSThomas Petazzoni 
354*57ecc7a0SMarcin Wojtas 		if (hw)
355*57ecc7a0SMarcin Wojtas 			cp110_unregister_gate(hw);
356d3da3eaeSThomas Petazzoni 	}
357d3da3eaeSThomas Petazzoni 
358*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
359d3da3eaeSThomas Petazzoni fail4:
360*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
361d3da3eaeSThomas Petazzoni fail3:
362*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
363d3da3eaeSThomas Petazzoni fail2:
364*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
365d3da3eaeSThomas Petazzoni fail1:
366*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
367d3da3eaeSThomas Petazzoni fail0:
368d3da3eaeSThomas Petazzoni 	return ret;
369d3da3eaeSThomas Petazzoni }
370d3da3eaeSThomas Petazzoni 
371d3da3eaeSThomas Petazzoni static int cp110_syscon_clk_remove(struct platform_device *pdev)
372d3da3eaeSThomas Petazzoni {
373*57ecc7a0SMarcin Wojtas 	struct clk_hw **cp110_clks = platform_get_drvdata(pdev);
374d3da3eaeSThomas Petazzoni 	int i;
375d3da3eaeSThomas Petazzoni 
376d3da3eaeSThomas Petazzoni 	of_clk_del_provider(pdev->dev.of_node);
377d3da3eaeSThomas Petazzoni 
378d3da3eaeSThomas Petazzoni 	for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
379*57ecc7a0SMarcin Wojtas 		struct clk_hw *hw = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
380d3da3eaeSThomas Petazzoni 
381*57ecc7a0SMarcin Wojtas 		if (hw)
382*57ecc7a0SMarcin Wojtas 			cp110_unregister_gate(hw);
383d3da3eaeSThomas Petazzoni 	}
384d3da3eaeSThomas Petazzoni 
385*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
386*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
387*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_EIP]);
388*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
389*57ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_APLL]);
390d3da3eaeSThomas Petazzoni 
391d3da3eaeSThomas Petazzoni 	return 0;
392d3da3eaeSThomas Petazzoni }
393d3da3eaeSThomas Petazzoni 
394d3da3eaeSThomas Petazzoni static const struct of_device_id cp110_syscon_of_match[] = {
395d3da3eaeSThomas Petazzoni 	{ .compatible = "marvell,cp110-system-controller0", },
396d3da3eaeSThomas Petazzoni 	{ }
397d3da3eaeSThomas Petazzoni };
398d3da3eaeSThomas Petazzoni MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match);
399d3da3eaeSThomas Petazzoni 
400d3da3eaeSThomas Petazzoni static struct platform_driver cp110_syscon_driver = {
401d3da3eaeSThomas Petazzoni 	.probe = cp110_syscon_clk_probe,
402d3da3eaeSThomas Petazzoni 	.remove = cp110_syscon_clk_remove,
403d3da3eaeSThomas Petazzoni 	.driver		= {
404d3da3eaeSThomas Petazzoni 		.name	= "marvell-cp110-system-controller0",
405d3da3eaeSThomas Petazzoni 		.of_match_table = cp110_syscon_of_match,
406d3da3eaeSThomas Petazzoni 	},
407d3da3eaeSThomas Petazzoni };
408d3da3eaeSThomas Petazzoni 
409d3da3eaeSThomas Petazzoni module_platform_driver(cp110_syscon_driver);
410d3da3eaeSThomas Petazzoni 
411d3da3eaeSThomas Petazzoni MODULE_DESCRIPTION("Marvell CP110 System Controller 0 driver");
412d3da3eaeSThomas Petazzoni MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
413d3da3eaeSThomas Petazzoni MODULE_LICENSE("GPL");
414