1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/clk-provider.h>
4 #include <linux/init.h>
5 #include <linux/of.h>
6 #include <linux/of_device.h>
7 #include <linux/platform_device.h>
8 
9 struct clk_bcm63xx_table_entry {
10 	const char * const name;
11 	u8 bit;
12 	unsigned long flags;
13 };
14 
15 struct clk_bcm63xx_hw {
16 	void __iomem *regs;
17 	spinlock_t lock;
18 
19 	struct clk_hw_onecell_data data;
20 };
21 
22 static const struct clk_bcm63xx_table_entry bcm3368_clocks[] = {
23 	{ .name = "mac", .bit = 3, },
24 	{ .name = "tc", .bit = 5, },
25 	{ .name = "us_top", .bit = 6, },
26 	{ .name = "ds_top", .bit = 7, },
27 	{ .name = "acm", .bit = 8, },
28 	{ .name = "spi", .bit = 9, },
29 	{ .name = "usbs", .bit = 10, },
30 	{ .name = "bmu", .bit = 11, },
31 	{ .name = "pcm", .bit = 12, },
32 	{ .name = "ntp", .bit = 13, },
33 	{ .name = "acp_b", .bit = 14, },
34 	{ .name = "acp_a", .bit = 15, },
35 	{ .name = "emusb", .bit = 17, },
36 	{ .name = "enet0", .bit = 18, },
37 	{ .name = "enet1", .bit = 19, },
38 	{ .name = "usbsu", .bit = 20, },
39 	{ .name = "ephy", .bit = 21, },
40 	{ },
41 };
42 
43 static const struct clk_bcm63xx_table_entry bcm6328_clocks[] = {
44 	{ .name = "phy_mips", .bit = 0, },
45 	{ .name = "adsl_qproc", .bit = 1, },
46 	{ .name = "adsl_afe", .bit = 2, },
47 	{ .name = "adsl", .bit = 3, },
48 	{ .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, },
49 	{ .name = "sar", .bit = 5, },
50 	{ .name = "pcm", .bit = 6, },
51 	{ .name = "usbd", .bit = 7, },
52 	{ .name = "usbh", .bit = 8, },
53 	{ .name = "hsspi", .bit = 9, },
54 	{ .name = "pcie", .bit = 10, },
55 	{ .name = "robosw", .bit = 11, },
56 	{ },
57 };
58 
59 static const struct clk_bcm63xx_table_entry bcm6358_clocks[] = {
60 	{ .name = "enet", .bit = 4, },
61 	{ .name = "adslphy", .bit = 5, },
62 	{ .name = "pcm", .bit = 8, },
63 	{ .name = "spi", .bit = 9, },
64 	{ .name = "usbs", .bit = 10, },
65 	{ .name = "sar", .bit = 11, },
66 	{ .name = "emusb", .bit = 17, },
67 	{ .name = "enet0", .bit = 18, },
68 	{ .name = "enet1", .bit = 19, },
69 	{ .name = "usbsu", .bit = 20, },
70 	{ .name = "ephy", .bit = 21, },
71 	{ },
72 };
73 
74 static const struct clk_bcm63xx_table_entry bcm6362_clocks[] = {
75 	{ .name = "adsl_qproc", .bit = 1, },
76 	{ .name = "adsl_afe", .bit = 2, },
77 	{ .name = "adsl", .bit = 3, },
78 	{ .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, },
79 	{ .name = "wlan_ocp", .bit = 5, },
80 	{ .name = "swpkt_usb", .bit = 7, },
81 	{ .name = "swpkt_sar", .bit = 8, },
82 	{ .name = "sar", .bit = 9, },
83 	{ .name = "robosw", .bit = 10, },
84 	{ .name = "pcm", .bit = 11, },
85 	{ .name = "usbd", .bit = 12, },
86 	{ .name = "usbh", .bit = 13, },
87 	{ .name = "ipsec", .bit = 14, },
88 	{ .name = "spi", .bit = 15, },
89 	{ .name = "hsspi", .bit = 16, },
90 	{ .name = "pcie", .bit = 17, },
91 	{ .name = "fap", .bit = 18, },
92 	{ .name = "phymips", .bit = 19, },
93 	{ .name = "nand", .bit = 20, },
94 	{ },
95 };
96 
97 static const struct clk_bcm63xx_table_entry bcm6368_clocks[] = {
98 	{ .name = "vdsl_qproc", .bit = 2, },
99 	{ .name = "vdsl_afe", .bit = 3, },
100 	{ .name = "vdsl_bonding", .bit = 4, },
101 	{ .name = "vdsl", .bit = 5, },
102 	{ .name = "phymips", .bit = 6, },
103 	{ .name = "swpkt_usb", .bit = 7, },
104 	{ .name = "swpkt_sar", .bit = 8, },
105 	{ .name = "spi", .bit = 9, },
106 	{ .name = "usbd", .bit = 10, },
107 	{ .name = "sar", .bit = 11, },
108 	{ .name = "robosw", .bit = 12, },
109 	{ .name = "utopia", .bit = 13, },
110 	{ .name = "pcm", .bit = 14, },
111 	{ .name = "usbh", .bit = 15, },
112 	{ .name = "disable_gless", .bit = 16, },
113 	{ .name = "nand", .bit = 17, },
114 	{ .name = "ipsec", .bit = 18, },
115 	{ },
116 };
117 
118 static const struct clk_bcm63xx_table_entry bcm63268_clocks[] = {
119 	{ .name = "disable_gless", .bit = 0, },
120 	{ .name = "vdsl_qproc", .bit = 1, },
121 	{ .name = "vdsl_afe", .bit = 2, },
122 	{ .name = "vdsl", .bit = 3, },
123 	{ .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, },
124 	{ .name = "wlan_ocp", .bit = 5, },
125 	{ .name = "dect", .bit = 6, },
126 	{ .name = "fap0", .bit = 7, },
127 	{ .name = "fap1", .bit = 8, },
128 	{ .name = "sar", .bit = 9, },
129 	{ .name = "robosw", .bit = 10, },
130 	{ .name = "pcm", .bit = 11, },
131 	{ .name = "usbd", .bit = 12, },
132 	{ .name = "usbh", .bit = 13, },
133 	{ .name = "ipsec", .bit = 14, },
134 	{ .name = "spi", .bit = 15, },
135 	{ .name = "hsspi", .bit = 16, },
136 	{ .name = "pcie", .bit = 17, },
137 	{ .name = "phymips", .bit = 18, },
138 	{ .name = "gmac", .bit = 19, },
139 	{ .name = "nand", .bit = 20, },
140 	{ .name = "tbus", .bit = 27, },
141 	{ .name = "robosw250", .bit = 31, },
142 	{ },
143 };
144 
145 static int clk_bcm63xx_probe(struct platform_device *pdev)
146 {
147 	const struct clk_bcm63xx_table_entry *entry, *table;
148 	struct clk_bcm63xx_hw *hw;
149 	u8 maxbit = 0;
150 	int i, ret;
151 
152 	table = of_device_get_match_data(&pdev->dev);
153 	if (!table)
154 		return -EINVAL;
155 
156 	for (entry = table; entry->name; entry++)
157 		maxbit = max_t(u8, maxbit, entry->bit);
158 
159 	hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit),
160 			  GFP_KERNEL);
161 	if (!hw)
162 		return -ENOMEM;
163 
164 	platform_set_drvdata(pdev, hw);
165 
166 	spin_lock_init(&hw->lock);
167 
168 	hw->data.num = maxbit;
169 	for (i = 0; i < maxbit; i++)
170 		hw->data.hws[i] = ERR_PTR(-ENODEV);
171 
172 	hw->regs = devm_platform_ioremap_resource(pdev, 0);
173 	if (IS_ERR(hw->regs))
174 		return PTR_ERR(hw->regs);
175 
176 	for (entry = table; entry->name; entry++) {
177 		struct clk_hw *clk;
178 
179 		clk = clk_hw_register_gate(&pdev->dev, entry->name, NULL,
180 					   entry->flags, hw->regs, entry->bit,
181 					   CLK_GATE_BIG_ENDIAN, &hw->lock);
182 		if (IS_ERR(clk)) {
183 			ret = PTR_ERR(clk);
184 			goto out_err;
185 		}
186 
187 		hw->data.hws[entry->bit] = clk;
188 	}
189 
190 	ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
191 				     &hw->data);
192 	if (!ret)
193 		return 0;
194 out_err:
195 	for (i = 0; i < hw->data.num; i++) {
196 		if (!IS_ERR(hw->data.hws[i]))
197 			clk_hw_unregister_gate(hw->data.hws[i]);
198 	}
199 
200 	return ret;
201 }
202 
203 static int clk_bcm63xx_remove(struct platform_device *pdev)
204 {
205 	struct clk_bcm63xx_hw *hw = platform_get_drvdata(pdev);
206 	int i;
207 
208 	of_clk_del_provider(pdev->dev.of_node);
209 
210 	for (i = 0; i < hw->data.num; i++) {
211 		if (!IS_ERR(hw->data.hws[i]))
212 			clk_hw_unregister_gate(hw->data.hws[i]);
213 	}
214 
215 	return 0;
216 }
217 
218 static const struct of_device_id clk_bcm63xx_dt_ids[] = {
219 	{ .compatible = "brcm,bcm3368-clocks", .data = &bcm3368_clocks, },
220 	{ .compatible = "brcm,bcm6328-clocks", .data = &bcm6328_clocks, },
221 	{ .compatible = "brcm,bcm6358-clocks", .data = &bcm6358_clocks, },
222 	{ .compatible = "brcm,bcm6362-clocks", .data = &bcm6362_clocks, },
223 	{ .compatible = "brcm,bcm6368-clocks", .data = &bcm6368_clocks, },
224 	{ .compatible = "brcm,bcm63268-clocks", .data = &bcm63268_clocks, },
225 	{ }
226 };
227 
228 static struct platform_driver clk_bcm63xx = {
229 	.probe = clk_bcm63xx_probe,
230 	.remove = clk_bcm63xx_remove,
231 	.driver = {
232 		.name = "bcm63xx-clock",
233 		.of_match_table = clk_bcm63xx_dt_ids,
234 	},
235 };
236 builtin_platform_driver(clk_bcm63xx);
237