xref: /linux/drivers/clk/mvebu/cp110-system-controller.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
1c3828949SGregory CLEMENT // SPDX-License-Identifier: GPL-2.0
2d3da3eaeSThomas Petazzoni /*
3d3da3eaeSThomas Petazzoni  * Marvell Armada CP110 System Controller
4d3da3eaeSThomas Petazzoni  *
5d3da3eaeSThomas Petazzoni  * Copyright (C) 2016 Marvell
6d3da3eaeSThomas Petazzoni  *
7d3da3eaeSThomas Petazzoni  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
8d3da3eaeSThomas Petazzoni  *
9d3da3eaeSThomas Petazzoni  */
10d3da3eaeSThomas Petazzoni 
11d3da3eaeSThomas Petazzoni /*
12a45af6d3SKonstantin Porotchkin  * CP110 has 6 core clocks:
13d3da3eaeSThomas Petazzoni  *
14c7e92defSGregory CLEMENT  *  - PLL0		(1 Ghz)
15c7e92defSGregory CLEMENT  *    - PPv2 core	(1/3 PLL0)
16c7e92defSGregory CLEMENT  *    - x2 Core		(1/2 PLL0)
17c7e92defSGregory CLEMENT  *	- Core		(1/2 x2 Core)
18c7e92defSGregory CLEMENT  *    - SDIO		(2/5 PLL0)
19d3da3eaeSThomas Petazzoni  *
20d3da3eaeSThomas Petazzoni  *  - NAND clock, which is either:
21a45af6d3SKonstantin Porotchkin  *    - Equal to SDIO clock
22c7e92defSGregory CLEMENT  *    - 2/5 PLL0
23d3da3eaeSThomas Petazzoni  *
247fbb639aSColin Ian King  * CP110 has 32 gateable clocks, for the various peripherals in the IP.
25d3da3eaeSThomas Petazzoni  */
26d3da3eaeSThomas Petazzoni 
27d3da3eaeSThomas Petazzoni #define pr_fmt(fmt) "cp110-system-controller: " fmt
28d3da3eaeSThomas Petazzoni 
2933c02590SGregory CLEMENT #include "armada_ap_cp_helper.h"
30d3da3eaeSThomas Petazzoni #include <linux/clk-provider.h>
31d3da3eaeSThomas Petazzoni #include <linux/mfd/syscon.h>
327acf751eSPaul Gortmaker #include <linux/init.h>
33d3da3eaeSThomas Petazzoni #include <linux/of.h>
34d3da3eaeSThomas Petazzoni #include <linux/platform_device.h>
35d3da3eaeSThomas Petazzoni #include <linux/regmap.h>
36d3da3eaeSThomas Petazzoni #include <linux/slab.h>
37d3da3eaeSThomas Petazzoni 
38d3da3eaeSThomas Petazzoni #define CP110_PM_CLOCK_GATING_REG	0x220
39d3da3eaeSThomas Petazzoni #define CP110_NAND_FLASH_CLK_CTRL_REG	0x700
40d3da3eaeSThomas Petazzoni #define    NF_CLOCK_SEL_400_MASK	BIT(0)
41d3da3eaeSThomas Petazzoni 
42d3da3eaeSThomas Petazzoni enum {
43d3da3eaeSThomas Petazzoni 	CP110_CLK_TYPE_CORE,
44d3da3eaeSThomas Petazzoni 	CP110_CLK_TYPE_GATABLE,
45d3da3eaeSThomas Petazzoni };
46d3da3eaeSThomas Petazzoni 
47a45af6d3SKonstantin Porotchkin #define CP110_MAX_CORE_CLOCKS		6
48d3da3eaeSThomas Petazzoni #define CP110_MAX_GATABLE_CLOCKS	32
49d3da3eaeSThomas Petazzoni 
50d3da3eaeSThomas Petazzoni #define CP110_CLK_NUM \
51d3da3eaeSThomas Petazzoni 	(CP110_MAX_CORE_CLOCKS + CP110_MAX_GATABLE_CLOCKS)
52d3da3eaeSThomas Petazzoni 
53c7e92defSGregory CLEMENT #define CP110_CORE_PLL0			0
54d3da3eaeSThomas Petazzoni #define CP110_CORE_PPV2			1
55c7e92defSGregory CLEMENT #define CP110_CORE_X2CORE		2
56d3da3eaeSThomas Petazzoni #define CP110_CORE_CORE			3
57d3da3eaeSThomas Petazzoni #define CP110_CORE_NAND			4
58a45af6d3SKonstantin Porotchkin #define CP110_CORE_SDIO			5
59d3da3eaeSThomas Petazzoni 
607fbb639aSColin Ian King /* A number of gateable clocks need special handling */
61d3da3eaeSThomas Petazzoni #define CP110_GATE_AUDIO		0
62d3da3eaeSThomas Petazzoni #define CP110_GATE_COMM_UNIT		1
63d3da3eaeSThomas Petazzoni #define CP110_GATE_NAND			2
64d3da3eaeSThomas Petazzoni #define CP110_GATE_PPV2			3
65d3da3eaeSThomas Petazzoni #define CP110_GATE_SDIO			4
661006ccccSThomas Petazzoni #define CP110_GATE_MG			5
671006ccccSThomas Petazzoni #define CP110_GATE_MG_CORE		6
68d3da3eaeSThomas Petazzoni #define CP110_GATE_XOR1			7
69d3da3eaeSThomas Petazzoni #define CP110_GATE_XOR0			8
701006ccccSThomas Petazzoni #define CP110_GATE_GOP_DP		9
71d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_X1_0		11
72d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_X1_1		12
73d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_X4		13
74d3da3eaeSThomas Petazzoni #define CP110_GATE_PCIE_XOR		14
75d3da3eaeSThomas Petazzoni #define CP110_GATE_SATA			15
76d3da3eaeSThomas Petazzoni #define CP110_GATE_SATA_USB		16
77d3da3eaeSThomas Petazzoni #define CP110_GATE_MAIN			17
781006ccccSThomas Petazzoni #define CP110_GATE_SDMMC_GOP		18
79d3da3eaeSThomas Petazzoni #define CP110_GATE_SLOW_IO		21
80d3da3eaeSThomas Petazzoni #define CP110_GATE_USB3H0		22
81d3da3eaeSThomas Petazzoni #define CP110_GATE_USB3H1		23
82d3da3eaeSThomas Petazzoni #define CP110_GATE_USB3DEV		24
83d3da3eaeSThomas Petazzoni #define CP110_GATE_EIP150		25
84d3da3eaeSThomas Petazzoni #define CP110_GATE_EIP197		26
85d3da3eaeSThomas Petazzoni 
864a5aa069SStephen Boyd static const char * const gate_base_names[] = {
87f5667274SGregory CLEMENT 	[CP110_GATE_AUDIO]	= "audio",
88f5667274SGregory CLEMENT 	[CP110_GATE_COMM_UNIT]	= "communit",
89f5667274SGregory CLEMENT 	[CP110_GATE_NAND]	= "nand",
90f5667274SGregory CLEMENT 	[CP110_GATE_PPV2]	= "ppv2",
91f5667274SGregory CLEMENT 	[CP110_GATE_SDIO]	= "sdio",
92f5667274SGregory CLEMENT 	[CP110_GATE_MG]		= "mg-domain",
93f5667274SGregory CLEMENT 	[CP110_GATE_MG_CORE]	= "mg-core",
94f5667274SGregory CLEMENT 	[CP110_GATE_XOR1]	= "xor1",
95f5667274SGregory CLEMENT 	[CP110_GATE_XOR0]	= "xor0",
96f5667274SGregory CLEMENT 	[CP110_GATE_GOP_DP]	= "gop-dp",
97f5667274SGregory CLEMENT 	[CP110_GATE_PCIE_X1_0]	= "pcie_x10",
98f5667274SGregory CLEMENT 	[CP110_GATE_PCIE_X1_1]	= "pcie_x11",
99f5667274SGregory CLEMENT 	[CP110_GATE_PCIE_X4]	= "pcie_x4",
100f5667274SGregory CLEMENT 	[CP110_GATE_PCIE_XOR]	= "pcie-xor",
101f5667274SGregory CLEMENT 	[CP110_GATE_SATA]	= "sata",
102f5667274SGregory CLEMENT 	[CP110_GATE_SATA_USB]	= "sata-usb",
103f5667274SGregory CLEMENT 	[CP110_GATE_MAIN]	= "main",
104f5667274SGregory CLEMENT 	[CP110_GATE_SDMMC_GOP]	= "sd-mmc-gop",
105f5667274SGregory CLEMENT 	[CP110_GATE_SLOW_IO]	= "slow-io",
106f5667274SGregory CLEMENT 	[CP110_GATE_USB3H0]	= "usb3h0",
107f5667274SGregory CLEMENT 	[CP110_GATE_USB3H1]	= "usb3h1",
108f5667274SGregory CLEMENT 	[CP110_GATE_USB3DEV]	= "usb3dev",
109f5667274SGregory CLEMENT 	[CP110_GATE_EIP150]	= "eip150",
110f5667274SGregory CLEMENT 	[CP110_GATE_EIP197]	= "eip197"
111f5667274SGregory CLEMENT };
112f5667274SGregory CLEMENT 
113d3da3eaeSThomas Petazzoni struct cp110_gate_clk {
114d3da3eaeSThomas Petazzoni 	struct clk_hw hw;
115d3da3eaeSThomas Petazzoni 	struct regmap *regmap;
116d3da3eaeSThomas Petazzoni 	u8 bit_idx;
117d3da3eaeSThomas Petazzoni };
118d3da3eaeSThomas Petazzoni 
11957ecc7a0SMarcin Wojtas #define to_cp110_gate_clk(hw) container_of(hw, struct cp110_gate_clk, hw)
120d3da3eaeSThomas Petazzoni 
cp110_gate_enable(struct clk_hw * hw)121d3da3eaeSThomas Petazzoni static int cp110_gate_enable(struct clk_hw *hw)
122d3da3eaeSThomas Petazzoni {
123d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
124d3da3eaeSThomas Petazzoni 
125d3da3eaeSThomas Petazzoni 	regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
126d3da3eaeSThomas Petazzoni 			   BIT(gate->bit_idx), BIT(gate->bit_idx));
127d3da3eaeSThomas Petazzoni 
128d3da3eaeSThomas Petazzoni 	return 0;
129d3da3eaeSThomas Petazzoni }
130d3da3eaeSThomas Petazzoni 
cp110_gate_disable(struct clk_hw * hw)131d3da3eaeSThomas Petazzoni static void cp110_gate_disable(struct clk_hw *hw)
132d3da3eaeSThomas Petazzoni {
133d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
134d3da3eaeSThomas Petazzoni 
135d3da3eaeSThomas Petazzoni 	regmap_update_bits(gate->regmap, CP110_PM_CLOCK_GATING_REG,
136d3da3eaeSThomas Petazzoni 			   BIT(gate->bit_idx), 0);
137d3da3eaeSThomas Petazzoni }
138d3da3eaeSThomas Petazzoni 
cp110_gate_is_enabled(struct clk_hw * hw)139d3da3eaeSThomas Petazzoni static int cp110_gate_is_enabled(struct clk_hw *hw)
140d3da3eaeSThomas Petazzoni {
141d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate = to_cp110_gate_clk(hw);
142d3da3eaeSThomas Petazzoni 	u32 val;
143d3da3eaeSThomas Petazzoni 
144d3da3eaeSThomas Petazzoni 	regmap_read(gate->regmap, CP110_PM_CLOCK_GATING_REG, &val);
145d3da3eaeSThomas Petazzoni 
146d3da3eaeSThomas Petazzoni 	return val & BIT(gate->bit_idx);
147d3da3eaeSThomas Petazzoni }
148d3da3eaeSThomas Petazzoni 
149d3da3eaeSThomas Petazzoni static const struct clk_ops cp110_gate_ops = {
150d3da3eaeSThomas Petazzoni 	.enable = cp110_gate_enable,
151d3da3eaeSThomas Petazzoni 	.disable = cp110_gate_disable,
152d3da3eaeSThomas Petazzoni 	.is_enabled = cp110_gate_is_enabled,
153d3da3eaeSThomas Petazzoni };
154d3da3eaeSThomas Petazzoni 
cp110_register_gate(const char * name,const char * parent_name,struct regmap * regmap,u8 bit_idx)15557ecc7a0SMarcin Wojtas static struct clk_hw *cp110_register_gate(const char *name,
156d3da3eaeSThomas Petazzoni 					  const char *parent_name,
157d3da3eaeSThomas Petazzoni 					  struct regmap *regmap, u8 bit_idx)
158d3da3eaeSThomas Petazzoni {
159d3da3eaeSThomas Petazzoni 	struct cp110_gate_clk *gate;
16057ecc7a0SMarcin Wojtas 	struct clk_hw *hw;
161d3da3eaeSThomas Petazzoni 	struct clk_init_data init;
16257ecc7a0SMarcin Wojtas 	int ret;
163d3da3eaeSThomas Petazzoni 
164d3da3eaeSThomas Petazzoni 	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
165d3da3eaeSThomas Petazzoni 	if (!gate)
166d3da3eaeSThomas Petazzoni 		return ERR_PTR(-ENOMEM);
167d3da3eaeSThomas Petazzoni 
168ad715b26SMarcin Wojtas 	memset(&init, 0, sizeof(init));
169ad715b26SMarcin Wojtas 
170d3da3eaeSThomas Petazzoni 	init.name = name;
171d3da3eaeSThomas Petazzoni 	init.ops = &cp110_gate_ops;
172d3da3eaeSThomas Petazzoni 	init.parent_names = &parent_name;
173d3da3eaeSThomas Petazzoni 	init.num_parents = 1;
174d3da3eaeSThomas Petazzoni 
175d3da3eaeSThomas Petazzoni 	gate->regmap = regmap;
176d3da3eaeSThomas Petazzoni 	gate->bit_idx = bit_idx;
177d3da3eaeSThomas Petazzoni 	gate->hw.init = &init;
178d3da3eaeSThomas Petazzoni 
17957ecc7a0SMarcin Wojtas 	hw = &gate->hw;
18057ecc7a0SMarcin Wojtas 	ret = clk_hw_register(NULL, hw);
18157ecc7a0SMarcin Wojtas 	if (ret) {
182d3da3eaeSThomas Petazzoni 		kfree(gate);
18357ecc7a0SMarcin Wojtas 		hw = ERR_PTR(ret);
184d3da3eaeSThomas Petazzoni 	}
185d3da3eaeSThomas Petazzoni 
18657ecc7a0SMarcin Wojtas 	return hw;
18757ecc7a0SMarcin Wojtas }
18857ecc7a0SMarcin Wojtas 
cp110_unregister_gate(struct clk_hw * hw)18957ecc7a0SMarcin Wojtas static void cp110_unregister_gate(struct clk_hw *hw)
190d3da3eaeSThomas Petazzoni {
19157ecc7a0SMarcin Wojtas 	clk_hw_unregister(hw);
192d3da3eaeSThomas Petazzoni 	kfree(to_cp110_gate_clk(hw));
193d3da3eaeSThomas Petazzoni }
194d3da3eaeSThomas Petazzoni 
cp110_of_clk_get(struct of_phandle_args * clkspec,void * data)19557ecc7a0SMarcin Wojtas static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec,
19657ecc7a0SMarcin Wojtas 				       void *data)
197d3da3eaeSThomas Petazzoni {
19857ecc7a0SMarcin Wojtas 	struct clk_hw_onecell_data *clk_data = data;
199d3da3eaeSThomas Petazzoni 	unsigned int type = clkspec->args[0];
200d3da3eaeSThomas Petazzoni 	unsigned int idx = clkspec->args[1];
201d3da3eaeSThomas Petazzoni 
202d3da3eaeSThomas Petazzoni 	if (type == CP110_CLK_TYPE_CORE) {
203d9f5b7f5SDan Carpenter 		if (idx >= CP110_MAX_CORE_CLOCKS)
204d3da3eaeSThomas Petazzoni 			return ERR_PTR(-EINVAL);
20557ecc7a0SMarcin Wojtas 		return clk_data->hws[idx];
206d3da3eaeSThomas Petazzoni 	} else if (type == CP110_CLK_TYPE_GATABLE) {
207d9f5b7f5SDan Carpenter 		if (idx >= CP110_MAX_GATABLE_CLOCKS)
208d3da3eaeSThomas Petazzoni 			return ERR_PTR(-EINVAL);
20957ecc7a0SMarcin Wojtas 		return clk_data->hws[CP110_MAX_CORE_CLOCKS + idx];
210d3da3eaeSThomas Petazzoni 	}
211d3da3eaeSThomas Petazzoni 
212d3da3eaeSThomas Petazzoni 	return ERR_PTR(-EINVAL);
213d3da3eaeSThomas Petazzoni }
214d3da3eaeSThomas Petazzoni 
cp110_syscon_common_probe(struct platform_device * pdev,struct device_node * syscon_node)2155ffeb5f5SGregory CLEMENT static int cp110_syscon_common_probe(struct platform_device *pdev,
2165ffeb5f5SGregory CLEMENT 				     struct device_node *syscon_node)
217d3da3eaeSThomas Petazzoni {
218d3da3eaeSThomas Petazzoni 	struct regmap *regmap;
219f5667274SGregory CLEMENT 	struct device *dev = &pdev->dev;
220f5667274SGregory CLEMENT 	struct device_node *np = dev->of_node;
221c7e92defSGregory CLEMENT 	const char *ppv2_name, *pll0_name, *core_name, *x2core_name, *nand_name,
222a45af6d3SKonstantin Porotchkin 		*sdio_name;
22357ecc7a0SMarcin Wojtas 	struct clk_hw_onecell_data *cp110_clk_data;
22457ecc7a0SMarcin Wojtas 	struct clk_hw *hw, **cp110_clks;
225d3da3eaeSThomas Petazzoni 	u32 nand_clk_ctrl;
226d3da3eaeSThomas Petazzoni 	int i, ret;
227f5667274SGregory CLEMENT 	char *gate_name[ARRAY_SIZE(gate_base_names)];
228d3da3eaeSThomas Petazzoni 
2295ffeb5f5SGregory CLEMENT 	regmap = syscon_node_to_regmap(syscon_node);
230d3da3eaeSThomas Petazzoni 	if (IS_ERR(regmap))
231d3da3eaeSThomas Petazzoni 		return PTR_ERR(regmap);
232d3da3eaeSThomas Petazzoni 
233d3da3eaeSThomas Petazzoni 	ret = regmap_read(regmap, CP110_NAND_FLASH_CLK_CTRL_REG,
234d3da3eaeSThomas Petazzoni 			  &nand_clk_ctrl);
235d3da3eaeSThomas Petazzoni 	if (ret)
236d3da3eaeSThomas Petazzoni 		return ret;
237d3da3eaeSThomas Petazzoni 
238e620a1e0SStephen Kitt 	cp110_clk_data = devm_kzalloc(dev, struct_size(cp110_clk_data, hws,
239e620a1e0SStephen Kitt 						       CP110_CLK_NUM),
240a0245eb7SMarcin Wojtas 				      GFP_KERNEL);
241a0245eb7SMarcin Wojtas 	if (!cp110_clk_data)
242a0245eb7SMarcin Wojtas 		return -ENOMEM;
243*f316cdffSKees Cook 	cp110_clk_data->num = CP110_CLK_NUM;
244a0245eb7SMarcin Wojtas 
24557ecc7a0SMarcin Wojtas 	cp110_clks = cp110_clk_data->hws;
246a0245eb7SMarcin Wojtas 
247c7e92defSGregory CLEMENT 	/* Register the PLL0 which is the root of the hw tree */
24833c02590SGregory CLEMENT 	pll0_name = ap_cp_unique_name(dev, syscon_node, "pll0");
249c7e92defSGregory CLEMENT 	hw = clk_hw_register_fixed_rate(NULL, pll0_name, NULL, 0,
250d3da3eaeSThomas Petazzoni 					1000 * 1000 * 1000);
25157ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
25257ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
253c7e92defSGregory CLEMENT 		goto fail_pll0;
254d3da3eaeSThomas Petazzoni 	}
255d3da3eaeSThomas Petazzoni 
256c7e92defSGregory CLEMENT 	cp110_clks[CP110_CORE_PLL0] = hw;
257d3da3eaeSThomas Petazzoni 
258c7e92defSGregory CLEMENT 	/* PPv2 is PLL0/3 */
25933c02590SGregory CLEMENT 	ppv2_name = ap_cp_unique_name(dev, syscon_node, "ppv2-core");
260c7e92defSGregory CLEMENT 	hw = clk_hw_register_fixed_factor(NULL, ppv2_name, pll0_name, 0, 1, 3);
26157ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
26257ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
26329e6beb5SGregory CLEMENT 		goto fail_ppv2;
264d3da3eaeSThomas Petazzoni 	}
265d3da3eaeSThomas Petazzoni 
26657ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_PPV2] = hw;
267d3da3eaeSThomas Petazzoni 
268c7e92defSGregory CLEMENT 	/* X2CORE clock is PLL0/2 */
26933c02590SGregory CLEMENT 	x2core_name = ap_cp_unique_name(dev, syscon_node, "x2core");
270c7e92defSGregory CLEMENT 	hw = clk_hw_register_fixed_factor(NULL, x2core_name, pll0_name,
271c7e92defSGregory CLEMENT 					  0, 1, 2);
27257ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
27357ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
27429e6beb5SGregory CLEMENT 		goto fail_eip;
275d3da3eaeSThomas Petazzoni 	}
276d3da3eaeSThomas Petazzoni 
277c7e92defSGregory CLEMENT 	cp110_clks[CP110_CORE_X2CORE] = hw;
278d3da3eaeSThomas Petazzoni 
279c7e92defSGregory CLEMENT 	/* Core clock is X2CORE/2 */
28033c02590SGregory CLEMENT 	core_name = ap_cp_unique_name(dev, syscon_node, "core");
281c7e92defSGregory CLEMENT 	hw = clk_hw_register_fixed_factor(NULL, core_name, x2core_name,
282c7e92defSGregory CLEMENT 					  0, 1, 2);
28357ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
28457ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
28529e6beb5SGregory CLEMENT 		goto fail_core;
286d3da3eaeSThomas Petazzoni 	}
287d3da3eaeSThomas Petazzoni 
28857ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_CORE] = hw;
289c7e92defSGregory CLEMENT 	/* NAND can be either PLL0/2.5 or core clock */
29033c02590SGregory CLEMENT 	nand_name = ap_cp_unique_name(dev, syscon_node, "nand-core");
291d3da3eaeSThomas Petazzoni 	if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK)
29257ecc7a0SMarcin Wojtas 		hw = clk_hw_register_fixed_factor(NULL, nand_name,
293c7e92defSGregory CLEMENT 						   pll0_name, 0, 2, 5);
294d3da3eaeSThomas Petazzoni 	else
29557ecc7a0SMarcin Wojtas 		hw = clk_hw_register_fixed_factor(NULL, nand_name,
296d3da3eaeSThomas Petazzoni 						   core_name, 0, 1, 1);
29757ecc7a0SMarcin Wojtas 	if (IS_ERR(hw)) {
29857ecc7a0SMarcin Wojtas 		ret = PTR_ERR(hw);
29929e6beb5SGregory CLEMENT 		goto fail_nand;
300d3da3eaeSThomas Petazzoni 	}
301d3da3eaeSThomas Petazzoni 
30257ecc7a0SMarcin Wojtas 	cp110_clks[CP110_CORE_NAND] = hw;
303d3da3eaeSThomas Petazzoni 
304c7e92defSGregory CLEMENT 	/* SDIO clock is PLL0/2.5 */
30533c02590SGregory CLEMENT 	sdio_name = ap_cp_unique_name(dev, syscon_node, "sdio-core");
306a45af6d3SKonstantin Porotchkin 	hw = clk_hw_register_fixed_factor(NULL, sdio_name,
307c7e92defSGregory CLEMENT 					  pll0_name, 0, 2, 5);
308a45af6d3SKonstantin Porotchkin 	if (IS_ERR(hw)) {
309a45af6d3SKonstantin Porotchkin 		ret = PTR_ERR(hw);
310a45af6d3SKonstantin Porotchkin 		goto fail_sdio;
311a45af6d3SKonstantin Porotchkin 	}
312a45af6d3SKonstantin Porotchkin 
313a45af6d3SKonstantin Porotchkin 	cp110_clks[CP110_CORE_SDIO] = hw;
314a45af6d3SKonstantin Porotchkin 
315f5667274SGregory CLEMENT 	/* create the unique name for all the gate clocks */
316f5667274SGregory CLEMENT 	for (i = 0; i < ARRAY_SIZE(gate_base_names); i++)
31733c02590SGregory CLEMENT 		gate_name[i] =	ap_cp_unique_name(dev, syscon_node,
3185ffeb5f5SGregory CLEMENT 						  gate_base_names[i]);
319d3da3eaeSThomas Petazzoni 
320f5667274SGregory CLEMENT 	for (i = 0; i < ARRAY_SIZE(gate_base_names); i++) {
321f5667274SGregory CLEMENT 		const char *parent;
322d3da3eaeSThomas Petazzoni 
323f5667274SGregory CLEMENT 		if (gate_name[i] == NULL)
324d3da3eaeSThomas Petazzoni 			continue;
325d3da3eaeSThomas Petazzoni 
326d3da3eaeSThomas Petazzoni 		switch (i) {
327d3da3eaeSThomas Petazzoni 		case CP110_GATE_NAND:
328d3da3eaeSThomas Petazzoni 			parent = nand_name;
329d3da3eaeSThomas Petazzoni 			break;
330c7e92defSGregory CLEMENT 		case CP110_GATE_MG:
331c7e92defSGregory CLEMENT 		case CP110_GATE_GOP_DP:
332d3da3eaeSThomas Petazzoni 		case CP110_GATE_PPV2:
333d3da3eaeSThomas Petazzoni 			parent = ppv2_name;
334d3da3eaeSThomas Petazzoni 			break;
335d3da3eaeSThomas Petazzoni 		case CP110_GATE_SDIO:
336a45af6d3SKonstantin Porotchkin 			parent = sdio_name;
337a45af6d3SKonstantin Porotchkin 			break;
338c7e92defSGregory CLEMENT 		case CP110_GATE_MAIN:
339c7e92defSGregory CLEMENT 		case CP110_GATE_PCIE_XOR:
340d3da3eaeSThomas Petazzoni 		case CP110_GATE_PCIE_X4:
341c7e92defSGregory CLEMENT 		case CP110_GATE_EIP150:
342c7e92defSGregory CLEMENT 		case CP110_GATE_EIP197:
343c7e92defSGregory CLEMENT 			parent = x2core_name;
344d3da3eaeSThomas Petazzoni 			break;
345d3da3eaeSThomas Petazzoni 		default:
346d3da3eaeSThomas Petazzoni 			parent = core_name;
347d3da3eaeSThomas Petazzoni 			break;
348d3da3eaeSThomas Petazzoni 		}
349f5667274SGregory CLEMENT 		hw = cp110_register_gate(gate_name[i], parent, regmap, i);
350d3da3eaeSThomas Petazzoni 
35157ecc7a0SMarcin Wojtas 		if (IS_ERR(hw)) {
35257ecc7a0SMarcin Wojtas 			ret = PTR_ERR(hw);
353d3da3eaeSThomas Petazzoni 			goto fail_gate;
354d3da3eaeSThomas Petazzoni 		}
355d3da3eaeSThomas Petazzoni 
35657ecc7a0SMarcin Wojtas 		cp110_clks[CP110_MAX_CORE_CLOCKS + i] = hw;
357d3da3eaeSThomas Petazzoni 	}
358d3da3eaeSThomas Petazzoni 
35957ecc7a0SMarcin Wojtas 	ret = of_clk_add_hw_provider(np, cp110_of_clk_get, cp110_clk_data);
360d3da3eaeSThomas Petazzoni 	if (ret)
361d3da3eaeSThomas Petazzoni 		goto fail_clk_add;
362d3da3eaeSThomas Petazzoni 
363a0245eb7SMarcin Wojtas 	platform_set_drvdata(pdev, cp110_clks);
364a0245eb7SMarcin Wojtas 
365d3da3eaeSThomas Petazzoni 	return 0;
366d3da3eaeSThomas Petazzoni 
367d3da3eaeSThomas Petazzoni fail_clk_add:
368d3da3eaeSThomas Petazzoni fail_gate:
369d3da3eaeSThomas Petazzoni 	for (i = 0; i < CP110_MAX_GATABLE_CLOCKS; i++) {
37057ecc7a0SMarcin Wojtas 		hw = cp110_clks[CP110_MAX_CORE_CLOCKS + i];
371d3da3eaeSThomas Petazzoni 
37257ecc7a0SMarcin Wojtas 		if (hw)
37357ecc7a0SMarcin Wojtas 			cp110_unregister_gate(hw);
374d3da3eaeSThomas Petazzoni 	}
375d3da3eaeSThomas Petazzoni 
376a45af6d3SKonstantin Porotchkin 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_SDIO]);
377a45af6d3SKonstantin Porotchkin fail_sdio:
37857ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_NAND]);
37929e6beb5SGregory CLEMENT fail_nand:
38057ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_CORE]);
38129e6beb5SGregory CLEMENT fail_core:
382c7e92defSGregory CLEMENT 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_X2CORE]);
38329e6beb5SGregory CLEMENT fail_eip:
38457ecc7a0SMarcin Wojtas 	clk_hw_unregister_fixed_factor(cp110_clks[CP110_CORE_PPV2]);
38529e6beb5SGregory CLEMENT fail_ppv2:
386c7e92defSGregory CLEMENT 	clk_hw_unregister_fixed_rate(cp110_clks[CP110_CORE_PLL0]);
387c7e92defSGregory CLEMENT fail_pll0:
388d3da3eaeSThomas Petazzoni 	return ret;
389d3da3eaeSThomas Petazzoni }
390d3da3eaeSThomas Petazzoni 
cp110_syscon_legacy_clk_probe(struct platform_device * pdev)3915ffeb5f5SGregory CLEMENT static int cp110_syscon_legacy_clk_probe(struct platform_device *pdev)
3925ffeb5f5SGregory CLEMENT {
3935ffeb5f5SGregory CLEMENT 	dev_warn(&pdev->dev, FW_WARN "Using legacy device tree binding\n");
3945ffeb5f5SGregory CLEMENT 	dev_warn(&pdev->dev, FW_WARN "Update your device tree:\n");
3955ffeb5f5SGregory CLEMENT 	dev_warn(&pdev->dev, FW_WARN
3965ffeb5f5SGregory CLEMENT 		 "This binding won't be supported in future kernels\n");
3975ffeb5f5SGregory CLEMENT 
3985ffeb5f5SGregory CLEMENT 	return cp110_syscon_common_probe(pdev, pdev->dev.of_node);
3995ffeb5f5SGregory CLEMENT }
4005ffeb5f5SGregory CLEMENT 
cp110_clk_probe(struct platform_device * pdev)4015ffeb5f5SGregory CLEMENT static int cp110_clk_probe(struct platform_device *pdev)
4025ffeb5f5SGregory CLEMENT {
4035ffeb5f5SGregory CLEMENT 	return cp110_syscon_common_probe(pdev, pdev->dev.of_node->parent);
4045ffeb5f5SGregory CLEMENT }
4055ffeb5f5SGregory CLEMENT 
4065ffeb5f5SGregory CLEMENT static const struct of_device_id cp110_syscon_legacy_of_match[] = {
407d3da3eaeSThomas Petazzoni 	{ .compatible = "marvell,cp110-system-controller0", },
408d3da3eaeSThomas Petazzoni 	{ }
409d3da3eaeSThomas Petazzoni };
410d3da3eaeSThomas Petazzoni 
4115ffeb5f5SGregory CLEMENT static struct platform_driver cp110_syscon_legacy_driver = {
4124a5aa069SStephen Boyd 	.probe = cp110_syscon_legacy_clk_probe,
413d3da3eaeSThomas Petazzoni 	.driver		= {
414d3da3eaeSThomas Petazzoni 		.name	= "marvell-cp110-system-controller0",
4155ffeb5f5SGregory CLEMENT 		.of_match_table = cp110_syscon_legacy_of_match,
4167acf751eSPaul Gortmaker 		.suppress_bind_attrs = true,
417d3da3eaeSThomas Petazzoni 	},
418d3da3eaeSThomas Petazzoni };
4195ffeb5f5SGregory CLEMENT builtin_platform_driver(cp110_syscon_legacy_driver);
4205ffeb5f5SGregory CLEMENT 
4215ffeb5f5SGregory CLEMENT static const struct of_device_id cp110_clock_of_match[] = {
4225ffeb5f5SGregory CLEMENT 	{ .compatible = "marvell,cp110-clock", },
4235ffeb5f5SGregory CLEMENT 	{ }
4245ffeb5f5SGregory CLEMENT };
4255ffeb5f5SGregory CLEMENT 
4265ffeb5f5SGregory CLEMENT static struct platform_driver cp110_clock_driver = {
4275ffeb5f5SGregory CLEMENT 	.probe = cp110_clk_probe,
4285ffeb5f5SGregory CLEMENT 	.driver		= {
4295ffeb5f5SGregory CLEMENT 		.name	= "marvell-cp110-clock",
4305ffeb5f5SGregory CLEMENT 		.of_match_table = cp110_clock_of_match,
4315ffeb5f5SGregory CLEMENT 		.suppress_bind_attrs = true,
4325ffeb5f5SGregory CLEMENT 	},
4335ffeb5f5SGregory CLEMENT };
4345ffeb5f5SGregory CLEMENT builtin_platform_driver(cp110_clock_driver);
435