| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Cirrus Logic EP93xx PLL support. |
| * |
| * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net> |
| */ |
| |
| #include <common.h> |
| #include <clock_legacy.h> |
| #include <asm/arch/ep93xx.h> |
| #include <asm/io.h> |
| #include <div64.h> |
| |
| /* |
| * get_board_sys_clk() should be defined as the input frequency of the PLL. |
| * |
| * get_FCLK(), get_HCLK(), get_PCLK() and get_UCLK() return the clock of |
| * the specified bus in HZ. |
| */ |
| |
| /* |
| * return the PLL output frequency |
| * |
| * PLL rate = get_board_sys_clk() * (X1FBD + 1) * (X2FBD + 1) |
| * / (X2IPD + 1) / 2^PS |
| */ |
| static ulong get_PLLCLK(uint32_t *pllreg) |
| { |
| uint8_t i; |
| const uint32_t clkset = readl(pllreg); |
| uint64_t rate = get_board_sys_clk(); |
| rate *= ((clkset >> SYSCON_CLKSET_PLL_X1FBD1_SHIFT) & 0x1f) + 1; |
| rate *= ((clkset >> SYSCON_CLKSET_PLL_X2FBD2_SHIFT) & 0x3f) + 1; |
| do_div(rate, (clkset & 0x1f) + 1); /* X2IPD */ |
| for (i = 0; i < ((clkset >> SYSCON_CLKSET_PLL_PS_SHIFT) & 3); i++) |
| rate >>= 1; |
| |
| return (ulong)rate; |
| } |
| |
| /* return FCLK frequency */ |
| ulong get_FCLK(void) |
| { |
| const uint8_t fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 }; |
| struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE; |
| |
| const uint32_t clkset1 = readl(&syscon->clkset1); |
| const uint8_t fclk_div = |
| fclk_divisors[(clkset1 >> SYSCON_CLKSET1_FCLK_DIV_SHIFT) & 7]; |
| const ulong fclk_rate = get_PLLCLK(&syscon->clkset1) / fclk_div; |
| |
| return fclk_rate; |
| } |
| |
| /* return HCLK frequency */ |
| ulong get_HCLK(void) |
| { |
| const uint8_t hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 }; |
| struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE; |
| |
| const uint32_t clkset1 = readl(&syscon->clkset1); |
| const uint8_t hclk_div = |
| hclk_divisors[(clkset1 >> SYSCON_CLKSET1_HCLK_DIV_SHIFT) & 7]; |
| const ulong hclk_rate = get_PLLCLK(&syscon->clkset1) / hclk_div; |
| |
| return hclk_rate; |
| } |
| |
| /* return PCLK frequency */ |
| ulong get_PCLK(void) |
| { |
| const uint8_t pclk_divisors[] = { 1, 2, 4, 8 }; |
| struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE; |
| |
| const uint32_t clkset1 = readl(&syscon->clkset1); |
| const uint8_t pclk_div = |
| pclk_divisors[(clkset1 >> SYSCON_CLKSET1_PCLK_DIV_SHIFT) & 3]; |
| const ulong pclk_rate = get_HCLK() / pclk_div; |
| |
| return pclk_rate; |
| } |
| |
| /* return UCLK frequency */ |
| ulong get_UCLK(void) |
| { |
| struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE; |
| ulong uclk_rate; |
| |
| const uint32_t value = readl(&syscon->pwrcnt); |
| if (value & SYSCON_PWRCNT_UART_BAUD) |
| uclk_rate = get_board_sys_clk(); |
| else |
| uclk_rate = get_board_sys_clk() / 2; |
| |
| return uclk_rate; |
| } |