[U-Boot] [PATCH] Exynos5: Clock: Generic api to set and get clock rate and source
Rajeshwari Birje
rajeshwari.birje at gmail.com
Mon Nov 11 14:08:44 CET 2013
Hi All,
This patch is based on:
[U-Boot] [PATCH 00/10 V6] EXYNOS5420: Add SMDK5420 board support
--
Regards,
Rajeshwari Shinde
On Mon, Nov 11, 2013 at 6:23 PM, Rajeshwari S Shinde
<rajeshwari.s at samsung.com> wrote:
> This patch implements generic api for exynos5250 and exynos5420.
> These api's set and get clock rate based on the peripheral id.
>
> Signed-off-by: Andrew Bresticker <abrestic at chromium.org>
> Signed-off-by: Rajeshwari S Shinde <rajeshwari.s at samsung.com>
> ---
> arch/arm/cpu/armv7/exynos/clock.c | 958 ++++++++++++---------------------
> arch/arm/include/asm/arch-exynos/clk.h | 30 +-
> drivers/mmc/exynos_dw_mmc.c | 15 +-
> 3 files changed, 385 insertions(+), 618 deletions(-)
>
> diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c
> index b52e61a..09e156c 100644
> --- a/arch/arm/cpu/armv7/exynos/clock.c
> +++ b/arch/arm/cpu/armv7/exynos/clock.c
> @@ -16,46 +16,97 @@
> #define PLL_DIV_65536 65536
>
> /* *
> - * This structure is to store the src bit, div bit and prediv bit
> - * positions of the peripheral clocks of the src and div registers
> + * This structure store positions of the peripheral clocks
> + * and their source, divider and predivider information.
> + * @periph_id: id of the peripheral
> + * @src_offset: offset of the source register
> + * @div_offset: offset of the divider register
> + * @prediv_offset: offset of the pre divider register
> + * @src_bit: bit location in the source register
> + * @div_bit: bit location in the divider register
> + * @pre_div_bit: bit location in the pre divider register
> + * @src_mask: mask for the source register value
> + * @div_mask: mask for the divider register value
> + * @pre_div_mask: mask for the pre divider register value
> */
> struct clk_bit_info {
> + int32_t periph_id;
> + int32_t src_offset;
> + int32_t div_offset;
> + int32_t prediv_offset;
> int8_t src_bit;
> int8_t div_bit;
> - int8_t prediv_bit;
> + int8_t pre_div_bit;
> + int8_t src_mask;
> + int32_t div_mask;
> + int32_t pre_div_mask;
> };
>
> -/* src_bit div_bit prediv_bit */
> -static struct clk_bit_info clk_bit_info[PERIPH_ID_COUNT] = {
> - {0, 0, -1},
> - {4, 4, -1},
> - {8, 8, -1},
> - {12, 12, -1},
> - {0, 0, 8},
> - {4, 16, 24},
> - {8, 0, 8},
> - {12, 16, 24},
> - {-1, -1, -1},
> - {16, 0, 8},
> - {20, 16, 24},
> - {24, 0, 8},
> - {0, 0, 4},
> - {4, 12, 16},
> - {-1, -1, -1},
> - {-1, -1, -1},
> - {-1, 24, 0},
> - {-1, 24, 0},
> - {-1, 24, 0},
> - {-1, 24, 0},
> - {-1, 24, 0},
> - {-1, 24, 0},
> - {-1, 24, 0},
> - {-1, 24, 0},
> - {24, 0, -1},
> - {24, 0, -1},
> - {24, 0, -1},
> - {24, 0, -1},
> - {24, 0, -1},
> +static struct clk_bit_info exynos5_bit_info_table[] = {
> + {PERIPH_ID_UART0, 0x10250, 0x10558, -1, 0, 0, -1, 0xf, 0xf, -1},
> + {PERIPH_ID_UART1, 0x10250, 0x10558, -1, 4, 4, -1, 0xf, 0xf, -1},
> + {PERIPH_ID_UART2, 0x10250, 0x10558, -1, 8, 8, -1, 0xf, 0xf, -1},
> + {PERIPH_ID_UART3, 0x10250, 0x10558, -1, 12, 12, -1, 0xf, 0xf, -1},
> + {PERIPH_ID_I2C0, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C1, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C2, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C3, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C4, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C5, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C6, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C7, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C8, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C9, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_I2C10, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
> + {PERIPH_ID_SPI0, 0x10254, 0x1055c, 0x1055c, 16, 0, 8, 0xf, 0xf, 0xff},
> + {PERIPH_ID_SPI1, 0x10254, 0x1055c, 0x1055c, 20, 16, 24, 0xf, 0xf, 0xff},
> + {PERIPH_ID_SPI2, 0x10254, 0x10560, 0x10560, 24, 0, 8, 0xf, 0xf, 0xff},
> + {PERIPH_ID_SPI3, 0x10270, 0x10580, 0x10580, 0, 0, 4, 0xf, 0xf, 0xff},
> + {PERIPH_ID_SPI4, 0x10270, 0x10580, 0x10580, 4, 12, 16, 0xf, 0xf, 0xff},
> + {PERIPH_ID_SDMMC0, 0x10244, 0x1054c, 0x1054c, 0, 0, 8, 0xf, 0xf, 0xff},
> + {PERIPH_ID_SDMMC1, 0x10244, 0x1054c, 0x1054c, 4, 16, 24, 0xf, 0xf,
> + 0xff},
> + {PERIPH_ID_SDMMC2, 0x10244, 0x10550, 0x10550, 8, 0, 8, 0xf, 0xf, 0xff},
> + {PERIPH_ID_SDMMC3, 0x10244, 0x10550, 0x10550, 12, 16, 24, 0xf, 0xf,
> + 0xff},
> + {PERIPH_ID_PWM0, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
> + {PERIPH_ID_PWM1, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
> + {PERIPH_ID_PWM2, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
> + {PERIPH_ID_PWM3, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
> + {PERIPH_ID_PWM4, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
> + {PERIPH_ID_I2S0, 0x10240, 0x10544, -1, 0, 0, -1, 0xf, 0xf, -1},
> +};
> +
> +static struct clk_bit_info exynos5420_bit_info_table[] = {
> + {PERIPH_ID_UART0, 0x10250, 0x10558, -1, 4, 8, -1, 0x7, 0xf, -1},
> + {PERIPH_ID_UART1, 0x10250, 0x10558, -1, 8, 12, -1, 0x7, 0xf, -1},
> + {PERIPH_ID_UART2, 0x10250, 0x10558, -1, 12, 16, -1, 0x7, 0xf, -1},
> + {PERIPH_ID_UART3, 0x10250, 0x10558, -1, 16, 20, -1, 0x7, 0xf, -1},
> + {PERIPH_ID_I2C0, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C1, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C2, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C3, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C4, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C5, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C6, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C7, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C8, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C9, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2C10, -1, 0x10504, -1, -1, 8, -1, 0x3f, -1, -1},
> + {PERIPH_ID_SPI0, 0x10254, 0x1055c, 0x10568, 20, 20, 8, 0x7, 0xf, 0xff},
> + {PERIPH_ID_SPI1, 0x10254, 0x1055c, 0x10568, 24, 24, 16, 0x7, 0xf, 0xff},
> + {PERIPH_ID_SPI2, 0x10254, 0x1055c, 0x10568, 28, 28, 24, 0x7, 0xf, 0xff},
> + {PERIPH_ID_SPI3, 0x10270, 0x10584, 0x10584, 12, 16, 0, 0x7, 0xf, 0xff},
> + {PERIPH_ID_SPI4, 0x10270, 0x10584, 0x10584, 16, 20, 8, 0x7, 0xf, 0xff},
> + {PERIPH_ID_SDMMC0, 0x10244, 0x1054c, -1, 8, 0, -1, 0x7, 0x3ff, -1},
> + {PERIPH_ID_SDMMC1, 0x10244, 0x1054c, -1, 12, 10, -1, 0x7, 0x3ff, -1},
> + {PERIPH_ID_SDMMC2, 0x10244, 0x1054c, -1, 16, 20, -1, 0x7, 0x3ff, -1},
> + {PERIPH_ID_PWM0, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_PWM1, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_PWM2, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_PWM3, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_PWM4, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
> + {PERIPH_ID_I2S0, 0x10240, 0x10544, -1, 28, 20, -1, 0x7, 0xf, -1},
> };
>
> /* Epll Clock division values to achive different frequency output */
> @@ -69,6 +120,27 @@ static struct set_epll_con_val exynos5_epll_div[] = {
> { 180633600, 0, 45, 3, 1, 10381 }
> };
>
> +static struct clk_bit_info *get_table_index(int periph_id)
> +{
> + int i, count;
> + struct clk_bit_info *table;
> +
> + if (proid_is_exynos5420()) {
> + table = exynos5420_bit_info_table;
> + count = ARRAY_SIZE(exynos5420_bit_info_table);
> + } else {
> + table = exynos5_bit_info_table;
> + count = ARRAY_SIZE(exynos5_bit_info_table);
> + }
> +
> + for (i = 0; i < count; i++) {
> + if ((table + i)->periph_id == periph_id)
> + return table + i;
> + }
> +
> + return NULL;
> +}
> +
> /* exynos: return pll clock frequency */
> static int exynos_get_pll_clk(int pllreg, unsigned int r, unsigned int k)
> {
> @@ -258,111 +330,6 @@ static unsigned long exynos5_get_pll_clk(int pllreg)
> return fout;
> }
>
> -static unsigned long exynos5_get_periph_rate(int peripheral)
> -{
> - struct clk_bit_info *bit_info = &clk_bit_info[peripheral];
> - unsigned long sclk, sub_clk;
> - unsigned int src, div, sub_div;
> - struct exynos5_clock *clk =
> - (struct exynos5_clock *)samsung_get_base_clock();
> -
> - switch (peripheral) {
> - case PERIPH_ID_UART0:
> - case PERIPH_ID_UART1:
> - case PERIPH_ID_UART2:
> - case PERIPH_ID_UART3:
> - src = readl(&clk->src_peric0);
> - div = readl(&clk->div_peric0);
> - break;
> - case PERIPH_ID_PWM0:
> - case PERIPH_ID_PWM1:
> - case PERIPH_ID_PWM2:
> - case PERIPH_ID_PWM3:
> - case PERIPH_ID_PWM4:
> - src = readl(&clk->src_peric0);
> - div = readl(&clk->div_peric3);
> - break;
> - case PERIPH_ID_I2S0:
> - src = readl(&clk->src_mau);
> - div = readl(&clk->div_mau);
> - case PERIPH_ID_SPI0:
> - case PERIPH_ID_SPI1:
> - src = readl(&clk->src_peric1);
> - div = readl(&clk->div_peric1);
> - break;
> - case PERIPH_ID_SPI2:
> - src = readl(&clk->src_peric1);
> - div = readl(&clk->div_peric2);
> - break;
> - case PERIPH_ID_SPI3:
> - case PERIPH_ID_SPI4:
> - src = readl(&clk->sclk_src_isp);
> - div = readl(&clk->sclk_div_isp);
> - break;
> - case PERIPH_ID_SDMMC0:
> - case PERIPH_ID_SDMMC1:
> - case PERIPH_ID_SDMMC2:
> - case PERIPH_ID_SDMMC3:
> - src = readl(&clk->src_fsys);
> - div = readl(&clk->div_fsys1);
> - break;
> - case PERIPH_ID_I2C0:
> - case PERIPH_ID_I2C1:
> - case PERIPH_ID_I2C2:
> - case PERIPH_ID_I2C3:
> - case PERIPH_ID_I2C4:
> - case PERIPH_ID_I2C5:
> - case PERIPH_ID_I2C6:
> - case PERIPH_ID_I2C7:
> - sclk = exynos5_get_pll_clk(MPLL);
> - sub_div = ((readl(&clk->div_top1) >> bit_info->div_bit)
> - & 0x7) + 1;
> - div = ((readl(&clk->div_top0) >> bit_info->prediv_bit)
> - & 0x7) + 1;
> - return (sclk / sub_div) / div;
> - default:
> - debug("%s: invalid peripheral %d", __func__, peripheral);
> - return -1;
> - };
> -
> - src = (src >> bit_info->src_bit) & 0xf;
> -
> - switch (src) {
> - case EXYNOS_SRC_MPLL:
> - sclk = exynos5_get_pll_clk(MPLL);
> - break;
> - case EXYNOS_SRC_EPLL:
> - sclk = exynos5_get_pll_clk(EPLL);
> - break;
> - case EXYNOS_SRC_VPLL:
> - sclk = exynos5_get_pll_clk(VPLL);
> - break;
> - default:
> - return 0;
> - }
> -
> - /* Ratio clock division for this peripheral */
> - sub_div = (div >> bit_info->div_bit) & 0xf;
> - sub_clk = sclk / (sub_div + 1);
> -
> - /* Pre-ratio clock division for SDMMC0 and 2 */
> - if (peripheral == PERIPH_ID_SDMMC0 || peripheral == PERIPH_ID_SDMMC2) {
> - div = (div >> bit_info->prediv_bit) & 0xff;
> - return sub_clk / (div + 1);
> - }
> -
> - return sub_clk;
> -}
> -
> -unsigned long clock_get_periph_rate(int peripheral)
> -{
> - if (cpu_is_exynos5())
> - return exynos5_get_periph_rate(peripheral);
> - else
> - return 0;
> -}
> -
> -/* exynos5420: return pll clock frequency */
> static unsigned long exynos5420_get_pll_clk(int pllreg)
> {
> struct exynos5420_clock *clk =
> @@ -391,6 +358,15 @@ static unsigned long exynos5420_get_pll_clk(int pllreg)
> r = readl(&clk->rpll_con0);
> k = readl(&clk->rpll_con1);
> break;
> + case SPLL:
> + r = readl(&clk->spll_con0);
> + break;
> + case CPLL:
> + r = readl(&clk->cpll_con0);
> + break;
> + case DPLL:
> + r = readl(&clk->dpll_con0);
> + break;
> default:
> printf("Unsupported PLL (%d)\n", pllreg);
> return 0;
> @@ -399,6 +375,240 @@ static unsigned long exynos5420_get_pll_clk(int pllreg)
> return exynos_get_pll_clk(pllreg, r, k);
> }
>
> +static unsigned long exynos5420_src_clk(int peripheral)
> +{
> + unsigned int src;
> + unsigned long sclk;
> + unsigned long clk_base = samsung_get_base_clock();
> + struct clk_bit_info *bit_info = get_table_index(peripheral);
> +
> + /*
> + * I2C and PWM clocks are parented by aclk66_peric which is
> + * parented by CPLL (initialized in exynos5420_clock_init()).
> + */
> + if (bit_info->src_offset < 0)
> + return get_pll_clk(CPLL);
> +
> + src = readl(clk_base + bit_info->src_offset);
> + src = (src >> bit_info->src_bit) & bit_info->src_mask;
> +
> + switch (src) {
> + case 0x3:
> + sclk = get_pll_clk(MPLL);
> + break;
> + case 0x6:
> + sclk = get_pll_clk(EPLL);
> + break;
> + case 0x7:
> + sclk = get_pll_clk(RPLL);
> + break;
> + default:
> + sclk = 0;
> + }
> + return sclk;
> +}
> +
> +static long exynos5_src_clk(int peripheral)
> +{
> + unsigned int src;
> + unsigned long sclk;
> + unsigned long clk_base = samsung_get_base_clock();
> + struct clk_bit_info *bit_info = get_table_index(peripheral);
> +
> + /*
> + * I2C clocks are parented by aclk66 which is always parented by
> + * MPLL on 5250.
> + */
> + if (bit_info->src_offset < 0)
> + return get_pll_clk(MPLL);
> +
> + src = readl(clk_base + bit_info->src_offset);
> + src = (src >> bit_info->src_bit) & bit_info->src_mask;
> +
> + switch (src) {
> + case 0x6:
> + sclk = get_pll_clk(MPLL);
> + break;
> + case 0x7:
> + sclk = get_pll_clk(EPLL);
> + break;
> + case 0x8:
> + sclk = get_pll_clk(RPLL);
> + break;
> + default:
> + sclk = -1;
> + }
> + return sclk;
> +}
> +
> +static unsigned long get_src_clk(int peripheral)
> +{
> + if (proid_is_exynos5420())
> + return exynos5420_src_clk(peripheral);
> + else if (proid_is_exynos5250())
> + return exynos5_src_clk(peripheral);
> + else
> + return 0;
> +}
> +
> +long clock_get_periph_rate(int peripheral)
> +{
> + struct clk_bit_info *bit_info = NULL;
> + unsigned long sclk, sub_clk;
> + unsigned int div, sub_div = 0;
> + unsigned long clk_base = samsung_get_base_clock();
> +
> + bit_info = get_table_index(peripheral);
> + if (!bit_info) {
> + debug("Invalid peripheral id\n");
> + return -1;
> + }
> +
> + sclk = get_src_clk(peripheral);
> + if (sclk < 0) {
> + debug("Unknown source clock\n");
> + return -1;
> + }
> +
> + div = readl(clk_base + bit_info->div_offset);
> +
> + /* Ratio clock division for this peripheral */
> + div = (div >> bit_info->div_bit) & bit_info->div_mask;
> + sub_clk = sclk / (div + 1);
> +
> + if (bit_info->prediv_offset >= 0) {
> + sub_div = readl(clk_base + bit_info->prediv_offset);
> + sub_div = (sub_div >> bit_info->pre_div_bit) &
> + bit_info->pre_div_mask;
> + return sub_clk / (sub_div + 1);
> + }
> +
> + return sub_clk;
> +}
> +
> +/**
> + * Linearly searches for the most accurate main and fine stage clock scalars
> + * (divisors) for a specified target frequency and scalar bit sizes by checking
> + * all multiples of main_scalar_bits values. Will always return scalars up to or
> + * slower than target.
> + *
> + * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32
> + * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32
> + * @param input_freq Clock frequency to be scaled in Hz
> + * @param target_freq Desired clock frequency in Hz
> + * @param best_fine_scalar Pointer to store the fine stage divisor
> + *
> + * @return best_main_scalar Main scalar for desired frequency or -1 if none
> + * found
> + */
> +static int clock_calc_best_scalar(unsigned int main_scaler_bits,
> + unsigned int fine_scalar_bits, unsigned int input_rate,
> + unsigned int target_rate, unsigned int *best_fine_scalar)
> +{
> + int i;
> + int best_main_scalar = -1;
> + unsigned int best_error = target_rate;
> + const unsigned int cap = (1 << fine_scalar_bits) - 1;
> + const unsigned int loops = 1 << main_scaler_bits;
> +
> + debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate,
> + target_rate, cap);
> +
> + assert(best_fine_scalar != NULL);
> + assert(main_scaler_bits <= fine_scalar_bits);
> +
> + *best_fine_scalar = 1;
> +
> + if (input_rate == 0 || target_rate == 0)
> + return -1;
> +
> + if (target_rate >= input_rate)
> + return 1;
> +
> + for (i = 1; i <= loops; i++) {
> + const unsigned int effective_div = max(min(input_rate / i /
> + target_rate, cap), 1);
> + const unsigned int effective_rate = input_rate / i /
> + effective_div;
> + const int error = target_rate - effective_rate;
> +
> + debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div,
> + effective_rate, error);
> +
> + if (error >= 0 && error <= best_error) {
> + best_error = error;
> + best_main_scalar = i;
> + *best_fine_scalar = effective_div;
> + }
> + }
> +
> + return best_main_scalar;
> +}
> +
> +int clock_set_periph_source(int periph_id, int src)
> +{
> + struct clk_bit_info *bit_info = NULL;
> + unsigned long clk_base = samsung_get_base_clock();
> +
> + bit_info = get_table_index(periph_id);
> + if (!bit_info) {
> + debug("Invalid peripheral id\n");
> + return -1;
> + }
> +
> + clrsetbits_le32(clk_base + bit_info->src_offset,
> + bit_info->src_mask << bit_info->src_bit,
> + (src & bit_info->src_mask) << bit_info->src_bit);
> +
> + return 0;
> +}
> +
> +int clock_set_periph_rate(int periph_id, unsigned long rate)
> +{
> + struct clk_bit_info *bit_info = NULL;
> + int div;
> + unsigned int pre_div = 0;
> + unsigned long sclk, sub_clk;
> + unsigned long clk_base = samsung_get_base_clock();
> +
> + bit_info = get_table_index(periph_id);
> + if (!bit_info) {
> + debug("Invalid peripheral id\n");
> + return -1;
> + }
> +
> + if ((PERIPH_ID_SPI0 <= periph_id && periph_id <= PERIPH_ID_SPI2) ||
> + PERIPH_ID_SPI3 == periph_id || periph_id == PERIPH_ID_SPI4) {
> + div = clock_calc_best_scalar(4, 8, 400000000, rate, &pre_div);
> + if (div < 0) {
> + debug("%s: Cannot set clock rate %lu for periph %d",
> + __func__, rate, periph_id);
> + return -1;
> + }
> + div = div - 1;
> + pre_div = pre_div - 1;
> + } else {
> + sclk = get_src_clk(periph_id);
> + div = DIV_ROUND_UP(sclk, rate);
> + if (bit_info->prediv_offset >= 0) {
> + sub_clk = sclk / (div + 1);
> + pre_div = DIV_ROUND_UP(sub_clk, rate);
> + }
> + }
> +
> + clrsetbits_le32(clk_base + bit_info->div_offset,
> + bit_info->div_mask << bit_info->div_bit,
> + (div & bit_info->div_mask) << bit_info->div_bit);
> +
> + if (bit_info->prediv_offset >= 0)
> + clrsetbits_le32(clk_base + bit_info->prediv_offset,
> + bit_info->pre_div_mask << bit_info->pre_div_bit,
> + (pre_div & bit_info->pre_div_mask) <<
> + bit_info->pre_div_bit);
> +
> + return 0;
> +}
> +
> /* exynos4: return ARM clock frequency */
> static unsigned long exynos4_get_arm_clk(void)
> {
> @@ -522,27 +732,6 @@ static unsigned long exynos4x12_get_pwm_clk(void)
> return pclk;
> }
>
> -/* exynos5420: return pwm clock frequency */
> -static unsigned long exynos5420_get_pwm_clk(void)
> -{
> - struct exynos5420_clock *clk =
> - (struct exynos5420_clock *)samsung_get_base_clock();
> - unsigned long pclk, sclk;
> - unsigned int ratio;
> -
> - /*
> - * CLK_DIV_PERIC3
> - * PWM_RATIO [3:0]
> - */
> - ratio = readl(&clk->div_peric0);
> - ratio = (ratio >> 28) & 0xf;
> - sclk = get_pll_clk(MPLL);
> -
> - pclk = sclk / (ratio + 1);
> -
> - return pclk;
> -}
> -
> /* exynos4: return uart clock frequency */
> static unsigned long exynos4_get_uart_clk(int dev_index)
> {
> @@ -635,100 +824,6 @@ static unsigned long exynos4x12_get_uart_clk(int dev_index)
> return uclk;
> }
>
> -/* exynos5: return uart clock frequency */
> -static unsigned long exynos5_get_uart_clk(int dev_index)
> -{
> - struct exynos5_clock *clk =
> - (struct exynos5_clock *)samsung_get_base_clock();
> - unsigned long uclk, sclk;
> - unsigned int sel;
> - unsigned int ratio;
> -
> - /*
> - * CLK_SRC_PERIC0
> - * UART0_SEL [3:0]
> - * UART1_SEL [7:4]
> - * UART2_SEL [8:11]
> - * UART3_SEL [12:15]
> - * UART4_SEL [16:19]
> - * UART5_SEL [23:20]
> - */
> - sel = readl(&clk->src_peric0);
> - sel = (sel >> (dev_index << 2)) & 0xf;
> -
> - if (sel == 0x6)
> - sclk = get_pll_clk(MPLL);
> - else if (sel == 0x7)
> - sclk = get_pll_clk(EPLL);
> - else if (sel == 0x8)
> - sclk = get_pll_clk(VPLL);
> - else
> - return 0;
> -
> - /*
> - * CLK_DIV_PERIC0
> - * UART0_RATIO [3:0]
> - * UART1_RATIO [7:4]
> - * UART2_RATIO [8:11]
> - * UART3_RATIO [12:15]
> - * UART4_RATIO [16:19]
> - * UART5_RATIO [23:20]
> - */
> - ratio = readl(&clk->div_peric0);
> - ratio = (ratio >> (dev_index << 2)) & 0xf;
> -
> - uclk = sclk / (ratio + 1);
> -
> - return uclk;
> -}
> -
> -/* exynos5420: return uart clock frequency */
> -static unsigned long exynos5420_get_uart_clk(int dev_index)
> -{
> - struct exynos5420_clock *clk =
> - (struct exynos5420_clock *)samsung_get_base_clock();
> - unsigned long uclk, sclk;
> - unsigned int sel;
> - unsigned int ratio;
> -
> - /*
> - * CLK_SRC_PERIC0
> - * UART0_SEL [3:0]
> - * UART1_SEL [7:4]
> - * UART2_SEL [8:11]
> - * UART3_SEL [12:15]
> - * UART4_SEL [16:19]
> - * UART5_SEL [23:20]
> - */
> - sel = readl(&clk->src_peric0);
> - sel = (sel >> ((dev_index * 4) + 4)) & 0x7;
> -
> - if (sel == 0x3)
> - sclk = get_pll_clk(MPLL);
> - else if (sel == 0x6)
> - sclk = get_pll_clk(EPLL);
> - else if (sel == 0x7)
> - sclk = get_pll_clk(RPLL);
> - else
> - return 0;
> -
> - /*
> - * CLK_DIV_PERIC0
> - * UART0_RATIO [3:0]
> - * UART1_RATIO [7:4]
> - * UART2_RATIO [8:11]
> - * UART3_RATIO [12:15]
> - * UART4_RATIO [16:19]
> - * UART5_RATIO [23:20]
> - */
> - ratio = readl(&clk->div_peric0);
> - ratio = (ratio >> ((dev_index * 4) + 8)) & 0xf;
> -
> - uclk = sclk / (ratio + 1);
> -
> - return uclk;
> -}
> -
> static unsigned long exynos4_get_mmc_clk(int dev_index)
> {
> struct exynos4_clock *clk =
> @@ -778,79 +873,6 @@ static unsigned long exynos4_get_mmc_clk(int dev_index)
> return uclk;
> }
>
> -static unsigned long exynos5_get_mmc_clk(int dev_index)
> -{
> - struct exynos5_clock *clk =
> - (struct exynos5_clock *)samsung_get_base_clock();
> - unsigned long uclk, sclk;
> - unsigned int sel, ratio, pre_ratio;
> - int shift = 0;
> -
> - sel = readl(&clk->src_fsys);
> - sel = (sel >> (dev_index << 2)) & 0xf;
> -
> - if (sel == 0x6)
> - sclk = get_pll_clk(MPLL);
> - else if (sel == 0x7)
> - sclk = get_pll_clk(EPLL);
> - else if (sel == 0x8)
> - sclk = get_pll_clk(VPLL);
> - else
> - return 0;
> -
> - switch (dev_index) {
> - case 0:
> - case 1:
> - ratio = readl(&clk->div_fsys1);
> - pre_ratio = readl(&clk->div_fsys1);
> - break;
> - case 2:
> - case 3:
> - ratio = readl(&clk->div_fsys2);
> - pre_ratio = readl(&clk->div_fsys2);
> - break;
> - default:
> - return 0;
> - }
> -
> - if (dev_index == 1 || dev_index == 3)
> - shift = 16;
> -
> - ratio = (ratio >> shift) & 0xf;
> - pre_ratio = (pre_ratio >> (shift + 8)) & 0xff;
> - uclk = (sclk / (ratio + 1)) / (pre_ratio + 1);
> -
> - return uclk;
> -}
> -
> -static unsigned long exynos5420_get_mmc_clk(int dev_index)
> -{
> - struct exynos5420_clock *clk =
> - (struct exynos5420_clock *)samsung_get_base_clock();
> - unsigned long uclk, sclk;
> - unsigned int sel, ratio;
> - int shift = 0;
> -
> - sel = readl(&clk->src_fsys);
> - sel = (sel >> ((dev_index * 4) + 8)) & 0x7;
> -
> - if (sel == 0x3)
> - sclk = get_pll_clk(MPLL);
> - else if (sel == 0x6)
> - sclk = get_pll_clk(EPLL);
> - else
> - return 0;
> -
> - ratio = readl(&clk->div_fsys1);
> -
> - shift = dev_index * 10;
> -
> - ratio = (ratio >> shift) & 0x3ff;
> - uclk = (sclk / (ratio + 1));
> -
> - return uclk;
> -}
> -
> /* exynos4: set the mmc clock */
> static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
> {
> @@ -910,50 +932,6 @@ static void exynos4x12_set_mmc_clk(int dev_index, unsigned int div)
> writel(val, addr);
> }
>
> -/* exynos5: set the mmc clock */
> -static void exynos5_set_mmc_clk(int dev_index, unsigned int div)
> -{
> - struct exynos5_clock *clk =
> - (struct exynos5_clock *)samsung_get_base_clock();
> - unsigned int addr;
> - unsigned int val;
> -
> - /*
> - * CLK_DIV_FSYS1
> - * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
> - * CLK_DIV_FSYS2
> - * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
> - */
> - if (dev_index < 2) {
> - addr = (unsigned int)&clk->div_fsys1;
> - } else {
> - addr = (unsigned int)&clk->div_fsys2;
> - dev_index -= 2;
> - }
> -
> - val = readl(addr);
> - val &= ~(0xff << ((dev_index << 4) + 8));
> - val |= (div & 0xff) << ((dev_index << 4) + 8);
> - writel(val, addr);
> -}
> -
> -/* exynos5: set the mmc clock */
> -static void exynos5420_set_mmc_clk(int dev_index, unsigned int div)
> -{
> - struct exynos5420_clock *clk =
> - (struct exynos5420_clock *)samsung_get_base_clock();
> - unsigned int addr;
> - unsigned int val, shift;
> -
> - addr = (unsigned int)&clk->div_fsys1;
> - shift = dev_index * 10;
> -
> - val = readl(addr);
> - val &= ~(0x3ff << shift);
> - val |= (div & 0x3ff) << shift;
> - writel(val, addr);
> -}
> -
> /* get_lcd_clk: return lcd clock frequency */
> static unsigned long exynos4_get_lcd_clk(void)
> {
> @@ -1222,28 +1200,6 @@ void exynos4_set_mipi_clk(void)
> writel(cfg, &clk->div_lcd0);
> }
>
> -/*
> - * I2C
> - *
> - * exynos5: obtaining the I2C clock
> - */
> -static unsigned long exynos5_get_i2c_clk(void)
> -{
> - struct exynos5_clock *clk =
> - (struct exynos5_clock *)samsung_get_base_clock();
> - unsigned long aclk_66, aclk_66_pre, sclk;
> - unsigned int ratio;
> -
> - sclk = get_pll_clk(MPLL);
> -
> - ratio = (readl(&clk->div_top1)) >> 24;
> - ratio &= 0x7;
> - aclk_66_pre = sclk / (ratio + 1);
> - ratio = readl(&clk->div_top0);
> - ratio &= 0x7;
> - aclk_66 = aclk_66_pre / (ratio + 1);
> - return aclk_66;
> -}
>
> int exynos5_set_epll_clk(unsigned long rate)
> {
> @@ -1358,200 +1314,6 @@ int exynos5_set_i2s_clk_prescaler(unsigned int src_frq,
> return 0;
> }
>
> -/**
> - * Linearly searches for the most accurate main and fine stage clock scalars
> - * (divisors) for a specified target frequency and scalar bit sizes by checking
> - * all multiples of main_scalar_bits values. Will always return scalars up to or
> - * slower than target.
> - *
> - * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32
> - * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32
> - * @param input_freq Clock frequency to be scaled in Hz
> - * @param target_freq Desired clock frequency in Hz
> - * @param best_fine_scalar Pointer to store the fine stage divisor
> - *
> - * @return best_main_scalar Main scalar for desired frequency or -1 if none
> - * found
> - */
> -static int clock_calc_best_scalar(unsigned int main_scaler_bits,
> - unsigned int fine_scalar_bits, unsigned int input_rate,
> - unsigned int target_rate, unsigned int *best_fine_scalar)
> -{
> - int i;
> - int best_main_scalar = -1;
> - unsigned int best_error = target_rate;
> - const unsigned int cap = (1 << fine_scalar_bits) - 1;
> - const unsigned int loops = 1 << main_scaler_bits;
> -
> - debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate,
> - target_rate, cap);
> -
> - assert(best_fine_scalar != NULL);
> - assert(main_scaler_bits <= fine_scalar_bits);
> -
> - *best_fine_scalar = 1;
> -
> - if (input_rate == 0 || target_rate == 0)
> - return -1;
> -
> - if (target_rate >= input_rate)
> - return 1;
> -
> - for (i = 1; i <= loops; i++) {
> - const unsigned int effective_div = max(min(input_rate / i /
> - target_rate, cap), 1);
> - const unsigned int effective_rate = input_rate / i /
> - effective_div;
> - const int error = target_rate - effective_rate;
> -
> - debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div,
> - effective_rate, error);
> -
> - if (error >= 0 && error <= best_error) {
> - best_error = error;
> - best_main_scalar = i;
> - *best_fine_scalar = effective_div;
> - }
> - }
> -
> - return best_main_scalar;
> -}
> -
> -static int exynos5_set_spi_clk(enum periph_id periph_id,
> - unsigned int rate)
> -{
> - struct exynos5_clock *clk =
> - (struct exynos5_clock *)samsung_get_base_clock();
> - int main;
> - unsigned int fine;
> - unsigned shift, pre_shift;
> - unsigned mask = 0xff;
> - u32 *reg;
> -
> - main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine);
> - if (main < 0) {
> - debug("%s: Cannot set clock rate for periph %d",
> - __func__, periph_id);
> - return -1;
> - }
> - main = main - 1;
> - fine = fine - 1;
> -
> - switch (periph_id) {
> - case PERIPH_ID_SPI0:
> - reg = &clk->div_peric1;
> - shift = 0;
> - pre_shift = 8;
> - break;
> - case PERIPH_ID_SPI1:
> - reg = &clk->div_peric1;
> - shift = 16;
> - pre_shift = 24;
> - break;
> - case PERIPH_ID_SPI2:
> - reg = &clk->div_peric2;
> - shift = 0;
> - pre_shift = 8;
> - break;
> - case PERIPH_ID_SPI3:
> - reg = &clk->sclk_div_isp;
> - shift = 0;
> - pre_shift = 4;
> - break;
> - case PERIPH_ID_SPI4:
> - reg = &clk->sclk_div_isp;
> - shift = 12;
> - pre_shift = 16;
> - break;
> - default:
> - debug("%s: Unsupported peripheral ID %d\n", __func__,
> - periph_id);
> - return -1;
> - }
> - clrsetbits_le32(reg, mask << shift, (main & mask) << shift);
> - clrsetbits_le32(reg, mask << pre_shift, (fine & mask) << pre_shift);
> -
> - return 0;
> -}
> -
> -static int exynos5420_set_spi_clk(enum periph_id periph_id,
> - unsigned int rate)
> -{
> - struct exynos5420_clock *clk =
> - (struct exynos5420_clock *)samsung_get_base_clock();
> - int main;
> - unsigned int fine, val;
> - unsigned shift, pre_shift;
> - unsigned div_mask = 0xf, pre_div_mask = 0xff;
> -
> - main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine);
> - if (main < 0) {
> - debug("%s: Cannot set clock rate for periph %d",
> - __func__, periph_id);
> - return -1;
> - }
> - main = main - 1;
> - fine = fine - 1;
> -
> - switch (periph_id) {
> - case PERIPH_ID_SPI0:
> - val = readl(&clk->div_peric1);
> - val &= ~(div_mask << 20);
> - val |= (main << 20);
> - writel(val, &clk->div_peric1);
> -
> - val = readl(&clk->div_peric4);
> - val &= ~(pre_div_mask << 8);
> - val |= (fine << 8);
> - writel(val, &clk->div_peric4);
> - break;
> - case PERIPH_ID_SPI1:
> - val = readl(&clk->div_peric1);
> - val &= ~(div_mask << 24);
> - val |= (main << 24);
> - writel(val, &clk->div_peric1);
> -
> - val = readl(&clk->div_peric4);
> - val &= ~(pre_div_mask << 16);
> - val |= (fine << 16);
> - writel(val, &clk->div_peric4);
> - break;
> - case PERIPH_ID_SPI2:
> - val = readl(&clk->div_peric1);
> - val &= ~(div_mask << 28);
> - val |= (main << 28);
> - writel(val, &clk->div_peric1);
> -
> - val = readl(&clk->div_peric4);
> - val &= ~(pre_div_mask << 24);
> - val |= (fine << 24);
> - writel(val, &clk->div_peric4);
> - break;
> - case PERIPH_ID_SPI3:
> - shift = 16;
> - pre_shift = 0;
> - clrsetbits_le32(&clk->div_isp1, div_mask << shift,
> - (main & div_mask) << shift);
> - clrsetbits_le32(&clk->div_isp1, pre_div_mask << pre_shift,
> - (fine & pre_div_mask) << pre_shift);
> - break;
> - case PERIPH_ID_SPI4:
> - shift = 20;
> - pre_shift = 8;
> - clrsetbits_le32(&clk->div_isp1, div_mask << shift,
> - (main & div_mask) << shift);
> - clrsetbits_le32(&clk->div_isp1, pre_div_mask << pre_shift,
> - (fine & pre_div_mask) << pre_shift);
> - break;
> - default:
> - debug("%s: Unsupported peripheral ID %d\n", __func__,
> - periph_id);
> - return -1;
> - }
> -
> - return 0;
> -}
> -
> static unsigned long exynos4_get_i2c_clk(void)
> {
> struct exynos4_clock *clk =
> @@ -1594,7 +1356,7 @@ unsigned long get_arm_clk(void)
> unsigned long get_i2c_clk(void)
> {
> if (cpu_is_exynos5()) {
> - return exynos5_get_i2c_clk();
> + return clock_get_periph_rate(PERIPH_ID_I2C0);
> } else if (cpu_is_exynos4()) {
> return exynos4_get_i2c_clk();
> } else {
> @@ -1606,8 +1368,6 @@ unsigned long get_i2c_clk(void)
> unsigned long get_pwm_clk(void)
> {
> if (cpu_is_exynos5()) {
> - if (proid_is_exynos5420())
> - return exynos5420_get_pwm_clk();
> return clock_get_periph_rate(PERIPH_ID_PWM0);
> } else {
> if (proid_is_exynos4412())
> @@ -1619,9 +1379,7 @@ unsigned long get_pwm_clk(void)
> unsigned long get_uart_clk(int dev_index)
> {
> if (cpu_is_exynos5()) {
> - if (proid_is_exynos5420())
> - return exynos5420_get_uart_clk(dev_index);
> - return exynos5_get_uart_clk(dev_index);
> + return clock_get_periph_rate(PERIPH_ID_UART0 + dev_index);
> } else {
> if (proid_is_exynos4412())
> return exynos4x12_get_uart_clk(dev_index);
> @@ -1632,9 +1390,7 @@ unsigned long get_uart_clk(int dev_index)
> unsigned long get_mmc_clk(int dev_index)
> {
> if (cpu_is_exynos5()) {
> - if (proid_is_exynos5420())
> - return exynos5420_get_mmc_clk(dev_index);
> - return exynos5_get_mmc_clk(dev_index);
> + return clock_get_periph_rate(PERIPH_ID_SDMMC0 + dev_index);
> } else {
> return exynos4_get_mmc_clk(dev_index);
> }
> @@ -1642,15 +1398,9 @@ unsigned long get_mmc_clk(int dev_index)
>
> void set_mmc_clk(int dev_index, unsigned int div)
> {
> - if (cpu_is_exynos5()) {
> - if (proid_is_exynos5420())
> - return exynos5420_set_mmc_clk(dev_index, div);
> - exynos5_set_mmc_clk(dev_index, div);
> - } else {
> if (proid_is_exynos4412())
> exynos4x12_set_mmc_clk(dev_index, div);
> exynos4_set_mmc_clk(dev_index, div);
> - }
> }
>
> unsigned long get_lcd_clk(void)
> @@ -1678,9 +1428,7 @@ void set_mipi_clk(void)
> int set_spi_clk(int periph_id, unsigned int rate)
> {
> if (cpu_is_exynos5()) {
> - if (proid_is_exynos5420())
> - return exynos5420_set_spi_clk(periph_id, rate);
> - return exynos5_set_spi_clk(periph_id, rate);
> + return clock_set_periph_rate(periph_id, rate);
> } else {
> return 0;
> }
> diff --git a/arch/arm/include/asm/arch-exynos/clk.h b/arch/arm/include/asm/arch-exynos/clk.h
> index cdeef32..a717805 100644
> --- a/arch/arm/include/asm/arch-exynos/clk.h
> +++ b/arch/arm/include/asm/arch-exynos/clk.h
> @@ -15,12 +15,9 @@
> #define VPLL 4
> #define BPLL 5
> #define RPLL 6
> -
> -enum pll_src_bit {
> - EXYNOS_SRC_MPLL = 6,
> - EXYNOS_SRC_EPLL,
> - EXYNOS_SRC_VPLL,
> -};
> +#define SPLL 7
> +#define CPLL 8
> +#define DPLL 9
>
> unsigned long get_pll_clk(int pllreg);
> unsigned long get_arm_clk(void);
> @@ -45,6 +42,25 @@ int set_spi_clk(int periph_id, unsigned int rate);
> *
> * @return frequency of the peripheral clk
> */
> -unsigned long clock_get_periph_rate(int peripheral);
> +long clock_get_periph_rate(int peripheral);
> +
> +/**
> + * set the clk frequency rate of the required peripheral
> + *
> + * @param peripheral Peripheral id
> + * @param rate frequency to be set
> + *
> + * @return 0 if success else -1
> + */
> +int clock_set_periph_rate(int periph_id, unsigned long rate);
>
> +/**
> + * set the clk source mux value of the required peripheral
> + *
> + * @param peripheral Peripheral id
> + * @param src source to be set
> + *
> + * @return 0 if success else -1
> + */
> +int clock_set_periph_source(int periph_id, int src);
> #endif
> diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
> index f7439a0..2290330 100644
> --- a/drivers/mmc/exynos_dw_mmc.c
> +++ b/drivers/mmc/exynos_dw_mmc.c
> @@ -45,19 +45,22 @@ unsigned int exynos_dwmci_get_clk(int dev_index)
> int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
> {
> struct dwmci_host *host = NULL;
> - unsigned int div;
> - unsigned long freq, sclk;
> + int ret;
> + unsigned long freq;
> host = malloc(sizeof(struct dwmci_host));
> if (!host) {
> printf("dwmci_host malloc fail!\n");
> return 1;
> }
> +
> /* request mmc clock vlaue of 52MHz. */
> freq = 52000000;
> - sclk = get_mmc_clk(index);
> - div = DIV_ROUND_UP(sclk, freq);
> - /* set the clock divisor for mmc */
> - set_mmc_clk(index, div);
> + /* set the clock rate for mmc */
> + ret = clock_set_periph_rate(PERIPH_ID_SDMMC0 + index, freq);
> + if (ret < 0) {
> + debug("Clock rate not set\n");
> + return -1;
> + }
>
> host->name = "EXYNOS DWMMC";
> host->ioaddr = (void *)regbase;
> --
> 1.7.12.4
>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
More information about the U-Boot
mailing list