[2/6,V7] EXYNOS: Add clock for SPI.

Message ID 1350906729-23749-3-git-send-email-hatim.rv@samsung.com
State New
Headers show

Commit Message

Hatim Ali Oct. 22, 2012, 11:52 a.m.
From: Rajeshwari Shinde <rajeshwari.s@samsung.com>

This patch adds api to calculate and set the clock for SPI channels

Signed-off-by: James Miller <jamesmiller@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Rajeshwari Shinde <rajeshwari.s@samsung.com>
Signed-off-by: Hatim Ali <hatim.rv@samsung.com>
---
Changes since v4:
	Added Signed-off-by of James Miller
Changes since v5:
	Incorporated review comments by Minkyu Kang
Changes since v6:
	Based on the review by Minkyu Kang, moved the include periph.h define to 
	clock.c file

Comments

Simon Glass Oct. 25, 2012, 5:16 p.m. | #1
On Mon, Oct 22, 2012 at 4:52 AM, Hatim Ali <hatim.rv@samsung.com> wrote:
> From: Rajeshwari Shinde <rajeshwari.s@samsung.com>
>
> This patch adds api to calculate and set the clock for SPI channels
>
> Signed-off-by: James Miller <jamesmiller@chromium.org>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@samsung.com>
> Signed-off-by: Hatim Ali <hatim.rv@samsung.com>

Acked-by: Simon Glass <sjg@chromium.org>

> ---
> Changes since v4:
>         Added Signed-off-by of James Miller
> Changes since v5:
>         Incorporated review comments by Minkyu Kang
> Changes since v6:
>         Based on the review by Minkyu Kang, moved the include periph.h define to
>         clock.c file
>
Minkyu Kang Oct. 26, 2012, 8:27 a.m. | #2
Dear Hatim Ali,

On 22 October 2012 20:52, Hatim Ali <hatim.rv@samsung.com> wrote:
> From: Rajeshwari Shinde <rajeshwari.s@samsung.com>
>
> This patch adds api to calculate and set the clock for SPI channels
>
> Signed-off-by: James Miller <jamesmiller@chromium.org>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> Signed-off-by: Rajeshwari Shinde <rajeshwari.s@samsung.com>
> Signed-off-by: Hatim Ali <hatim.rv@samsung.com>
> ---
> Changes since v4:
>         Added Signed-off-by of James Miller
> Changes since v5:
>         Incorporated review comments by Minkyu Kang
> Changes since v6:
>         Based on the review by Minkyu Kang, moved the include periph.h define to
>         clock.c file
>

Sorry, please rebase this patch.

Thanks.

Patch

diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c
index 4f3b451..e6c46da 100644
--- a/arch/arm/cpu/armv7/exynos/clock.c
+++ b/arch/arm/cpu/armv7/exynos/clock.c
@@ -25,6 +25,7 @@ 
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/clk.h>
+#include <asm/arch/periph.h>
 
 /* exynos4: return pll clock frequency */
 static unsigned long exynos4_get_pll_clk(int pllreg)
@@ -732,6 +733,122 @@  static unsigned long exynos5_get_i2c_clk(void)
 	return aclk_66;
 }
 
+/**
+ * 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;
+}
+
 unsigned long get_pll_clk(int pllreg)
 {
 	if (cpu_is_exynos5())
@@ -803,3 +920,11 @@  void set_mipi_clk(void)
 	if (cpu_is_exynos4())
 		exynos4_set_mipi_clk();
 }
+
+int set_spi_clk(int periph_id, unsigned int rate)
+{
+	if (cpu_is_exynos5())
+		return exynos5_set_spi_clk(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 5529025..c0a3455 100644
--- a/arch/arm/include/asm/arch-exynos/clk.h
+++ b/arch/arm/include/asm/arch-exynos/clk.h
@@ -38,5 +38,5 @@  void set_mmc_clk(int dev_index, unsigned int div);
 unsigned long get_lcd_clk(void);
 void set_lcd_clk(void);
 void set_mipi_clk(void);
-
+int set_spi_clk(int periph_id, unsigned int rate);
 #endif