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 	struct resource *r;
150 	u8 maxbit = 0;
151 	int i, ret;
152 
153 	table = of_device_get_match_data(&pdev->dev);
154 	if (!table)
155 		return -EINVAL;
156 
157 	for (entry = table; entry->name; entry++)
158 		maxbit = max_t(u8, maxbit, entry->bit);
159 
160 	hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit),
161 			  GFP_KERNEL);
162 	if (!hw)
163 		return -ENOMEM;
164 
165 	platform_set_drvdata(pdev, hw);
166 
167 	spin_lock_init(&hw->lock);
168 
169 	hw->data.num = maxbit;
170 	for (i = 0; i < maxbit; i++)
171 		hw->data.hws[i] = ERR_PTR(-ENODEV);
172 
173 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
174 	hw->regs = devm_ioremap_resource(&pdev->dev, r);
175 	if (IS_ERR(hw->regs))
176 		return PTR_ERR(hw->regs);
177 
178 	for (entry = table; entry->name; entry++) {
179 		struct clk_hw *clk;
180 
181 		clk = clk_hw_register_gate(&pdev->dev, entry->name, NULL,
182 					   entry->flags, hw->regs, entry->bit,
183 					   CLK_GATE_BIG_ENDIAN, &hw->lock);
184 		if (IS_ERR(clk)) {
185 			ret = PTR_ERR(clk);
186 			goto out_err;
187 		}
188 
189 		hw->data.hws[entry->bit] = clk;
190 	}
191 
192 	ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
193 				     &hw->data);
194 	if (!ret)
195 		return 0;
196 out_err:
197 	for (i = 0; i < hw->data.num; i++) {
198 		if (!IS_ERR(hw->data.hws[i]))
199 			clk_hw_unregister_gate(hw->data.hws[i]);
200 	}
201 
202 	return ret;
203 }
204 
205 static int clk_bcm63xx_remove(struct platform_device *pdev)
206 {
207 	struct clk_bcm63xx_hw *hw = platform_get_drvdata(pdev);
208 	int i;
209 
210 	of_clk_del_provider(pdev->dev.of_node);
211 
212 	for (i = 0; i < hw->data.num; i++) {
213 		if (!IS_ERR(hw->data.hws[i]))
214 			clk_hw_unregister_gate(hw->data.hws[i]);
215 	}
216 
217 	return 0;
218 }
219 
220 static const struct of_device_id clk_bcm63xx_dt_ids[] = {
221 	{ .compatible = "brcm,bcm3368-clocks", .data = &bcm3368_clocks, },
222 	{ .compatible = "brcm,bcm6328-clocks", .data = &bcm6328_clocks, },
223 	{ .compatible = "brcm,bcm6358-clocks", .data = &bcm6358_clocks, },
224 	{ .compatible = "brcm,bcm6362-clocks", .data = &bcm6362_clocks, },
225 	{ .compatible = "brcm,bcm6368-clocks", .data = &bcm6368_clocks, },
226 	{ .compatible = "brcm,bcm63268-clocks", .data = &bcm63268_clocks, },
227 	{ }
228 };
229 
230 static struct platform_driver clk_bcm63xx = {
231 	.probe = clk_bcm63xx_probe,
232 	.remove = clk_bcm63xx_remove,
233 	.driver = {
234 		.name = "bcm63xx-clock",
235 		.of_match_table = clk_bcm63xx_dt_ids,
236 	},
237 };
238 builtin_platform_driver(clk_bcm63xx);
239