162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Old U-boot compatibility for Acadia 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Josh Boyer <jwboyer@linux.vnet.ibm.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2008 IBM Corporation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "ops.h" 1162306a36Sopenharmony_ci#include "io.h" 1262306a36Sopenharmony_ci#include "dcr.h" 1362306a36Sopenharmony_ci#include "stdio.h" 1462306a36Sopenharmony_ci#include "4xx.h" 1562306a36Sopenharmony_ci#include "44x.h" 1662306a36Sopenharmony_ci#include "cuboot.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define TARGET_4xx 1962306a36Sopenharmony_ci#include "ppcboot.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic bd_t bd; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define CPR_PERD0_SPIDV_MASK 0x000F0000 /* SPI Clock Divider */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define PLLC_SRC_MASK 0x20000000 /* PLL feedback source */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define PLLD_FBDV_MASK 0x1F000000 /* PLL feedback divider value */ 2862306a36Sopenharmony_ci#define PLLD_FWDVA_MASK 0x000F0000 /* PLL forward divider A value */ 2962306a36Sopenharmony_ci#define PLLD_FWDVB_MASK 0x00000700 /* PLL forward divider B value */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define PRIMAD_CPUDV_MASK 0x0F000000 /* CPU Clock Divisor Mask */ 3262306a36Sopenharmony_ci#define PRIMAD_PLBDV_MASK 0x000F0000 /* PLB Clock Divisor Mask */ 3362306a36Sopenharmony_ci#define PRIMAD_OPBDV_MASK 0x00000F00 /* OPB Clock Divisor Mask */ 3462306a36Sopenharmony_ci#define PRIMAD_EBCDV_MASK 0x0000000F /* EBC Clock Divisor Mask */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PERD0_PWMDV_MASK 0xFF000000 /* PWM Divider Mask */ 3762306a36Sopenharmony_ci#define PERD0_SPIDV_MASK 0x000F0000 /* SPI Divider Mask */ 3862306a36Sopenharmony_ci#define PERD0_U0DV_MASK 0x0000FF00 /* UART 0 Divider Mask */ 3962306a36Sopenharmony_ci#define PERD0_U1DV_MASK 0x000000FF /* UART 1 Divider Mask */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void get_clocks(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci unsigned long sysclk, cpr_plld, cpr_pllc, cpr_primad, plloutb, i; 4462306a36Sopenharmony_ci unsigned long pllFwdDiv, pllFwdDivB, pllFbkDiv, pllPlbDiv, pllExtBusDiv; 4562306a36Sopenharmony_ci unsigned long pllOpbDiv, freqEBC, freqUART, freqOPB; 4662306a36Sopenharmony_ci unsigned long div; /* total divisor udiv * bdiv */ 4762306a36Sopenharmony_ci unsigned long umin; /* minimum udiv */ 4862306a36Sopenharmony_ci unsigned short diff; /* smallest diff */ 4962306a36Sopenharmony_ci unsigned long udiv; /* best udiv */ 5062306a36Sopenharmony_ci unsigned short idiff; /* current diff */ 5162306a36Sopenharmony_ci unsigned short ibdiv; /* current bdiv */ 5262306a36Sopenharmony_ci unsigned long est; /* current estimate */ 5362306a36Sopenharmony_ci unsigned long baud; 5462306a36Sopenharmony_ci void *np; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* read the sysclk value from the CPLD */ 5762306a36Sopenharmony_ci sysclk = (in_8((unsigned char *)0x80000000) == 0xc) ? 66666666 : 33333000; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* 6062306a36Sopenharmony_ci * Read PLL Mode registers 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci cpr_plld = CPR0_READ(DCRN_CPR0_PLLD); 6362306a36Sopenharmony_ci cpr_pllc = CPR0_READ(DCRN_CPR0_PLLC); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Determine forward divider A 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci pllFwdDiv = ((cpr_plld & PLLD_FWDVA_MASK) >> 16); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* 7162306a36Sopenharmony_ci * Determine forward divider B 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci pllFwdDivB = ((cpr_plld & PLLD_FWDVB_MASK) >> 8); 7462306a36Sopenharmony_ci if (pllFwdDivB == 0) 7562306a36Sopenharmony_ci pllFwdDivB = 8; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Determine FBK_DIV. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci pllFbkDiv = ((cpr_plld & PLLD_FBDV_MASK) >> 24); 8162306a36Sopenharmony_ci if (pllFbkDiv == 0) 8262306a36Sopenharmony_ci pllFbkDiv = 256; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Read CPR_PRIMAD register 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci cpr_primad = CPR0_READ(DCRN_CPR0_PRIMAD); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* 9062306a36Sopenharmony_ci * Determine PLB_DIV. 9162306a36Sopenharmony_ci */ 9262306a36Sopenharmony_ci pllPlbDiv = ((cpr_primad & PRIMAD_PLBDV_MASK) >> 16); 9362306a36Sopenharmony_ci if (pllPlbDiv == 0) 9462306a36Sopenharmony_ci pllPlbDiv = 16; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * Determine EXTBUS_DIV. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci pllExtBusDiv = (cpr_primad & PRIMAD_EBCDV_MASK); 10062306a36Sopenharmony_ci if (pllExtBusDiv == 0) 10162306a36Sopenharmony_ci pllExtBusDiv = 16; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * Determine OPB_DIV. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ci pllOpbDiv = ((cpr_primad & PRIMAD_OPBDV_MASK) >> 8); 10762306a36Sopenharmony_ci if (pllOpbDiv == 0) 10862306a36Sopenharmony_ci pllOpbDiv = 16; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* There is a bug in U-Boot that prevents us from using 11162306a36Sopenharmony_ci * bd.bi_opbfreq because U-Boot doesn't populate it for 11262306a36Sopenharmony_ci * 405EZ. We get to calculate it, yay! 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci freqOPB = (sysclk *pllFbkDiv) /pllOpbDiv; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci freqEBC = (sysclk * pllFbkDiv) / pllExtBusDiv; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci plloutb = ((sysclk * ((cpr_pllc & PLLC_SRC_MASK) ? 11962306a36Sopenharmony_ci pllFwdDivB : pllFwdDiv) * 12062306a36Sopenharmony_ci pllFbkDiv) / pllFwdDivB); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci np = find_node_by_alias("serial0"); 12362306a36Sopenharmony_ci if (getprop(np, "current-speed", &baud, sizeof(baud)) != sizeof(baud)) 12462306a36Sopenharmony_ci fatal("no current-speed property\n\r"); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci udiv = 256; /* Assume lowest possible serial clk */ 12762306a36Sopenharmony_ci div = plloutb / (16 * baud); /* total divisor */ 12862306a36Sopenharmony_ci umin = (plloutb / freqOPB) << 1; /* 2 x OPB divisor */ 12962306a36Sopenharmony_ci diff = 256; /* highest possible */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* i is the test udiv value -- start with the largest 13262306a36Sopenharmony_ci * possible (256) to minimize serial clock and constrain 13362306a36Sopenharmony_ci * search to umin. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci for (i = 256; i > umin; i--) { 13662306a36Sopenharmony_ci ibdiv = div / i; 13762306a36Sopenharmony_ci est = i * ibdiv; 13862306a36Sopenharmony_ci idiff = (est > div) ? (est-div) : (div-est); 13962306a36Sopenharmony_ci if (idiff == 0) { 14062306a36Sopenharmony_ci udiv = i; 14162306a36Sopenharmony_ci break; /* can't do better */ 14262306a36Sopenharmony_ci } else if (idiff < diff) { 14362306a36Sopenharmony_ci udiv = i; /* best so far */ 14462306a36Sopenharmony_ci diff = idiff; /* update lowest diff*/ 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci freqUART = plloutb / udiv; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci dt_fixup_cpu_clocks(bd.bi_procfreq, bd.bi_intfreq, bd.bi_plb_busfreq); 15062306a36Sopenharmony_ci dt_fixup_clock("/plb/ebc", freqEBC); 15162306a36Sopenharmony_ci dt_fixup_clock("/plb/opb", freqOPB); 15262306a36Sopenharmony_ci dt_fixup_clock("/plb/opb/serial@ef600300", freqUART); 15362306a36Sopenharmony_ci dt_fixup_clock("/plb/opb/serial@ef600400", freqUART); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void acadia_fixups(void) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci dt_fixup_memory(bd.bi_memstart, bd.bi_memsize); 15962306a36Sopenharmony_ci get_clocks(); 16062306a36Sopenharmony_ci dt_fixup_mac_address_by_alias("ethernet0", bd.bi_enetaddr); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_civoid platform_init(unsigned long r3, unsigned long r4, unsigned long r5, 16462306a36Sopenharmony_ci unsigned long r6, unsigned long r7) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci CUBOOT_INIT(); 16762306a36Sopenharmony_ci platform_ops.fixups = acadia_fixups; 16862306a36Sopenharmony_ci platform_ops.exit = ibm40x_dbcr_reset; 16962306a36Sopenharmony_ci fdt_init(_dtb_start); 17062306a36Sopenharmony_ci serial_console_init(); 17162306a36Sopenharmony_ci} 172