132 lines
3.8 KiB
C++
132 lines
3.8 KiB
C++
#include "pllmb.hpp"
|
|
|
|
namespace pllmb {
|
|
static const u8 qlin_hw_to_pdiv[17] = {
|
|
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 30, 32
|
|
};
|
|
|
|
enum pdiv_type {
|
|
PDIV_QLIN,
|
|
PDIV_POW2
|
|
};
|
|
|
|
struct pll_desc_t {
|
|
u32 base_offset;
|
|
u8 divm_shift;
|
|
u8 divm_width;
|
|
u8 divn_shift;
|
|
u8 divn_width;
|
|
u8 divp_shift;
|
|
u8 divp_width;
|
|
pdiv_type ptype;
|
|
};
|
|
|
|
static const pll_desc_t pll_table[] = {
|
|
{ PLLM_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLMB_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLP_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
|
|
{ PLLA_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
|
|
{ PLLU_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
|
|
{ _PLLD_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
|
|
{ PLLX_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLA1_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLDP_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLD2_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLC4_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLRE_BASE, 0, 8, 8, 8, 16, 4, PDIV_QLIN },
|
|
{ PLLC_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLC2_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
|
|
{ PLLC3_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
|
|
};
|
|
|
|
static inline u32 clk_read32(u32 offset)
|
|
{
|
|
return *(volatile u32 *)(uintptr_t)(board::clkVirtAddr + offset);
|
|
}
|
|
|
|
static inline u32 extract(u32 val, u8 shift, u8 width)
|
|
{
|
|
return (val >> shift) & ((1u << width) - 1u);
|
|
}
|
|
|
|
static u64 pll_rate_from_desc(const pll_desc_t &pll, u64 osc_hz,
|
|
bool undivided)
|
|
{
|
|
u32 base = clk_read32(pll.base_offset);
|
|
u32 divm = extract(base, pll.divm_shift, pll.divm_width);
|
|
u32 divn = extract(base, pll.divn_shift, pll.divn_width);
|
|
|
|
if (divm == 0 || divn == 0)
|
|
return 0;
|
|
|
|
u64 vco = osc_hz * divn / divm;
|
|
|
|
if (undivided)
|
|
return vco;
|
|
|
|
u32 hw_p = extract(base, pll.divp_shift, pll.divp_width);
|
|
u32 pdiv;
|
|
|
|
if (pll.ptype == PDIV_QLIN)
|
|
pdiv = (hw_p < 17) ? qlin_hw_to_pdiv[hw_p] : 1;
|
|
else
|
|
pdiv = 1u << hw_p;
|
|
|
|
return vco / pdiv;
|
|
}
|
|
|
|
static u64 pll_rate_by_offset(u32 base_offset, u64 osc_hz,
|
|
bool undivided)
|
|
{
|
|
for (const auto &pll : pll_table) {
|
|
if (pll.base_offset == base_offset)
|
|
return pll_rate_from_desc(pll, osc_hz, undivided);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u64 getRamClockRatePLLMB()
|
|
{
|
|
u32 clk_src = clk_read32(CLK_SOURCE_EMC);
|
|
u32 src = (clk_src >> 29) & 0x7;
|
|
u32 div = (clk_src >> 0) & 0xff;
|
|
|
|
u32 pll_off;
|
|
bool undivided = false;
|
|
|
|
switch (src) {
|
|
case EMC_SRC_PLLM:
|
|
pll_off = PLLM_BASE;
|
|
break;
|
|
case EMC_SRC_PLLM_UD:
|
|
pll_off = PLLM_BASE;
|
|
undivided = true;
|
|
break;
|
|
case EMC_SRC_PLLMB:
|
|
pll_off = PLLMB_BASE;
|
|
break;
|
|
case EMC_SRC_PLLMB_UD:
|
|
pll_off = PLLMB_BASE;
|
|
undivided = true;
|
|
break;
|
|
case EMC_SRC_PLLP:
|
|
pll_off = PLLP_BASE;
|
|
break;
|
|
case EMC_SRC_PLLP_UD:
|
|
pll_off = PLLP_BASE;
|
|
undivided = true;
|
|
break;
|
|
case EMC_SRC_PLLC:
|
|
pll_off = PLLC_BASE;
|
|
break;
|
|
case EMC_SRC_CLK_M:
|
|
return OSC_HZ;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
u64 pll_hz = pll_rate_by_offset(pll_off, OSC_HZ, undivided);
|
|
return pll_hz / (div + 2) * 2;
|
|
}
|
|
|
|
} |