Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 190de005 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Josh Boyer
Browse files

[POWERPC] 4xx: Rework clock probing in boot wrapper



This reworks the boot wrapper library function that probes
the chip clocks. Better separate the base function that is
used on 440GX,SPe,EP,... from the uart fixups as those need
different device-tree path on different processors.

Also, rework the function itself based on the arch/ppc code
from Eugene Surovegin which I find more readable, and which
handles one more bypass case. Also handle the subtle difference
between 440EP/EPx and 440SPe/GX, on the former, PerClk is derived
from the PLB clock while on the later, it's derived from the OPB.

Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarJosh Boyer <jwboyer@linux.vnet.ibm.com>
parent bc0b4e7f
Loading
Loading
Loading
Loading
+204 −68
Original line number Original line Diff line number Diff line
@@ -275,89 +275,225 @@ void ibm4xx_fixup_ebc_ranges(const char *ebc)
	setprop(devp, "ranges", ranges, (p - ranges) * sizeof(u32));
	setprop(devp, "ranges", ranges, (p - ranges) * sizeof(u32));
}
}


#define SPRN_CCR1 0x378
/* Calculate 440GP clocks */
void ibm440ep_fixup_clocks(unsigned int sysclk, unsigned int ser_clk)
void ibm440gp_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk)
{
{
	u32 cpu, plb, opb, ebc, tb, uart0, m, vco;
	u32 sys0 = mfdcr(DCRN_CPC0_SYS0);
	u32 reg;
	u32 cr0 = mfdcr(DCRN_CPC0_CR0);
	u32 fwdva, fwdvb, fbdv, lfbdv, opbdv0, perdv0, spcid0, prbdv0, tmp;
	u32 cpu, plb, opb, ebc, tb, uart0, uart1, m;

	u32 opdv = CPC0_SYS0_OPDV(sys0);
	mtdcr(DCRN_CPR0_ADDR, CPR0_PLLD0);
	u32 epdv = CPC0_SYS0_EPDV(sys0);
	reg = mfdcr(DCRN_CPR0_DATA);

	tmp = (reg & 0x000F0000) >> 16;
	if (sys0 & CPC0_SYS0_BYPASS) {
	fwdva = tmp ? tmp : 16;
		/* Bypass system PLL */
	tmp = (reg & 0x00000700) >> 8;
		cpu = plb = sys_clk;
	fwdvb = tmp ? tmp : 8;
	} else {
	tmp = (reg & 0x1F000000) >> 24;
		if (sys0 & CPC0_SYS0_EXTSL)
	fbdv = tmp ? tmp : 32;
			/* PerClk */
	lfbdv = (reg & 0x0000007F);
			m = CPC0_SYS0_FWDVB(sys0) * opdv * epdv;

	mtdcr(DCRN_CPR0_ADDR, CPR0_OPBD0);
	reg = mfdcr(DCRN_CPR0_DATA);
	tmp = (reg & 0x03000000) >> 24;
	opbdv0 = tmp ? tmp : 4;

	mtdcr(DCRN_CPR0_ADDR, CPR0_PERD0);
	reg = mfdcr(DCRN_CPR0_DATA);
	tmp = (reg & 0x07000000) >> 24;
	perdv0 = tmp ? tmp : 8;

	mtdcr(DCRN_CPR0_ADDR, CPR0_PRIMBD0);
	reg = mfdcr(DCRN_CPR0_DATA);
	tmp = (reg & 0x07000000) >> 24;
	prbdv0 = tmp ? tmp : 8;

	mtdcr(DCRN_CPR0_ADDR, CPR0_SCPID);
	reg = mfdcr(DCRN_CPR0_DATA);
	tmp = (reg & 0x03000000) >> 24;
	spcid0 = tmp ? tmp : 4;

	/* Calculate M */
	mtdcr(DCRN_CPR0_ADDR, CPR0_PLLC0);
	reg = mfdcr(DCRN_CPR0_DATA);
	tmp = (reg & 0x03000000) >> 24;
	if (tmp == 0) { /* PLL output */
		tmp = (reg & 0x20000000) >> 29;
		if (!tmp) /* PLLOUTA */
			m = fbdv * lfbdv * fwdva;
		else
		else
			m = fbdv * lfbdv * fwdvb;
			/* CPU clock */
			m = CPC0_SYS0_FBDV(sys0) * CPC0_SYS0_FWDVA(sys0);
		cpu = sys_clk * m / CPC0_SYS0_FWDVA(sys0);
		plb = sys_clk * m / CPC0_SYS0_FWDVB(sys0);
	}
	}
	else if (tmp == 1) /* CPU output */
		m = fbdv * fwdva;
	else
		m = perdv0 * opbdv0 * fwdvb;


	vco = (m * sysclk) + (m >> 1);
	opb = plb / opdv;
	cpu = vco / fwdva;
	ebc = opb / epdv;
	plb = vco / fwdvb / prbdv0;

	opb = plb / opbdv0;
	/* FIXME: Check if this is for all 440GP, or just Ebony */
	ebc = plb / perdv0;
	if ((mfpvr() & 0xf0000fff) == 0x40000440)
		/* Rev. B 440GP, use external system clock */
		tb = sys_clk;
	else
		/* Rev. C 440GP, errata force us to use internal clock */
		tb = cpu;


	/* FIXME */
	if (cr0 & CPC0_CR0_U0EC)
		/* External UART clock */
		uart0 = ser_clk;
		uart0 = ser_clk;
	else
		/* Internal UART clock */
		uart0 = plb / CPC0_CR0_UDIV(cr0);


	/* Figure out timebase.  Either CPU or default TmrClk */
	if (cr0 & CPC0_CR0_U1EC)
	asm volatile (
		/* External UART clock */
			"mfspr	%0,%1\n"
		uart1 = ser_clk;
			:
			"=&r"(reg) : "i"(SPRN_CCR1));
	if (reg & 0x0080)
		tb = 25000000; /* TmrClk is 25MHz */
	else
	else
		/* Internal UART clock */
		uart1 = plb / CPC0_CR0_UDIV(cr0);

	printf("PPC440GP: SysClk = %dMHz (%x)\n\r",
	       (sys_clk + 500000) / 1000000, sys_clk);

	dt_fixup_cpu_clocks(cpu, tb, 0);

	dt_fixup_clock("/plb", plb);
	dt_fixup_clock("/plb/opb", opb);
	dt_fixup_clock("/plb/opb/ebc", ebc);
	dt_fixup_clock("/plb/opb/serial@40000200", uart0);
	dt_fixup_clock("/plb/opb/serial@40000300", uart1);
}

#define SPRN_CCR1 0x378

static inline u32 __fix_zero(u32 v, u32 def)
{
	return v ? v : def;
}

static unsigned int __ibm440eplike_fixup_clocks(unsigned int sys_clk,
						unsigned int tmr_clk,
						int per_clk_from_opb)
{
	/* PLL config */
	u32 pllc  = CPR0_READ(DCRN_CPR0_PLLC);
	u32 plld  = CPR0_READ(DCRN_CPR0_PLLD);

	/* Dividers */
	u32 fbdv   = __fix_zero((plld >> 24) & 0x1f, 32);
	u32 fwdva  = __fix_zero((plld >> 16) & 0xf, 16);
	u32 fwdvb  = __fix_zero((plld >> 8) & 7, 8);
	u32 lfbdv  = __fix_zero(plld & 0x3f, 64);
	u32 pradv0 = __fix_zero((CPR0_READ(DCRN_CPR0_PRIMAD) >> 24) & 7, 8);
	u32 prbdv0 = __fix_zero((CPR0_READ(DCRN_CPR0_PRIMBD) >> 24) & 7, 8);
	u32 opbdv0 = __fix_zero((CPR0_READ(DCRN_CPR0_OPBD) >> 24) & 3, 4);
	u32 perdv0 = __fix_zero((CPR0_READ(DCRN_CPR0_PERD) >> 24) & 3, 4);

	/* Input clocks for primary dividers */
	u32 clk_a, clk_b;

	/* Resulting clocks */
	u32 cpu, plb, opb, ebc, vco;

	/* Timebase */
	u32 ccr1, tb = tmr_clk;

	if (pllc & 0x40000000) {
		u32 m;

		/* Feedback path */
		switch ((pllc >> 24) & 7) {
		case 0:
			/* PLLOUTx */
			m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv;
			break;
		case 1:
			/* CPU */
			m = fwdva * pradv0;
			break;
		case 5:
			/* PERClk */
			m = fwdvb * prbdv0 * opbdv0 * perdv0;
			break;
		default:
			printf("WARNING ! Invalid PLL feedback source !\n");
			goto bypass;
		}
		m *= fbdv;
		vco = sys_clk * m;
		clk_a = vco / fwdva;
		clk_b = vco / fwdvb;
	} else {
bypass:
		/* Bypass system PLL */
		vco = 0;
		clk_a = clk_b = sys_clk;
	}

	cpu = clk_a / pradv0;
	plb = clk_b / prbdv0;
	opb = plb / opbdv0;
	ebc = (per_clk_from_opb ? opb : plb) / perdv0;

	/* Figure out timebase.  Either CPU or default TmrClk */
	ccr1 = mfspr(SPRN_CCR1);

	/* If passed a 0 tmr_clk, force CPU clock */
	if (tb == 0) {
		ccr1 &= ~0x80u;
		mtspr(SPRN_CCR1, ccr1);
	}
	if ((ccr1 & 0x0080) == 0)
		tb = cpu;
		tb = cpu;


	dt_fixup_cpu_clocks(cpu, tb, 0);
	dt_fixup_cpu_clocks(cpu, tb, 0);
	dt_fixup_clock("/plb", plb);
	dt_fixup_clock("/plb", plb);
	dt_fixup_clock("/plb/opb", opb);
	dt_fixup_clock("/plb/opb", opb);
	dt_fixup_clock("/plb/opb/ebc", ebc);
	dt_fixup_clock("/plb/opb/ebc", ebc);
	dt_fixup_clock("/plb/opb/serial@ef600300", uart0);

	dt_fixup_clock("/plb/opb/serial@ef600400", uart0);
	return plb;
	dt_fixup_clock("/plb/opb/serial@ef600500", uart0);
}
	dt_fixup_clock("/plb/opb/serial@ef600600", uart0);

static void eplike_fixup_uart_clk(int index, const char *path,
				  unsigned int ser_clk,
				  unsigned int plb_clk)
{
	unsigned int sdr;
	unsigned int clock;

	switch (index) {
	case 0:
		sdr = SDR0_READ(DCRN_SDR0_UART0);
		break;
	case 1:
		sdr = SDR0_READ(DCRN_SDR0_UART1);
		break;
	case 2:
		sdr = SDR0_READ(DCRN_SDR0_UART2);
		break;
	case 3:
		sdr = SDR0_READ(DCRN_SDR0_UART3);
		break;
	default:
		return;
	}

	if (sdr & 0x00800000u)
		clock = ser_clk;
	else
		clock = plb_clk / __fix_zero(sdr & 0xff, 256);

	dt_fixup_clock(path, clock);
}

void ibm440ep_fixup_clocks(unsigned int sys_clk,
			   unsigned int ser_clk,
			   unsigned int tmr_clk)
{
	unsigned int plb_clk = __ibm440eplike_fixup_clocks(sys_clk, tmr_clk, 0);

	/* serial clocks beed fixup based on int/ext */
	eplike_fixup_uart_clk(0, "/plb/opb/serial@ef600300", ser_clk, plb_clk);
	eplike_fixup_uart_clk(1, "/plb/opb/serial@ef600400", ser_clk, plb_clk);
	eplike_fixup_uart_clk(2, "/plb/opb/serial@ef600500", ser_clk, plb_clk);
	eplike_fixup_uart_clk(3, "/plb/opb/serial@ef600600", ser_clk, plb_clk);
}

void ibm440gx_fixup_clocks(unsigned int sys_clk,
			   unsigned int ser_clk,
			   unsigned int tmr_clk)
{
	unsigned int plb_clk = __ibm440eplike_fixup_clocks(sys_clk, tmr_clk, 1);

	/* serial clocks beed fixup based on int/ext */
	eplike_fixup_uart_clk(0, "/plb/opb/serial@40000200", ser_clk, plb_clk);
	eplike_fixup_uart_clk(1, "/plb/opb/serial@40000300", ser_clk, plb_clk);
}

void ibm440spe_fixup_clocks(unsigned int sys_clk,
			    unsigned int ser_clk,
			    unsigned int tmr_clk)
{
	unsigned int plb_clk = __ibm440eplike_fixup_clocks(sys_clk, tmr_clk, 1);

	/* serial clocks beed fixup based on int/ext */
	eplike_fixup_uart_clk(0, "/plb/opb/serial@10000200", ser_clk, plb_clk);
	eplike_fixup_uart_clk(1, "/plb/opb/serial@10000300", ser_clk, plb_clk);
	eplike_fixup_uart_clk(2, "/plb/opb/serial@10000600", ser_clk, plb_clk);
}
}


void ibm405gp_fixup_clocks(unsigned int sysclk, unsigned int ser_clk)
void ibm405gp_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk)
{
{
	u32 pllmr = mfdcr(DCRN_CPC0_PLLMR);
	u32 pllmr = mfdcr(DCRN_CPC0_PLLMR);
	u32 cpc0_cr0 = mfdcr(DCRN_405_CPC0_CR0);
	u32 cpc0_cr0 = mfdcr(DCRN_405_CPC0_CR0);
@@ -374,7 +510,7 @@ void ibm405gp_fixup_clocks(unsigned int sysclk, unsigned int ser_clk)


	m = fwdv * fbdv * cbdv;
	m = fwdv * fbdv * cbdv;


	cpu = sysclk * m / fwdv;
	cpu = sys_clk * m / fwdv;
	plb = cpu / cbdv;
	plb = cpu / cbdv;
	opb = plb / opdv;
	opb = plb / opdv;
	ebc = plb / epdv;
	ebc = plb / epdv;
+9 −2
Original line number Original line Diff line number Diff line
@@ -18,7 +18,14 @@ void ibm44x_dbcr_reset(void);
void ibm40x_dbcr_reset(void);
void ibm40x_dbcr_reset(void);
void ibm4xx_quiesce_eth(u32 *emac0, u32 *emac1);
void ibm4xx_quiesce_eth(u32 *emac0, u32 *emac1);
void ibm4xx_fixup_ebc_ranges(const char *ebc);
void ibm4xx_fixup_ebc_ranges(const char *ebc);
void ibm440ep_fixup_clocks(unsigned int sysclk, unsigned int ser_clk);

void ibm405gp_fixup_clocks(unsigned int sysclk, unsigned int ser_clk);
void ibm405gp_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk);
void ibm440gp_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk);
void ibm440ep_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk,
			   unsigned int tmr_clk);
void ibm440gx_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk,
			   unsigned int tmr_clk);
void ibm440spe_fixup_clocks(unsigned int sys_clk, unsigned int ser_clk,
			    unsigned int tmr_clk);


#endif /* _POWERPC_BOOT_4XX_H_ */
#endif /* _POWERPC_BOOT_4XX_H_ */
+1 −1
Original line number Original line Diff line number Diff line
@@ -30,7 +30,7 @@ static void bamboo_fixups(void)
{
{
	unsigned long sysclk = 33333333;
	unsigned long sysclk = 33333333;


	ibm440ep_fixup_clocks(sysclk, 11059200);
	ibm440ep_fixup_clocks(sysclk, 11059200, 25000000);
	ibm4xx_sdram_fixup_memsize();
	ibm4xx_sdram_fixup_memsize();
	ibm4xx_quiesce_eth((u32 *)0xef600e00, (u32 *)0xef600f00);
	ibm4xx_quiesce_eth((u32 *)0xef600e00, (u32 *)0xef600f00);
	dt_fixup_mac_addresses(bamboo_mac0, bamboo_mac1);
	dt_fixup_mac_addresses(bamboo_mac0, bamboo_mac1);
+1 −1
Original line number Original line Diff line number Diff line
@@ -39,7 +39,7 @@ static void sequoia_fixups(void)
{
{
	unsigned long sysclk = 33333333;
	unsigned long sysclk = 33333333;


	ibm440ep_fixup_clocks(sysclk, 11059200);
	ibm440ep_fixup_clocks(sysclk, 11059200, 25000000);
	ibm4xx_fixup_ebc_ranges("/plb/opb/ebc");
	ibm4xx_fixup_ebc_ranges("/plb/opb/ebc");
	ibm4xx_denali_fixup_memsize();
	ibm4xx_denali_fixup_memsize();
	dt_fixup_mac_addresses(&bd.bi_enetaddr, &bd.bi_enet1addr);
	dt_fixup_mac_addresses(&bd.bi_enetaddr, &bd.bi_enet1addr);
+1 −3
Original line number Original line Diff line number Diff line
@@ -34,9 +34,7 @@ static void taishan_fixups(void)
	   registers */
	   registers */
	unsigned long sysclk = 33000000;
	unsigned long sysclk = 33000000;


	/* 440EP Clock logic is all but identical to 440GX
	ibm440gx_fixup_clocks(sysclk, 6 * 1843200, 25000000);
	   so we just use that code for now at least */
	ibm440ep_fixup_clocks(sysclk, 6 * 1843200);


	ibm4xx_sdram_fixup_memsize();
	ibm4xx_sdram_fixup_memsize();


Loading