static void ppc405ep_compute_clocks (ppc405ep_cpc_t *cpc)

{

    uint32_t CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk;

    uint32_t UART0_clk, UART1_clk;

    uint64_t VCO_out, PLL_out;

    int M, D;



    VCO_out = 0;

    if ((cpc->pllmr[1] & 0x80000000) && !(cpc->pllmr[1] & 0x40000000)) {

        M = (((cpc->pllmr[1] >> 20) - 1) & 0xF) + 1; /* FBMUL */

#ifdef DEBUG_CLOCKS_LL

        printf("FBMUL %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 20) & 0xF, M);

#endif

        D = 8 - ((cpc->pllmr[1] >> 16) & 0x7); /* FWDA */

#ifdef DEBUG_CLOCKS_LL

        printf("FWDA %01" PRIx32 " %d\n", (cpc->pllmr[1] >> 16) & 0x7, D);

#endif

        VCO_out = cpc->sysclk * M * D;

        if (VCO_out < 500000000UL || VCO_out > 1000000000UL) {

            /* Error - unlock the PLL */

            printf("VCO out of range %" PRIu64 "\n", VCO_out);

#if 0

            cpc->pllmr[1] &= ~0x80000000;

            goto pll_bypass;

#endif

        }

        PLL_out = VCO_out / D;

        /* Pretend the PLL is locked */

        cpc->boot |= 0x00000001;

    } else {

#if 0

    pll_bypass:

#endif

        PLL_out = cpc->sysclk;

        if (cpc->pllmr[1] & 0x40000000) {

            /* Pretend the PLL is not locked */

            cpc->boot &= ~0x00000001;

        }

    }

    /* Now, compute all other clocks */

    D = ((cpc->pllmr[0] >> 20) & 0x3) + 1; /* CCDV */

#ifdef DEBUG_CLOCKS_LL

    printf("CCDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 20) & 0x3, D);

#endif

    CPU_clk = PLL_out / D;

    D = ((cpc->pllmr[0] >> 16) & 0x3) + 1; /* CBDV */

#ifdef DEBUG_CLOCKS_LL

    printf("CBDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 16) & 0x3, D);

#endif

    PLB_clk = CPU_clk / D;

    D = ((cpc->pllmr[0] >> 12) & 0x3) + 1; /* OPDV */

#ifdef DEBUG_CLOCKS_LL

    printf("OPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 12) & 0x3, D);

#endif

    OPB_clk = PLB_clk / D;

    D = ((cpc->pllmr[0] >> 8) & 0x3) + 2; /* EPDV */

#ifdef DEBUG_CLOCKS_LL

    printf("EPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 8) & 0x3, D);

#endif

    EBC_clk = PLB_clk / D;

    D = ((cpc->pllmr[0] >> 4) & 0x3) + 1; /* MPDV */

#ifdef DEBUG_CLOCKS_LL

    printf("MPDV %01" PRIx32 " %d\n", (cpc->pllmr[0] >> 4) & 0x3, D);

#endif

    MAL_clk = PLB_clk / D;

    D = (cpc->pllmr[0] & 0x3) + 1; /* PPDV */

#ifdef DEBUG_CLOCKS_LL

    printf("PPDV %01" PRIx32 " %d\n", cpc->pllmr[0] & 0x3, D);

#endif

    PCI_clk = PLB_clk / D;

    D = ((cpc->ucr - 1) & 0x7F) + 1; /* U0DIV */

#ifdef DEBUG_CLOCKS_LL

    printf("U0DIV %01" PRIx32 " %d\n", cpc->ucr & 0x7F, D);

#endif

    UART0_clk = PLL_out / D;

    D = (((cpc->ucr >> 8) - 1) & 0x7F) + 1; /* U1DIV */

#ifdef DEBUG_CLOCKS_LL

    printf("U1DIV %01" PRIx32 " %d\n", (cpc->ucr >> 8) & 0x7F, D);

#endif

    UART1_clk = PLL_out / D;

#ifdef DEBUG_CLOCKS

    printf("Setup PPC405EP clocks - sysclk %" PRIu32 " VCO %" PRIu64

           " PLL out %" PRIu64 " Hz\n", cpc->sysclk, VCO_out, PLL_out);

    printf("CPU %" PRIu32 " PLB %" PRIu32 " OPB %" PRIu32 " EBC %" PRIu32

           " MAL %" PRIu32 " PCI %" PRIu32 " UART0 %" PRIu32

           " UART1 %" PRIu32 "\n",

           CPU_clk, PLB_clk, OPB_clk, EBC_clk, MAL_clk, PCI_clk,

           UART0_clk, UART1_clk);

#endif

    /* Setup CPU clocks */

    clk_setup(&cpc->clk_setup[PPC405EP_CPU_CLK], CPU_clk);

    /* Setup PLB clock */

    clk_setup(&cpc->clk_setup[PPC405EP_PLB_CLK], PLB_clk);

    /* Setup OPB clock */

    clk_setup(&cpc->clk_setup[PPC405EP_OPB_CLK], OPB_clk);

    /* Setup external clock */

    clk_setup(&cpc->clk_setup[PPC405EP_EBC_CLK], EBC_clk);

    /* Setup MAL clock */

    clk_setup(&cpc->clk_setup[PPC405EP_MAL_CLK], MAL_clk);

    /* Setup PCI clock */

    clk_setup(&cpc->clk_setup[PPC405EP_PCI_CLK], PCI_clk);

    /* Setup UART0 clock */

    clk_setup(&cpc->clk_setup[PPC405EP_UART0_CLK], UART0_clk);

    /* Setup UART1 clock */

    clk_setup(&cpc->clk_setup[PPC405EP_UART1_CLK], UART1_clk);

}
