1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 294c73a8fSJosh Boyer /* 394c73a8fSJosh Boyer * Old U-boot compatibility for Acadia 494c73a8fSJosh Boyer * 594c73a8fSJosh Boyer * Author: Josh Boyer <jwboyer@linux.vnet.ibm.com> 694c73a8fSJosh Boyer * 794c73a8fSJosh Boyer * Copyright 2008 IBM Corporation 894c73a8fSJosh Boyer */ 994c73a8fSJosh Boyer 1094c73a8fSJosh Boyer #include "ops.h" 1194c73a8fSJosh Boyer #include "io.h" 1294c73a8fSJosh Boyer #include "dcr.h" 1394c73a8fSJosh Boyer #include "stdio.h" 1494c73a8fSJosh Boyer #include "4xx.h" 1594c73a8fSJosh Boyer #include "44x.h" 1694c73a8fSJosh Boyer #include "cuboot.h" 1794c73a8fSJosh Boyer 1894c73a8fSJosh Boyer #define TARGET_4xx 1994c73a8fSJosh Boyer #include "ppcboot.h" 2094c73a8fSJosh Boyer 2194c73a8fSJosh Boyer static bd_t bd; 2294c73a8fSJosh Boyer 2394c73a8fSJosh Boyer #define CPR_PERD0_SPIDV_MASK 0x000F0000 /* SPI Clock Divider */ 2494c73a8fSJosh Boyer 2594c73a8fSJosh Boyer #define PLLC_SRC_MASK 0x20000000 /* PLL feedback source */ 2694c73a8fSJosh Boyer 2794c73a8fSJosh Boyer #define PLLD_FBDV_MASK 0x1F000000 /* PLL feedback divider value */ 2894c73a8fSJosh Boyer #define PLLD_FWDVA_MASK 0x000F0000 /* PLL forward divider A value */ 2994c73a8fSJosh Boyer #define PLLD_FWDVB_MASK 0x00000700 /* PLL forward divider B value */ 3094c73a8fSJosh Boyer 3194c73a8fSJosh Boyer #define PRIMAD_CPUDV_MASK 0x0F000000 /* CPU Clock Divisor Mask */ 3294c73a8fSJosh Boyer #define PRIMAD_PLBDV_MASK 0x000F0000 /* PLB Clock Divisor Mask */ 3394c73a8fSJosh Boyer #define PRIMAD_OPBDV_MASK 0x00000F00 /* OPB Clock Divisor Mask */ 3494c73a8fSJosh Boyer #define PRIMAD_EBCDV_MASK 0x0000000F /* EBC Clock Divisor Mask */ 3594c73a8fSJosh Boyer 3694c73a8fSJosh Boyer #define PERD0_PWMDV_MASK 0xFF000000 /* PWM Divider Mask */ 3794c73a8fSJosh Boyer #define PERD0_SPIDV_MASK 0x000F0000 /* SPI Divider Mask */ 3894c73a8fSJosh Boyer #define PERD0_U0DV_MASK 0x0000FF00 /* UART 0 Divider Mask */ 3994c73a8fSJosh Boyer #define PERD0_U1DV_MASK 0x000000FF /* UART 1 Divider Mask */ 4094c73a8fSJosh Boyer 4194c73a8fSJosh Boyer static void get_clocks(void) 4294c73a8fSJosh Boyer { 4394c73a8fSJosh Boyer unsigned long sysclk, cpr_plld, cpr_pllc, cpr_primad, plloutb, i; 4494c73a8fSJosh Boyer unsigned long pllFwdDiv, pllFwdDivB, pllFbkDiv, pllPlbDiv, pllExtBusDiv; 4594c73a8fSJosh Boyer unsigned long pllOpbDiv, freqEBC, freqUART, freqOPB; 4694c73a8fSJosh Boyer unsigned long div; /* total divisor udiv * bdiv */ 4794c73a8fSJosh Boyer unsigned long umin; /* minimum udiv */ 4894c73a8fSJosh Boyer unsigned short diff; /* smallest diff */ 4994c73a8fSJosh Boyer unsigned long udiv; /* best udiv */ 5094c73a8fSJosh Boyer unsigned short idiff; /* current diff */ 5194c73a8fSJosh Boyer unsigned short ibdiv; /* current bdiv */ 5294c73a8fSJosh Boyer unsigned long est; /* current estimate */ 5394c73a8fSJosh Boyer unsigned long baud; 5494c73a8fSJosh Boyer void *np; 5594c73a8fSJosh Boyer 5694c73a8fSJosh Boyer /* read the sysclk value from the CPLD */ 5794c73a8fSJosh Boyer sysclk = (in_8((unsigned char *)0x80000000) == 0xc) ? 66666666 : 33333000; 5894c73a8fSJosh Boyer 5994c73a8fSJosh Boyer /* 6094c73a8fSJosh Boyer * Read PLL Mode registers 6194c73a8fSJosh Boyer */ 6294c73a8fSJosh Boyer cpr_plld = CPR0_READ(DCRN_CPR0_PLLD); 6394c73a8fSJosh Boyer cpr_pllc = CPR0_READ(DCRN_CPR0_PLLC); 6494c73a8fSJosh Boyer 6594c73a8fSJosh Boyer /* 6694c73a8fSJosh Boyer * Determine forward divider A 6794c73a8fSJosh Boyer */ 6894c73a8fSJosh Boyer pllFwdDiv = ((cpr_plld & PLLD_FWDVA_MASK) >> 16); 6994c73a8fSJosh Boyer 7094c73a8fSJosh Boyer /* 7194c73a8fSJosh Boyer * Determine forward divider B 7294c73a8fSJosh Boyer */ 7394c73a8fSJosh Boyer pllFwdDivB = ((cpr_plld & PLLD_FWDVB_MASK) >> 8); 7494c73a8fSJosh Boyer if (pllFwdDivB == 0) 7594c73a8fSJosh Boyer pllFwdDivB = 8; 7694c73a8fSJosh Boyer 7794c73a8fSJosh Boyer /* 7894c73a8fSJosh Boyer * Determine FBK_DIV. 7994c73a8fSJosh Boyer */ 8094c73a8fSJosh Boyer pllFbkDiv = ((cpr_plld & PLLD_FBDV_MASK) >> 24); 8194c73a8fSJosh Boyer if (pllFbkDiv == 0) 8294c73a8fSJosh Boyer pllFbkDiv = 256; 8394c73a8fSJosh Boyer 8494c73a8fSJosh Boyer /* 8594c73a8fSJosh Boyer * Read CPR_PRIMAD register 8694c73a8fSJosh Boyer */ 8794c73a8fSJosh Boyer cpr_primad = CPR0_READ(DCRN_CPR0_PRIMAD); 8894c73a8fSJosh Boyer 8994c73a8fSJosh Boyer /* 9094c73a8fSJosh Boyer * Determine PLB_DIV. 9194c73a8fSJosh Boyer */ 9294c73a8fSJosh Boyer pllPlbDiv = ((cpr_primad & PRIMAD_PLBDV_MASK) >> 16); 9394c73a8fSJosh Boyer if (pllPlbDiv == 0) 9494c73a8fSJosh Boyer pllPlbDiv = 16; 9594c73a8fSJosh Boyer 9694c73a8fSJosh Boyer /* 9794c73a8fSJosh Boyer * Determine EXTBUS_DIV. 9894c73a8fSJosh Boyer */ 9994c73a8fSJosh Boyer pllExtBusDiv = (cpr_primad & PRIMAD_EBCDV_MASK); 10094c73a8fSJosh Boyer if (pllExtBusDiv == 0) 10194c73a8fSJosh Boyer pllExtBusDiv = 16; 10294c73a8fSJosh Boyer 10394c73a8fSJosh Boyer /* 10494c73a8fSJosh Boyer * Determine OPB_DIV. 10594c73a8fSJosh Boyer */ 10694c73a8fSJosh Boyer pllOpbDiv = ((cpr_primad & PRIMAD_OPBDV_MASK) >> 8); 10794c73a8fSJosh Boyer if (pllOpbDiv == 0) 10894c73a8fSJosh Boyer pllOpbDiv = 16; 10994c73a8fSJosh Boyer 11094c73a8fSJosh Boyer /* There is a bug in U-Boot that prevents us from using 11194c73a8fSJosh Boyer * bd.bi_opbfreq because U-Boot doesn't populate it for 11294c73a8fSJosh Boyer * 405EZ. We get to calculate it, yay! 11394c73a8fSJosh Boyer */ 11494c73a8fSJosh Boyer freqOPB = (sysclk *pllFbkDiv) /pllOpbDiv; 11594c73a8fSJosh Boyer 11694c73a8fSJosh Boyer freqEBC = (sysclk * pllFbkDiv) / pllExtBusDiv; 11794c73a8fSJosh Boyer 11894c73a8fSJosh Boyer plloutb = ((sysclk * ((cpr_pllc & PLLC_SRC_MASK) ? 11994c73a8fSJosh Boyer pllFwdDivB : pllFwdDiv) * 12094c73a8fSJosh Boyer pllFbkDiv) / pllFwdDivB); 12194c73a8fSJosh Boyer 12294c73a8fSJosh Boyer np = find_node_by_alias("serial0"); 12394c73a8fSJosh Boyer if (getprop(np, "current-speed", &baud, sizeof(baud)) != sizeof(baud)) 12494c73a8fSJosh Boyer fatal("no current-speed property\n\r"); 12594c73a8fSJosh Boyer 12694c73a8fSJosh Boyer udiv = 256; /* Assume lowest possible serial clk */ 12794c73a8fSJosh Boyer div = plloutb / (16 * baud); /* total divisor */ 12894c73a8fSJosh Boyer umin = (plloutb / freqOPB) << 1; /* 2 x OPB divisor */ 12994c73a8fSJosh Boyer diff = 256; /* highest possible */ 13094c73a8fSJosh Boyer 13194c73a8fSJosh Boyer /* i is the test udiv value -- start with the largest 13294c73a8fSJosh Boyer * possible (256) to minimize serial clock and constrain 13394c73a8fSJosh Boyer * search to umin. 13494c73a8fSJosh Boyer */ 13594c73a8fSJosh Boyer for (i = 256; i > umin; i--) { 13694c73a8fSJosh Boyer ibdiv = div / i; 13794c73a8fSJosh Boyer est = i * ibdiv; 13894c73a8fSJosh Boyer idiff = (est > div) ? (est-div) : (div-est); 13994c73a8fSJosh Boyer if (idiff == 0) { 14094c73a8fSJosh Boyer udiv = i; 14194c73a8fSJosh Boyer break; /* can't do better */ 14294c73a8fSJosh Boyer } else if (idiff < diff) { 14394c73a8fSJosh Boyer udiv = i; /* best so far */ 14494c73a8fSJosh Boyer diff = idiff; /* update lowest diff*/ 14594c73a8fSJosh Boyer } 14694c73a8fSJosh Boyer } 14794c73a8fSJosh Boyer freqUART = plloutb / udiv; 14894c73a8fSJosh Boyer 14994c73a8fSJosh Boyer dt_fixup_cpu_clocks(bd.bi_procfreq, bd.bi_intfreq, bd.bi_plb_busfreq); 15094c73a8fSJosh Boyer dt_fixup_clock("/plb/ebc", freqEBC); 15194c73a8fSJosh Boyer dt_fixup_clock("/plb/opb", freqOPB); 15294c73a8fSJosh Boyer dt_fixup_clock("/plb/opb/serial@ef600300", freqUART); 15394c73a8fSJosh Boyer dt_fixup_clock("/plb/opb/serial@ef600400", freqUART); 15494c73a8fSJosh Boyer } 15594c73a8fSJosh Boyer 15694c73a8fSJosh Boyer static void acadia_fixups(void) 15794c73a8fSJosh Boyer { 15894c73a8fSJosh Boyer dt_fixup_memory(bd.bi_memstart, bd.bi_memsize); 15994c73a8fSJosh Boyer get_clocks(); 16094c73a8fSJosh Boyer dt_fixup_mac_address_by_alias("ethernet0", bd.bi_enetaddr); 16194c73a8fSJosh Boyer } 16294c73a8fSJosh Boyer 16394c73a8fSJosh Boyer void platform_init(unsigned long r3, unsigned long r4, unsigned long r5, 16494c73a8fSJosh Boyer unsigned long r6, unsigned long r7) 16594c73a8fSJosh Boyer { 16694c73a8fSJosh Boyer CUBOOT_INIT(); 16794c73a8fSJosh Boyer platform_ops.fixups = acadia_fixups; 16894c73a8fSJosh Boyer platform_ops.exit = ibm40x_dbcr_reset; 16994c73a8fSJosh Boyer fdt_init(_dtb_start); 17094c73a8fSJosh Boyer serial_console_init(); 17194c73a8fSJosh Boyer } 172