diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/cavium-octeon/octeon-platform.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/cavium-octeon/octeon-platform.c')
-rw-r--r-- | arch/mips/cavium-octeon/octeon-platform.c | 1141 |
1 files changed, 1141 insertions, 0 deletions
diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c new file mode 100644 index 000000000..ce05c0dd3 --- /dev/null +++ b/arch/mips/cavium-octeon/octeon-platform.c | |||
@@ -0,0 +1,1141 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file "COPYING" in the main directory of this archive | ||
4 | * for more details. | ||
5 | * | ||
6 | * Copyright (C) 2004-2017 Cavium, Inc. | ||
7 | * Copyright (C) 2008 Wind River Systems | ||
8 | */ | ||
9 | |||
10 | #include <linux/etherdevice.h> | ||
11 | #include <linux/of_platform.h> | ||
12 | #include <linux/of_fdt.h> | ||
13 | #include <linux/libfdt.h> | ||
14 | |||
15 | #include <asm/octeon/octeon.h> | ||
16 | #include <asm/octeon/cvmx-helper-board.h> | ||
17 | |||
18 | #ifdef CONFIG_USB | ||
19 | #include <linux/usb/ehci_def.h> | ||
20 | #include <linux/usb/ehci_pdriver.h> | ||
21 | #include <linux/usb/ohci_pdriver.h> | ||
22 | #include <asm/octeon/cvmx-uctlx-defs.h> | ||
23 | |||
24 | #define CVMX_UAHCX_EHCI_USBCMD (CVMX_ADD_IO_SEG(0x00016F0000000010ull)) | ||
25 | #define CVMX_UAHCX_OHCI_USBCMD (CVMX_ADD_IO_SEG(0x00016F0000000408ull)) | ||
26 | |||
27 | static DEFINE_MUTEX(octeon2_usb_clocks_mutex); | ||
28 | |||
29 | static int octeon2_usb_clock_start_cnt; | ||
30 | |||
31 | static int __init octeon2_usb_reset(void) | ||
32 | { | ||
33 | union cvmx_uctlx_clk_rst_ctl clk_rst_ctl; | ||
34 | u32 ucmd; | ||
35 | |||
36 | if (!OCTEON_IS_OCTEON2()) | ||
37 | return 0; | ||
38 | |||
39 | clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0)); | ||
40 | if (clk_rst_ctl.s.hrst) { | ||
41 | ucmd = cvmx_read64_uint32(CVMX_UAHCX_EHCI_USBCMD); | ||
42 | ucmd &= ~CMD_RUN; | ||
43 | cvmx_write64_uint32(CVMX_UAHCX_EHCI_USBCMD, ucmd); | ||
44 | mdelay(2); | ||
45 | ucmd |= CMD_RESET; | ||
46 | cvmx_write64_uint32(CVMX_UAHCX_EHCI_USBCMD, ucmd); | ||
47 | ucmd = cvmx_read64_uint32(CVMX_UAHCX_OHCI_USBCMD); | ||
48 | ucmd |= CMD_RUN; | ||
49 | cvmx_write64_uint32(CVMX_UAHCX_OHCI_USBCMD, ucmd); | ||
50 | } | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | arch_initcall(octeon2_usb_reset); | ||
55 | |||
56 | static void octeon2_usb_clocks_start(struct device *dev) | ||
57 | { | ||
58 | u64 div; | ||
59 | union cvmx_uctlx_if_ena if_ena; | ||
60 | union cvmx_uctlx_clk_rst_ctl clk_rst_ctl; | ||
61 | union cvmx_uctlx_uphy_portx_ctl_status port_ctl_status; | ||
62 | int i; | ||
63 | unsigned long io_clk_64_to_ns; | ||
64 | u32 clock_rate = 12000000; | ||
65 | bool is_crystal_clock = false; | ||
66 | |||
67 | |||
68 | mutex_lock(&octeon2_usb_clocks_mutex); | ||
69 | |||
70 | octeon2_usb_clock_start_cnt++; | ||
71 | if (octeon2_usb_clock_start_cnt != 1) | ||
72 | goto exit; | ||
73 | |||
74 | io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate(); | ||
75 | |||
76 | if (dev->of_node) { | ||
77 | struct device_node *uctl_node; | ||
78 | const char *clock_type; | ||
79 | |||
80 | uctl_node = of_get_parent(dev->of_node); | ||
81 | if (!uctl_node) { | ||
82 | dev_err(dev, "No UCTL device node\n"); | ||
83 | goto exit; | ||
84 | } | ||
85 | i = of_property_read_u32(uctl_node, | ||
86 | "refclk-frequency", &clock_rate); | ||
87 | if (i) { | ||
88 | dev_err(dev, "No UCTL \"refclk-frequency\"\n"); | ||
89 | of_node_put(uctl_node); | ||
90 | goto exit; | ||
91 | } | ||
92 | i = of_property_read_string(uctl_node, | ||
93 | "refclk-type", &clock_type); | ||
94 | of_node_put(uctl_node); | ||
95 | if (!i && strcmp("crystal", clock_type) == 0) | ||
96 | is_crystal_clock = true; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * Step 1: Wait for voltages stable. That surely happened | ||
101 | * before starting the kernel. | ||
102 | * | ||
103 | * Step 2: Enable SCLK of UCTL by writing UCTL0_IF_ENA[EN] = 1 | ||
104 | */ | ||
105 | if_ena.u64 = 0; | ||
106 | if_ena.s.en = 1; | ||
107 | cvmx_write_csr(CVMX_UCTLX_IF_ENA(0), if_ena.u64); | ||
108 | |||
109 | for (i = 0; i <= 1; i++) { | ||
110 | port_ctl_status.u64 = | ||
111 | cvmx_read_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0)); | ||
112 | /* Set txvreftune to 15 to obtain compliant 'eye' diagram. */ | ||
113 | port_ctl_status.s.txvreftune = 15; | ||
114 | port_ctl_status.s.txrisetune = 1; | ||
115 | port_ctl_status.s.txpreemphasistune = 1; | ||
116 | cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0), | ||
117 | port_ctl_status.u64); | ||
118 | } | ||
119 | |||
120 | /* Step 3: Configure the reference clock, PHY, and HCLK */ | ||
121 | clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0)); | ||
122 | |||
123 | /* | ||
124 | * If the UCTL looks like it has already been started, skip | ||
125 | * the initialization, otherwise bus errors are obtained. | ||
126 | */ | ||
127 | if (clk_rst_ctl.s.hrst) | ||
128 | goto end_clock; | ||
129 | /* 3a */ | ||
130 | clk_rst_ctl.s.p_por = 1; | ||
131 | clk_rst_ctl.s.hrst = 0; | ||
132 | clk_rst_ctl.s.p_prst = 0; | ||
133 | clk_rst_ctl.s.h_clkdiv_rst = 0; | ||
134 | clk_rst_ctl.s.o_clkdiv_rst = 0; | ||
135 | clk_rst_ctl.s.h_clkdiv_en = 0; | ||
136 | clk_rst_ctl.s.o_clkdiv_en = 0; | ||
137 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
138 | |||
139 | /* 3b */ | ||
140 | clk_rst_ctl.s.p_refclk_sel = is_crystal_clock ? 0 : 1; | ||
141 | switch (clock_rate) { | ||
142 | default: | ||
143 | pr_err("Invalid UCTL clock rate of %u, using 12000000 instead\n", | ||
144 | clock_rate); | ||
145 | fallthrough; | ||
146 | case 12000000: | ||
147 | clk_rst_ctl.s.p_refclk_div = 0; | ||
148 | break; | ||
149 | case 24000000: | ||
150 | clk_rst_ctl.s.p_refclk_div = 1; | ||
151 | break; | ||
152 | case 48000000: | ||
153 | clk_rst_ctl.s.p_refclk_div = 2; | ||
154 | break; | ||
155 | } | ||
156 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
157 | |||
158 | /* 3c */ | ||
159 | div = octeon_get_io_clock_rate() / 130000000ull; | ||
160 | |||
161 | switch (div) { | ||
162 | case 0: | ||
163 | div = 1; | ||
164 | break; | ||
165 | case 1: | ||
166 | case 2: | ||
167 | case 3: | ||
168 | case 4: | ||
169 | break; | ||
170 | case 5: | ||
171 | div = 4; | ||
172 | break; | ||
173 | case 6: | ||
174 | case 7: | ||
175 | div = 6; | ||
176 | break; | ||
177 | case 8: | ||
178 | case 9: | ||
179 | case 10: | ||
180 | case 11: | ||
181 | div = 8; | ||
182 | break; | ||
183 | default: | ||
184 | div = 12; | ||
185 | break; | ||
186 | } | ||
187 | clk_rst_ctl.s.h_div = div; | ||
188 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
189 | /* Read it back, */ | ||
190 | clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0)); | ||
191 | clk_rst_ctl.s.h_clkdiv_en = 1; | ||
192 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
193 | /* 3d */ | ||
194 | clk_rst_ctl.s.h_clkdiv_rst = 1; | ||
195 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
196 | |||
197 | /* 3e: delay 64 io clocks */ | ||
198 | ndelay(io_clk_64_to_ns); | ||
199 | |||
200 | /* | ||
201 | * Step 4: Program the power-on reset field in the UCTL | ||
202 | * clock-reset-control register. | ||
203 | */ | ||
204 | clk_rst_ctl.s.p_por = 0; | ||
205 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
206 | |||
207 | /* Step 5: Wait 3 ms for the PHY clock to start. */ | ||
208 | mdelay(3); | ||
209 | |||
210 | /* Steps 6..9 for ATE only, are skipped. */ | ||
211 | |||
212 | /* Step 10: Configure the OHCI_CLK48 and OHCI_CLK12 clocks. */ | ||
213 | /* 10a */ | ||
214 | clk_rst_ctl.s.o_clkdiv_rst = 1; | ||
215 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
216 | |||
217 | /* 10b */ | ||
218 | clk_rst_ctl.s.o_clkdiv_en = 1; | ||
219 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
220 | |||
221 | /* 10c */ | ||
222 | ndelay(io_clk_64_to_ns); | ||
223 | |||
224 | /* | ||
225 | * Step 11: Program the PHY reset field: | ||
226 | * UCTL0_CLK_RST_CTL[P_PRST] = 1 | ||
227 | */ | ||
228 | clk_rst_ctl.s.p_prst = 1; | ||
229 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
230 | |||
231 | /* Step 11b */ | ||
232 | udelay(1); | ||
233 | |||
234 | /* Step 11c */ | ||
235 | clk_rst_ctl.s.p_prst = 0; | ||
236 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
237 | |||
238 | /* Step 11d */ | ||
239 | mdelay(1); | ||
240 | |||
241 | /* Step 11e */ | ||
242 | clk_rst_ctl.s.p_prst = 1; | ||
243 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
244 | |||
245 | /* Step 12: Wait 1 uS. */ | ||
246 | udelay(1); | ||
247 | |||
248 | /* Step 13: Program the HRESET_N field: UCTL0_CLK_RST_CTL[HRST] = 1 */ | ||
249 | clk_rst_ctl.s.hrst = 1; | ||
250 | cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); | ||
251 | |||
252 | end_clock: | ||
253 | /* Set uSOF cycle period to 60,000 bits. */ | ||
254 | cvmx_write_csr(CVMX_UCTLX_EHCI_FLA(0), 0x20ull); | ||
255 | |||
256 | exit: | ||
257 | mutex_unlock(&octeon2_usb_clocks_mutex); | ||
258 | } | ||
259 | |||
260 | static void octeon2_usb_clocks_stop(void) | ||
261 | { | ||
262 | mutex_lock(&octeon2_usb_clocks_mutex); | ||
263 | octeon2_usb_clock_start_cnt--; | ||
264 | mutex_unlock(&octeon2_usb_clocks_mutex); | ||
265 | } | ||
266 | |||
267 | static int octeon_ehci_power_on(struct platform_device *pdev) | ||
268 | { | ||
269 | octeon2_usb_clocks_start(&pdev->dev); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static void octeon_ehci_power_off(struct platform_device *pdev) | ||
274 | { | ||
275 | octeon2_usb_clocks_stop(); | ||
276 | } | ||
277 | |||
278 | static struct usb_ehci_pdata octeon_ehci_pdata = { | ||
279 | /* Octeon EHCI matches CPU endianness. */ | ||
280 | #ifdef __BIG_ENDIAN | ||
281 | .big_endian_mmio = 1, | ||
282 | #endif | ||
283 | /* | ||
284 | * We can DMA from anywhere. But the descriptors must be in | ||
285 | * the lower 4GB. | ||
286 | */ | ||
287 | .dma_mask_64 = 0, | ||
288 | .power_on = octeon_ehci_power_on, | ||
289 | .power_off = octeon_ehci_power_off, | ||
290 | }; | ||
291 | |||
292 | static void __init octeon_ehci_hw_start(struct device *dev) | ||
293 | { | ||
294 | union cvmx_uctlx_ehci_ctl ehci_ctl; | ||
295 | |||
296 | octeon2_usb_clocks_start(dev); | ||
297 | |||
298 | ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0)); | ||
299 | /* Use 64-bit addressing. */ | ||
300 | ehci_ctl.s.ehci_64b_addr_en = 1; | ||
301 | ehci_ctl.s.l2c_addr_msb = 0; | ||
302 | #ifdef __BIG_ENDIAN | ||
303 | ehci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */ | ||
304 | ehci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */ | ||
305 | #else | ||
306 | ehci_ctl.s.l2c_buff_emod = 0; /* not swapped. */ | ||
307 | ehci_ctl.s.l2c_desc_emod = 0; /* not swapped. */ | ||
308 | ehci_ctl.s.inv_reg_a2 = 1; | ||
309 | #endif | ||
310 | cvmx_write_csr(CVMX_UCTLX_EHCI_CTL(0), ehci_ctl.u64); | ||
311 | |||
312 | octeon2_usb_clocks_stop(); | ||
313 | } | ||
314 | |||
315 | static int __init octeon_ehci_device_init(void) | ||
316 | { | ||
317 | struct platform_device *pd; | ||
318 | struct device_node *ehci_node; | ||
319 | int ret = 0; | ||
320 | |||
321 | ehci_node = of_find_node_by_name(NULL, "ehci"); | ||
322 | if (!ehci_node) | ||
323 | return 0; | ||
324 | |||
325 | pd = of_find_device_by_node(ehci_node); | ||
326 | of_node_put(ehci_node); | ||
327 | if (!pd) | ||
328 | return 0; | ||
329 | |||
330 | pd->dev.platform_data = &octeon_ehci_pdata; | ||
331 | octeon_ehci_hw_start(&pd->dev); | ||
332 | put_device(&pd->dev); | ||
333 | |||
334 | return ret; | ||
335 | } | ||
336 | device_initcall(octeon_ehci_device_init); | ||
337 | |||
338 | static int octeon_ohci_power_on(struct platform_device *pdev) | ||
339 | { | ||
340 | octeon2_usb_clocks_start(&pdev->dev); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static void octeon_ohci_power_off(struct platform_device *pdev) | ||
345 | { | ||
346 | octeon2_usb_clocks_stop(); | ||
347 | } | ||
348 | |||
349 | static struct usb_ohci_pdata octeon_ohci_pdata = { | ||
350 | /* Octeon OHCI matches CPU endianness. */ | ||
351 | #ifdef __BIG_ENDIAN | ||
352 | .big_endian_mmio = 1, | ||
353 | #endif | ||
354 | .power_on = octeon_ohci_power_on, | ||
355 | .power_off = octeon_ohci_power_off, | ||
356 | }; | ||
357 | |||
358 | static void __init octeon_ohci_hw_start(struct device *dev) | ||
359 | { | ||
360 | union cvmx_uctlx_ohci_ctl ohci_ctl; | ||
361 | |||
362 | octeon2_usb_clocks_start(dev); | ||
363 | |||
364 | ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0)); | ||
365 | ohci_ctl.s.l2c_addr_msb = 0; | ||
366 | #ifdef __BIG_ENDIAN | ||
367 | ohci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */ | ||
368 | ohci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */ | ||
369 | #else | ||
370 | ohci_ctl.s.l2c_buff_emod = 0; /* not swapped. */ | ||
371 | ohci_ctl.s.l2c_desc_emod = 0; /* not swapped. */ | ||
372 | ohci_ctl.s.inv_reg_a2 = 1; | ||
373 | #endif | ||
374 | cvmx_write_csr(CVMX_UCTLX_OHCI_CTL(0), ohci_ctl.u64); | ||
375 | |||
376 | octeon2_usb_clocks_stop(); | ||
377 | } | ||
378 | |||
379 | static int __init octeon_ohci_device_init(void) | ||
380 | { | ||
381 | struct platform_device *pd; | ||
382 | struct device_node *ohci_node; | ||
383 | int ret = 0; | ||
384 | |||
385 | ohci_node = of_find_node_by_name(NULL, "ohci"); | ||
386 | if (!ohci_node) | ||
387 | return 0; | ||
388 | |||
389 | pd = of_find_device_by_node(ohci_node); | ||
390 | of_node_put(ohci_node); | ||
391 | if (!pd) | ||
392 | return 0; | ||
393 | |||
394 | pd->dev.platform_data = &octeon_ohci_pdata; | ||
395 | octeon_ohci_hw_start(&pd->dev); | ||
396 | put_device(&pd->dev); | ||
397 | |||
398 | return ret; | ||
399 | } | ||
400 | device_initcall(octeon_ohci_device_init); | ||
401 | |||
402 | #endif /* CONFIG_USB */ | ||
403 | |||
404 | /* Octeon Random Number Generator. */ | ||
405 | static int __init octeon_rng_device_init(void) | ||
406 | { | ||
407 | struct platform_device *pd; | ||
408 | int ret = 0; | ||
409 | |||
410 | struct resource rng_resources[] = { | ||
411 | { | ||
412 | .flags = IORESOURCE_MEM, | ||
413 | .start = XKPHYS_TO_PHYS(CVMX_RNM_CTL_STATUS), | ||
414 | .end = XKPHYS_TO_PHYS(CVMX_RNM_CTL_STATUS) + 0xf | ||
415 | }, { | ||
416 | .flags = IORESOURCE_MEM, | ||
417 | .start = cvmx_build_io_address(8, 0), | ||
418 | .end = cvmx_build_io_address(8, 0) + 0x7 | ||
419 | } | ||
420 | }; | ||
421 | |||
422 | pd = platform_device_alloc("octeon_rng", -1); | ||
423 | if (!pd) { | ||
424 | ret = -ENOMEM; | ||
425 | goto out; | ||
426 | } | ||
427 | |||
428 | ret = platform_device_add_resources(pd, rng_resources, | ||
429 | ARRAY_SIZE(rng_resources)); | ||
430 | if (ret) | ||
431 | goto fail; | ||
432 | |||
433 | ret = platform_device_add(pd); | ||
434 | if (ret) | ||
435 | goto fail; | ||
436 | |||
437 | return ret; | ||
438 | fail: | ||
439 | platform_device_put(pd); | ||
440 | |||
441 | out: | ||
442 | return ret; | ||
443 | } | ||
444 | device_initcall(octeon_rng_device_init); | ||
445 | |||
446 | static const struct of_device_id octeon_ids[] __initconst = { | ||
447 | { .compatible = "simple-bus", }, | ||
448 | { .compatible = "cavium,octeon-6335-uctl", }, | ||
449 | { .compatible = "cavium,octeon-5750-usbn", }, | ||
450 | { .compatible = "cavium,octeon-3860-bootbus", }, | ||
451 | { .compatible = "cavium,mdio-mux", }, | ||
452 | { .compatible = "gpio-leds", }, | ||
453 | { .compatible = "cavium,octeon-7130-usb-uctl", }, | ||
454 | {}, | ||
455 | }; | ||
456 | |||
457 | static bool __init octeon_has_88e1145(void) | ||
458 | { | ||
459 | return !OCTEON_IS_MODEL(OCTEON_CN52XX) && | ||
460 | !OCTEON_IS_MODEL(OCTEON_CN6XXX) && | ||
461 | !OCTEON_IS_MODEL(OCTEON_CN56XX); | ||
462 | } | ||
463 | |||
464 | static bool __init octeon_has_fixed_link(int ipd_port) | ||
465 | { | ||
466 | switch (cvmx_sysinfo_get()->board_type) { | ||
467 | case CVMX_BOARD_TYPE_CN3005_EVB_HS5: | ||
468 | case CVMX_BOARD_TYPE_CN3010_EVB_HS5: | ||
469 | case CVMX_BOARD_TYPE_CN3020_EVB_HS5: | ||
470 | case CVMX_BOARD_TYPE_CUST_NB5: | ||
471 | case CVMX_BOARD_TYPE_EBH3100: | ||
472 | /* Port 1 on these boards is always gigabit. */ | ||
473 | return ipd_port == 1; | ||
474 | case CVMX_BOARD_TYPE_BBGW_REF: | ||
475 | /* Ports 0 and 1 connect to the switch. */ | ||
476 | return ipd_port == 0 || ipd_port == 1; | ||
477 | } | ||
478 | return false; | ||
479 | } | ||
480 | |||
481 | static void __init octeon_fdt_set_phy(int eth, int phy_addr) | ||
482 | { | ||
483 | const __be32 *phy_handle; | ||
484 | const __be32 *alt_phy_handle; | ||
485 | const __be32 *reg; | ||
486 | u32 phandle; | ||
487 | int phy; | ||
488 | int alt_phy; | ||
489 | const char *p; | ||
490 | int current_len; | ||
491 | char new_name[20]; | ||
492 | |||
493 | phy_handle = fdt_getprop(initial_boot_params, eth, "phy-handle", NULL); | ||
494 | if (!phy_handle) | ||
495 | return; | ||
496 | |||
497 | phandle = be32_to_cpup(phy_handle); | ||
498 | phy = fdt_node_offset_by_phandle(initial_boot_params, phandle); | ||
499 | |||
500 | alt_phy_handle = fdt_getprop(initial_boot_params, eth, "cavium,alt-phy-handle", NULL); | ||
501 | if (alt_phy_handle) { | ||
502 | u32 alt_phandle = be32_to_cpup(alt_phy_handle); | ||
503 | |||
504 | alt_phy = fdt_node_offset_by_phandle(initial_boot_params, alt_phandle); | ||
505 | } else { | ||
506 | alt_phy = -1; | ||
507 | } | ||
508 | |||
509 | if (phy_addr < 0 || phy < 0) { | ||
510 | /* Delete the PHY things */ | ||
511 | fdt_nop_property(initial_boot_params, eth, "phy-handle"); | ||
512 | /* This one may fail */ | ||
513 | fdt_nop_property(initial_boot_params, eth, "cavium,alt-phy-handle"); | ||
514 | if (phy >= 0) | ||
515 | fdt_nop_node(initial_boot_params, phy); | ||
516 | if (alt_phy >= 0) | ||
517 | fdt_nop_node(initial_boot_params, alt_phy); | ||
518 | return; | ||
519 | } | ||
520 | |||
521 | if (phy_addr >= 256 && alt_phy > 0) { | ||
522 | const struct fdt_property *phy_prop; | ||
523 | struct fdt_property *alt_prop; | ||
524 | fdt32_t phy_handle_name; | ||
525 | |||
526 | /* Use the alt phy node instead.*/ | ||
527 | phy_prop = fdt_get_property(initial_boot_params, eth, "phy-handle", NULL); | ||
528 | phy_handle_name = phy_prop->nameoff; | ||
529 | fdt_nop_node(initial_boot_params, phy); | ||
530 | fdt_nop_property(initial_boot_params, eth, "phy-handle"); | ||
531 | alt_prop = fdt_get_property_w(initial_boot_params, eth, "cavium,alt-phy-handle", NULL); | ||
532 | alt_prop->nameoff = phy_handle_name; | ||
533 | phy = alt_phy; | ||
534 | } | ||
535 | |||
536 | phy_addr &= 0xff; | ||
537 | |||
538 | if (octeon_has_88e1145()) { | ||
539 | fdt_nop_property(initial_boot_params, phy, "marvell,reg-init"); | ||
540 | memset(new_name, 0, sizeof(new_name)); | ||
541 | strcpy(new_name, "marvell,88e1145"); | ||
542 | p = fdt_getprop(initial_boot_params, phy, "compatible", | ||
543 | ¤t_len); | ||
544 | if (p && current_len >= strlen(new_name)) | ||
545 | fdt_setprop_inplace(initial_boot_params, phy, | ||
546 | "compatible", new_name, current_len); | ||
547 | } | ||
548 | |||
549 | reg = fdt_getprop(initial_boot_params, phy, "reg", NULL); | ||
550 | if (phy_addr == be32_to_cpup(reg)) | ||
551 | return; | ||
552 | |||
553 | fdt_setprop_inplace_cell(initial_boot_params, phy, "reg", phy_addr); | ||
554 | |||
555 | snprintf(new_name, sizeof(new_name), "ethernet-phy@%x", phy_addr); | ||
556 | |||
557 | p = fdt_get_name(initial_boot_params, phy, ¤t_len); | ||
558 | if (p && current_len == strlen(new_name)) | ||
559 | fdt_set_name(initial_boot_params, phy, new_name); | ||
560 | else | ||
561 | pr_err("Error: could not rename ethernet phy: <%s>", p); | ||
562 | } | ||
563 | |||
564 | static void __init octeon_fdt_set_mac_addr(int n, u64 *pmac) | ||
565 | { | ||
566 | const u8 *old_mac; | ||
567 | int old_len; | ||
568 | u8 new_mac[6]; | ||
569 | u64 mac = *pmac; | ||
570 | int r; | ||
571 | |||
572 | old_mac = fdt_getprop(initial_boot_params, n, "local-mac-address", | ||
573 | &old_len); | ||
574 | if (!old_mac || old_len != 6 || is_valid_ether_addr(old_mac)) | ||
575 | return; | ||
576 | |||
577 | new_mac[0] = (mac >> 40) & 0xff; | ||
578 | new_mac[1] = (mac >> 32) & 0xff; | ||
579 | new_mac[2] = (mac >> 24) & 0xff; | ||
580 | new_mac[3] = (mac >> 16) & 0xff; | ||
581 | new_mac[4] = (mac >> 8) & 0xff; | ||
582 | new_mac[5] = mac & 0xff; | ||
583 | |||
584 | r = fdt_setprop_inplace(initial_boot_params, n, "local-mac-address", | ||
585 | new_mac, sizeof(new_mac)); | ||
586 | |||
587 | if (r) { | ||
588 | pr_err("Setting \"local-mac-address\" failed %d", r); | ||
589 | return; | ||
590 | } | ||
591 | *pmac = mac + 1; | ||
592 | } | ||
593 | |||
594 | static void __init octeon_fdt_rm_ethernet(int node) | ||
595 | { | ||
596 | const __be32 *phy_handle; | ||
597 | |||
598 | phy_handle = fdt_getprop(initial_boot_params, node, "phy-handle", NULL); | ||
599 | if (phy_handle) { | ||
600 | u32 ph = be32_to_cpup(phy_handle); | ||
601 | int p = fdt_node_offset_by_phandle(initial_boot_params, ph); | ||
602 | |||
603 | if (p >= 0) | ||
604 | fdt_nop_node(initial_boot_params, p); | ||
605 | } | ||
606 | fdt_nop_node(initial_boot_params, node); | ||
607 | } | ||
608 | |||
609 | static void __init _octeon_rx_tx_delay(int eth, int rx_delay, int tx_delay) | ||
610 | { | ||
611 | fdt_setprop_inplace_cell(initial_boot_params, eth, "rx-delay", | ||
612 | rx_delay); | ||
613 | fdt_setprop_inplace_cell(initial_boot_params, eth, "tx-delay", | ||
614 | tx_delay); | ||
615 | } | ||
616 | |||
617 | static void __init octeon_rx_tx_delay(int eth, int iface, int port) | ||
618 | { | ||
619 | switch (cvmx_sysinfo_get()->board_type) { | ||
620 | case CVMX_BOARD_TYPE_CN3005_EVB_HS5: | ||
621 | if (iface == 0) { | ||
622 | if (port == 0) { | ||
623 | /* | ||
624 | * Boards with gigabit WAN ports need a | ||
625 | * different setting that is compatible with | ||
626 | * 100 Mbit settings | ||
627 | */ | ||
628 | _octeon_rx_tx_delay(eth, 0xc, 0x0c); | ||
629 | return; | ||
630 | } else if (port == 1) { | ||
631 | /* Different config for switch port. */ | ||
632 | _octeon_rx_tx_delay(eth, 0x0, 0x0); | ||
633 | return; | ||
634 | } | ||
635 | } | ||
636 | break; | ||
637 | case CVMX_BOARD_TYPE_UBNT_E100: | ||
638 | if (iface == 0 && port <= 2) { | ||
639 | _octeon_rx_tx_delay(eth, 0x0, 0x10); | ||
640 | return; | ||
641 | } | ||
642 | break; | ||
643 | } | ||
644 | fdt_nop_property(initial_boot_params, eth, "rx-delay"); | ||
645 | fdt_nop_property(initial_boot_params, eth, "tx-delay"); | ||
646 | } | ||
647 | |||
648 | static void __init octeon_fdt_pip_port(int iface, int i, int p, int max) | ||
649 | { | ||
650 | char name_buffer[20]; | ||
651 | int eth; | ||
652 | int phy_addr; | ||
653 | int ipd_port; | ||
654 | int fixed_link; | ||
655 | |||
656 | snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", p); | ||
657 | eth = fdt_subnode_offset(initial_boot_params, iface, name_buffer); | ||
658 | if (eth < 0) | ||
659 | return; | ||
660 | if (p > max) { | ||
661 | pr_debug("Deleting port %x:%x\n", i, p); | ||
662 | octeon_fdt_rm_ethernet(eth); | ||
663 | return; | ||
664 | } | ||
665 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) | ||
666 | ipd_port = (0x100 * i) + (0x10 * p) + 0x800; | ||
667 | else | ||
668 | ipd_port = 16 * i + p; | ||
669 | |||
670 | phy_addr = cvmx_helper_board_get_mii_address(ipd_port); | ||
671 | octeon_fdt_set_phy(eth, phy_addr); | ||
672 | |||
673 | fixed_link = fdt_subnode_offset(initial_boot_params, eth, "fixed-link"); | ||
674 | if (fixed_link < 0) | ||
675 | WARN_ON(octeon_has_fixed_link(ipd_port)); | ||
676 | else if (!octeon_has_fixed_link(ipd_port)) | ||
677 | fdt_nop_node(initial_boot_params, fixed_link); | ||
678 | octeon_rx_tx_delay(eth, i, p); | ||
679 | } | ||
680 | |||
681 | static void __init octeon_fdt_pip_iface(int pip, int idx) | ||
682 | { | ||
683 | char name_buffer[20]; | ||
684 | int iface; | ||
685 | int p; | ||
686 | int count = 0; | ||
687 | |||
688 | snprintf(name_buffer, sizeof(name_buffer), "interface@%d", idx); | ||
689 | iface = fdt_subnode_offset(initial_boot_params, pip, name_buffer); | ||
690 | if (iface < 0) | ||
691 | return; | ||
692 | |||
693 | if (cvmx_helper_interface_enumerate(idx) == 0) | ||
694 | count = cvmx_helper_ports_on_interface(idx); | ||
695 | |||
696 | for (p = 0; p < 16; p++) | ||
697 | octeon_fdt_pip_port(iface, idx, p, count - 1); | ||
698 | } | ||
699 | |||
700 | void __init octeon_fill_mac_addresses(void) | ||
701 | { | ||
702 | const char *alias_prop; | ||
703 | char name_buffer[20]; | ||
704 | u64 mac_addr_base; | ||
705 | int aliases; | ||
706 | int pip; | ||
707 | int i; | ||
708 | |||
709 | aliases = fdt_path_offset(initial_boot_params, "/aliases"); | ||
710 | if (aliases < 0) | ||
711 | return; | ||
712 | |||
713 | mac_addr_base = | ||
714 | ((octeon_bootinfo->mac_addr_base[0] & 0xffull)) << 40 | | ||
715 | ((octeon_bootinfo->mac_addr_base[1] & 0xffull)) << 32 | | ||
716 | ((octeon_bootinfo->mac_addr_base[2] & 0xffull)) << 24 | | ||
717 | ((octeon_bootinfo->mac_addr_base[3] & 0xffull)) << 16 | | ||
718 | ((octeon_bootinfo->mac_addr_base[4] & 0xffull)) << 8 | | ||
719 | (octeon_bootinfo->mac_addr_base[5] & 0xffull); | ||
720 | |||
721 | for (i = 0; i < 2; i++) { | ||
722 | int mgmt; | ||
723 | |||
724 | snprintf(name_buffer, sizeof(name_buffer), "mix%d", i); | ||
725 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
726 | name_buffer, NULL); | ||
727 | if (!alias_prop) | ||
728 | continue; | ||
729 | mgmt = fdt_path_offset(initial_boot_params, alias_prop); | ||
730 | if (mgmt < 0) | ||
731 | continue; | ||
732 | octeon_fdt_set_mac_addr(mgmt, &mac_addr_base); | ||
733 | } | ||
734 | |||
735 | alias_prop = fdt_getprop(initial_boot_params, aliases, "pip", NULL); | ||
736 | if (!alias_prop) | ||
737 | return; | ||
738 | |||
739 | pip = fdt_path_offset(initial_boot_params, alias_prop); | ||
740 | if (pip < 0) | ||
741 | return; | ||
742 | |||
743 | for (i = 0; i <= 4; i++) { | ||
744 | int iface; | ||
745 | int p; | ||
746 | |||
747 | snprintf(name_buffer, sizeof(name_buffer), "interface@%d", i); | ||
748 | iface = fdt_subnode_offset(initial_boot_params, pip, | ||
749 | name_buffer); | ||
750 | if (iface < 0) | ||
751 | continue; | ||
752 | for (p = 0; p < 16; p++) { | ||
753 | int eth; | ||
754 | |||
755 | snprintf(name_buffer, sizeof(name_buffer), | ||
756 | "ethernet@%x", p); | ||
757 | eth = fdt_subnode_offset(initial_boot_params, iface, | ||
758 | name_buffer); | ||
759 | if (eth < 0) | ||
760 | continue; | ||
761 | octeon_fdt_set_mac_addr(eth, &mac_addr_base); | ||
762 | } | ||
763 | } | ||
764 | } | ||
765 | |||
766 | int __init octeon_prune_device_tree(void) | ||
767 | { | ||
768 | int i, max_port, uart_mask; | ||
769 | const char *pip_path; | ||
770 | const char *alias_prop; | ||
771 | char name_buffer[20]; | ||
772 | int aliases; | ||
773 | |||
774 | if (fdt_check_header(initial_boot_params)) | ||
775 | panic("Corrupt Device Tree."); | ||
776 | |||
777 | WARN(octeon_bootinfo->board_type == CVMX_BOARD_TYPE_CUST_DSR1000N, | ||
778 | "Built-in DTB booting is deprecated on %s. Please switch to use appended DTB.", | ||
779 | cvmx_board_type_to_string(octeon_bootinfo->board_type)); | ||
780 | |||
781 | aliases = fdt_path_offset(initial_boot_params, "/aliases"); | ||
782 | if (aliases < 0) { | ||
783 | pr_err("Error: No /aliases node in device tree."); | ||
784 | return -EINVAL; | ||
785 | } | ||
786 | |||
787 | if (OCTEON_IS_MODEL(OCTEON_CN52XX) || OCTEON_IS_MODEL(OCTEON_CN63XX)) | ||
788 | max_port = 2; | ||
789 | else if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN68XX)) | ||
790 | max_port = 1; | ||
791 | else | ||
792 | max_port = 0; | ||
793 | |||
794 | if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E) | ||
795 | max_port = 0; | ||
796 | |||
797 | for (i = 0; i < 2; i++) { | ||
798 | int mgmt; | ||
799 | |||
800 | snprintf(name_buffer, sizeof(name_buffer), | ||
801 | "mix%d", i); | ||
802 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
803 | name_buffer, NULL); | ||
804 | if (alias_prop) { | ||
805 | mgmt = fdt_path_offset(initial_boot_params, alias_prop); | ||
806 | if (mgmt < 0) | ||
807 | continue; | ||
808 | if (i >= max_port) { | ||
809 | pr_debug("Deleting mix%d\n", i); | ||
810 | octeon_fdt_rm_ethernet(mgmt); | ||
811 | fdt_nop_property(initial_boot_params, aliases, | ||
812 | name_buffer); | ||
813 | } else { | ||
814 | int phy_addr = cvmx_helper_board_get_mii_address(CVMX_HELPER_BOARD_MGMT_IPD_PORT + i); | ||
815 | |||
816 | octeon_fdt_set_phy(mgmt, phy_addr); | ||
817 | } | ||
818 | } | ||
819 | } | ||
820 | |||
821 | pip_path = fdt_getprop(initial_boot_params, aliases, "pip", NULL); | ||
822 | if (pip_path) { | ||
823 | int pip = fdt_path_offset(initial_boot_params, pip_path); | ||
824 | |||
825 | if (pip >= 0) | ||
826 | for (i = 0; i <= 4; i++) | ||
827 | octeon_fdt_pip_iface(pip, i); | ||
828 | } | ||
829 | |||
830 | /* I2C */ | ||
831 | if (OCTEON_IS_MODEL(OCTEON_CN52XX) || | ||
832 | OCTEON_IS_MODEL(OCTEON_CN63XX) || | ||
833 | OCTEON_IS_MODEL(OCTEON_CN68XX) || | ||
834 | OCTEON_IS_MODEL(OCTEON_CN56XX)) | ||
835 | max_port = 2; | ||
836 | else | ||
837 | max_port = 1; | ||
838 | |||
839 | for (i = 0; i < 2; i++) { | ||
840 | int i2c; | ||
841 | |||
842 | snprintf(name_buffer, sizeof(name_buffer), | ||
843 | "twsi%d", i); | ||
844 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
845 | name_buffer, NULL); | ||
846 | |||
847 | if (alias_prop) { | ||
848 | i2c = fdt_path_offset(initial_boot_params, alias_prop); | ||
849 | if (i2c < 0) | ||
850 | continue; | ||
851 | if (i >= max_port) { | ||
852 | pr_debug("Deleting twsi%d\n", i); | ||
853 | fdt_nop_node(initial_boot_params, i2c); | ||
854 | fdt_nop_property(initial_boot_params, aliases, | ||
855 | name_buffer); | ||
856 | } | ||
857 | } | ||
858 | } | ||
859 | |||
860 | /* SMI/MDIO */ | ||
861 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) | ||
862 | max_port = 4; | ||
863 | else if (OCTEON_IS_MODEL(OCTEON_CN52XX) || | ||
864 | OCTEON_IS_MODEL(OCTEON_CN63XX) || | ||
865 | OCTEON_IS_MODEL(OCTEON_CN56XX)) | ||
866 | max_port = 2; | ||
867 | else | ||
868 | max_port = 1; | ||
869 | |||
870 | for (i = 0; i < 2; i++) { | ||
871 | int i2c; | ||
872 | |||
873 | snprintf(name_buffer, sizeof(name_buffer), | ||
874 | "smi%d", i); | ||
875 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
876 | name_buffer, NULL); | ||
877 | if (alias_prop) { | ||
878 | i2c = fdt_path_offset(initial_boot_params, alias_prop); | ||
879 | if (i2c < 0) | ||
880 | continue; | ||
881 | if (i >= max_port) { | ||
882 | pr_debug("Deleting smi%d\n", i); | ||
883 | fdt_nop_node(initial_boot_params, i2c); | ||
884 | fdt_nop_property(initial_boot_params, aliases, | ||
885 | name_buffer); | ||
886 | } | ||
887 | } | ||
888 | } | ||
889 | |||
890 | /* Serial */ | ||
891 | uart_mask = 3; | ||
892 | |||
893 | /* Right now CN52XX is the only chip with a third uart */ | ||
894 | if (OCTEON_IS_MODEL(OCTEON_CN52XX)) | ||
895 | uart_mask |= 4; /* uart2 */ | ||
896 | |||
897 | for (i = 0; i < 3; i++) { | ||
898 | int uart; | ||
899 | |||
900 | snprintf(name_buffer, sizeof(name_buffer), | ||
901 | "uart%d", i); | ||
902 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
903 | name_buffer, NULL); | ||
904 | |||
905 | if (alias_prop) { | ||
906 | uart = fdt_path_offset(initial_boot_params, alias_prop); | ||
907 | if (uart_mask & (1 << i)) { | ||
908 | __be32 f; | ||
909 | |||
910 | f = cpu_to_be32(octeon_get_io_clock_rate()); | ||
911 | fdt_setprop_inplace(initial_boot_params, | ||
912 | uart, "clock-frequency", | ||
913 | &f, sizeof(f)); | ||
914 | continue; | ||
915 | } | ||
916 | pr_debug("Deleting uart%d\n", i); | ||
917 | fdt_nop_node(initial_boot_params, uart); | ||
918 | fdt_nop_property(initial_boot_params, aliases, | ||
919 | name_buffer); | ||
920 | } | ||
921 | } | ||
922 | |||
923 | /* Compact Flash */ | ||
924 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
925 | "cf0", NULL); | ||
926 | if (alias_prop) { | ||
927 | union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg; | ||
928 | unsigned long base_ptr, region_base, region_size; | ||
929 | unsigned long region1_base = 0; | ||
930 | unsigned long region1_size = 0; | ||
931 | int cs, bootbus; | ||
932 | bool is_16bit = false; | ||
933 | bool is_true_ide = false; | ||
934 | __be32 new_reg[6]; | ||
935 | __be32 *ranges; | ||
936 | int len; | ||
937 | |||
938 | int cf = fdt_path_offset(initial_boot_params, alias_prop); | ||
939 | |||
940 | base_ptr = 0; | ||
941 | if (octeon_bootinfo->major_version == 1 | ||
942 | && octeon_bootinfo->minor_version >= 1) { | ||
943 | if (octeon_bootinfo->compact_flash_common_base_addr) | ||
944 | base_ptr = octeon_bootinfo->compact_flash_common_base_addr; | ||
945 | } else { | ||
946 | base_ptr = 0x1d000800; | ||
947 | } | ||
948 | |||
949 | if (!base_ptr) | ||
950 | goto no_cf; | ||
951 | |||
952 | /* Find CS0 region. */ | ||
953 | for (cs = 0; cs < 8; cs++) { | ||
954 | mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs)); | ||
955 | region_base = mio_boot_reg_cfg.s.base << 16; | ||
956 | region_size = (mio_boot_reg_cfg.s.size + 1) << 16; | ||
957 | if (mio_boot_reg_cfg.s.en && base_ptr >= region_base | ||
958 | && base_ptr < region_base + region_size) { | ||
959 | is_16bit = mio_boot_reg_cfg.s.width; | ||
960 | break; | ||
961 | } | ||
962 | } | ||
963 | if (cs >= 7) { | ||
964 | /* cs and cs + 1 are CS0 and CS1, both must be less than 8. */ | ||
965 | goto no_cf; | ||
966 | } | ||
967 | |||
968 | if (!(base_ptr & 0xfffful)) { | ||
969 | /* | ||
970 | * Boot loader signals availability of DMA (true_ide | ||
971 | * mode) by setting low order bits of base_ptr to | ||
972 | * zero. | ||
973 | */ | ||
974 | |||
975 | /* Asume that CS1 immediately follows. */ | ||
976 | mio_boot_reg_cfg.u64 = | ||
977 | cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs + 1)); | ||
978 | region1_base = mio_boot_reg_cfg.s.base << 16; | ||
979 | region1_size = (mio_boot_reg_cfg.s.size + 1) << 16; | ||
980 | if (!mio_boot_reg_cfg.s.en) | ||
981 | goto no_cf; | ||
982 | is_true_ide = true; | ||
983 | |||
984 | } else { | ||
985 | fdt_nop_property(initial_boot_params, cf, "cavium,true-ide"); | ||
986 | fdt_nop_property(initial_boot_params, cf, "cavium,dma-engine-handle"); | ||
987 | if (!is_16bit) { | ||
988 | __be32 width = cpu_to_be32(8); | ||
989 | |||
990 | fdt_setprop_inplace(initial_boot_params, cf, | ||
991 | "cavium,bus-width", &width, sizeof(width)); | ||
992 | } | ||
993 | } | ||
994 | new_reg[0] = cpu_to_be32(cs); | ||
995 | new_reg[1] = cpu_to_be32(0); | ||
996 | new_reg[2] = cpu_to_be32(0x10000); | ||
997 | new_reg[3] = cpu_to_be32(cs + 1); | ||
998 | new_reg[4] = cpu_to_be32(0); | ||
999 | new_reg[5] = cpu_to_be32(0x10000); | ||
1000 | fdt_setprop_inplace(initial_boot_params, cf, | ||
1001 | "reg", new_reg, sizeof(new_reg)); | ||
1002 | |||
1003 | bootbus = fdt_parent_offset(initial_boot_params, cf); | ||
1004 | if (bootbus < 0) | ||
1005 | goto no_cf; | ||
1006 | ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len); | ||
1007 | if (!ranges || len < (5 * 8 * sizeof(__be32))) | ||
1008 | goto no_cf; | ||
1009 | |||
1010 | ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32); | ||
1011 | ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff); | ||
1012 | ranges[(cs * 5) + 4] = cpu_to_be32(region_size); | ||
1013 | if (is_true_ide) { | ||
1014 | cs++; | ||
1015 | ranges[(cs * 5) + 2] = cpu_to_be32(region1_base >> 32); | ||
1016 | ranges[(cs * 5) + 3] = cpu_to_be32(region1_base & 0xffffffff); | ||
1017 | ranges[(cs * 5) + 4] = cpu_to_be32(region1_size); | ||
1018 | } | ||
1019 | goto end_cf; | ||
1020 | no_cf: | ||
1021 | fdt_nop_node(initial_boot_params, cf); | ||
1022 | |||
1023 | end_cf: | ||
1024 | ; | ||
1025 | } | ||
1026 | |||
1027 | /* 8 char LED */ | ||
1028 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
1029 | "led0", NULL); | ||
1030 | if (alias_prop) { | ||
1031 | union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg; | ||
1032 | unsigned long base_ptr, region_base, region_size; | ||
1033 | int cs, bootbus; | ||
1034 | __be32 new_reg[6]; | ||
1035 | __be32 *ranges; | ||
1036 | int len; | ||
1037 | int led = fdt_path_offset(initial_boot_params, alias_prop); | ||
1038 | |||
1039 | base_ptr = octeon_bootinfo->led_display_base_addr; | ||
1040 | if (base_ptr == 0) | ||
1041 | goto no_led; | ||
1042 | /* Find CS0 region. */ | ||
1043 | for (cs = 0; cs < 8; cs++) { | ||
1044 | mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs)); | ||
1045 | region_base = mio_boot_reg_cfg.s.base << 16; | ||
1046 | region_size = (mio_boot_reg_cfg.s.size + 1) << 16; | ||
1047 | if (mio_boot_reg_cfg.s.en && base_ptr >= region_base | ||
1048 | && base_ptr < region_base + region_size) | ||
1049 | break; | ||
1050 | } | ||
1051 | |||
1052 | if (cs > 7) | ||
1053 | goto no_led; | ||
1054 | |||
1055 | new_reg[0] = cpu_to_be32(cs); | ||
1056 | new_reg[1] = cpu_to_be32(0x20); | ||
1057 | new_reg[2] = cpu_to_be32(0x20); | ||
1058 | new_reg[3] = cpu_to_be32(cs); | ||
1059 | new_reg[4] = cpu_to_be32(0); | ||
1060 | new_reg[5] = cpu_to_be32(0x20); | ||
1061 | fdt_setprop_inplace(initial_boot_params, led, | ||
1062 | "reg", new_reg, sizeof(new_reg)); | ||
1063 | |||
1064 | bootbus = fdt_parent_offset(initial_boot_params, led); | ||
1065 | if (bootbus < 0) | ||
1066 | goto no_led; | ||
1067 | ranges = fdt_getprop_w(initial_boot_params, bootbus, "ranges", &len); | ||
1068 | if (!ranges || len < (5 * 8 * sizeof(__be32))) | ||
1069 | goto no_led; | ||
1070 | |||
1071 | ranges[(cs * 5) + 2] = cpu_to_be32(region_base >> 32); | ||
1072 | ranges[(cs * 5) + 3] = cpu_to_be32(region_base & 0xffffffff); | ||
1073 | ranges[(cs * 5) + 4] = cpu_to_be32(region_size); | ||
1074 | goto end_led; | ||
1075 | |||
1076 | no_led: | ||
1077 | fdt_nop_node(initial_boot_params, led); | ||
1078 | end_led: | ||
1079 | ; | ||
1080 | } | ||
1081 | |||
1082 | #ifdef CONFIG_USB | ||
1083 | /* OHCI/UHCI USB */ | ||
1084 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
1085 | "uctl", NULL); | ||
1086 | if (alias_prop) { | ||
1087 | int uctl = fdt_path_offset(initial_boot_params, alias_prop); | ||
1088 | |||
1089 | if (uctl >= 0 && (!OCTEON_IS_MODEL(OCTEON_CN6XXX) || | ||
1090 | octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC2E)) { | ||
1091 | pr_debug("Deleting uctl\n"); | ||
1092 | fdt_nop_node(initial_boot_params, uctl); | ||
1093 | fdt_nop_property(initial_boot_params, aliases, "uctl"); | ||
1094 | } else if (octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC10E || | ||
1095 | octeon_bootinfo->board_type == CVMX_BOARD_TYPE_NIC4E) { | ||
1096 | /* Missing "refclk-type" defaults to crystal. */ | ||
1097 | fdt_nop_property(initial_boot_params, uctl, "refclk-type"); | ||
1098 | } | ||
1099 | } | ||
1100 | |||
1101 | /* DWC2 USB */ | ||
1102 | alias_prop = fdt_getprop(initial_boot_params, aliases, | ||
1103 | "usbn", NULL); | ||
1104 | if (alias_prop) { | ||
1105 | int usbn = fdt_path_offset(initial_boot_params, alias_prop); | ||
1106 | |||
1107 | if (usbn >= 0 && (current_cpu_type() == CPU_CAVIUM_OCTEON2 || | ||
1108 | !octeon_has_feature(OCTEON_FEATURE_USB))) { | ||
1109 | pr_debug("Deleting usbn\n"); | ||
1110 | fdt_nop_node(initial_boot_params, usbn); | ||
1111 | fdt_nop_property(initial_boot_params, aliases, "usbn"); | ||
1112 | } else { | ||
1113 | __be32 new_f[1]; | ||
1114 | enum cvmx_helper_board_usb_clock_types c; | ||
1115 | |||
1116 | c = __cvmx_helper_board_usb_get_clock_type(); | ||
1117 | switch (c) { | ||
1118 | case USB_CLOCK_TYPE_REF_48: | ||
1119 | new_f[0] = cpu_to_be32(48000000); | ||
1120 | fdt_setprop_inplace(initial_boot_params, usbn, | ||
1121 | "refclk-frequency", new_f, sizeof(new_f)); | ||
1122 | fallthrough; | ||
1123 | case USB_CLOCK_TYPE_REF_12: | ||
1124 | /* Missing "refclk-type" defaults to external. */ | ||
1125 | fdt_nop_property(initial_boot_params, usbn, "refclk-type"); | ||
1126 | break; | ||
1127 | default: | ||
1128 | break; | ||
1129 | } | ||
1130 | } | ||
1131 | } | ||
1132 | #endif | ||
1133 | |||
1134 | return 0; | ||
1135 | } | ||
1136 | |||
1137 | static int __init octeon_publish_devices(void) | ||
1138 | { | ||
1139 | return of_platform_populate(NULL, octeon_ids, NULL, NULL); | ||
1140 | } | ||
1141 | arch_initcall(octeon_publish_devices); | ||