/*
 * arch/ppc/syslib/ibm440gp_common.c
 *
 * PPC440GP system library
 *
 * Matt Porter <mporter@mvista.com>
 * Copyright 2002-2003 MontaVista Software Inc.
 *
 * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
 * Copyright (c) 2003 Zultys Technologies
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 *
 */
#include <linux/config.h>
#include <linux/types.h>
#include <asm/reg.h>
#include <asm/ibm44x.h>
#include <asm/mmu.h>

/*
 * Calculate 440GP clocks
 */
void __init ibm440gp_get_clocks(struct ibm44x_clocks* p,
				unsigned int sys_clk,
				unsigned int ser_clk)
{
	u32 cpc0_sys0 = mfdcr(DCRN_CPC0_SYS0);
	u32 cpc0_cr0 = mfdcr(DCRN_CPC0_CR0);
	u32 opdv = ((cpc0_sys0 >> 10) & 0x3) + 1;
	u32 epdv = ((cpc0_sys0 >> 8) & 0x3) + 1;

	if (cpc0_sys0 & 0x2){
		/* Bypass system PLL */
		p->cpu = p->plb = sys_clk;
	}
	else {
		u32 fbdv, fwdva, fwdvb, m, vco;

		fbdv = (cpc0_sys0 >> 18) & 0x0f;
		if (!fbdv)
			fbdv = 16;

		fwdva = 8 - ((cpc0_sys0 >> 15) & 0x7);
		fwdvb = 8 - ((cpc0_sys0 >> 12) & 0x7);

    		/* Feedback path */	
		if (cpc0_sys0 & 0x00000080){
			/* PerClk */
			m = fwdvb * opdv * epdv;
		}
		else {
			/* CPU clock */
			m = fbdv * fwdva;
    		}
		vco = sys_clk * m;
		p->cpu = vco / fwdva;
		p->plb = vco / fwdvb;
	}

	p->opb = p->plb / opdv;
	p->ebc = p->opb / epdv;

	if (cpc0_cr0 & 0x00400000){
		/* External UART clock */
		p->uart0 = p->uart1 = ser_clk;
	}
	else {
		/* Internal UART clock */
    		u32 uart_div = ((cpc0_cr0 >> 16) & 0x1f) + 1;
		p->uart0 = p->uart1 = p->plb / uart_div;
	}
}
