[U-Boot] [PATCH] Exynos5: Clock: Generic api to set and get clock rate and source
Rajeshwari S Shinde
rajeshwari.s at samsung.com
Mon Nov 11 13:53:44 CET 2013
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
More information about the U-Boot
mailing list