diff mbox

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

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

Commit Message

Hatim Ali Oct. 16, 2012, 10:58 a.m. UTC
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>
---

Changes since v4:
	Added Signed-off-by of James Miller

 arch/arm/cpu/armv7/exynos/clock.c      |  124 ++++++++++++++++++++++++++++++++
 arch/arm/include/asm/arch-exynos/clk.h |    4 +-
 2 files changed, 127 insertions(+), 1 deletions(-)

Comments

Minkyu Kang Oct. 17, 2012, 2:06 a.m. UTC | #1
Dear Hatim,

On 16 October 2012 19:58, 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>
> ---
>
> Changes since v4:
>         Added Signed-off-by of James Miller
>
>  arch/arm/cpu/armv7/exynos/clock.c      |  124 ++++++++++++++++++++++++++++++++
>  arch/arm/include/asm/arch-exynos/clk.h |    4 +-
>  2 files changed, 127 insertions(+), 1 deletions(-)
>
> diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c
> index 4f3b451..44dff2b 100644
> --- a/arch/arm/cpu/armv7/exynos/clock.c
> +++ b/arch/arm/cpu/armv7/exynos/clock.c
> @@ -732,6 +732,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_spi_set_clock_rate(enum periph_id periph_id,

please modify to exynos5_set_spi_clk(int 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 +919,11 @@ void set_mipi_clk(void)
>         if (cpu_is_exynos4())
>                 exynos4_set_mipi_clk();
>  }
> +
> +int spi_set_clock_rate(enum periph_id periph_id, unsigned int rate)

set_spi_clk()

> +{
> +       if (cpu_is_exynos5())
> +               return exynos5_spi_set_clock_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 5529025..4e51402 100644
> --- a/arch/arm/include/asm/arch-exynos/clk.h
> +++ b/arch/arm/include/asm/arch-exynos/clk.h
> @@ -22,6 +22,8 @@
>  #ifndef __ASM_ARM_ARCH_CLK_H_
>  #define __ASM_ARM_ARCH_CLK_H_
>
> +#include <asm/arch/pinmux.h>

no. we don't need this header file.

> +
>  #define APLL   0
>  #define MPLL   1
>  #define EPLL   2
> @@ -38,5 +40,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 spi_set_clock_rate(enum periph_id periph_id, unsigned int rate);
>  #endif
> --
> 1.7.2.3
>

Thanks.
Minkyu Kang.
Hatim Rv Oct. 17, 2012, 5:21 a.m. UTC | #2
Dear Minkyu Kang,

On Wed, Oct 17, 2012 at 7:36 AM, Minkyu Kang <promsoft@gmail.com> wrote:
> Dear Hatim,
>
> On 16 October 2012 19:58, 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>
>> ---
>>
>> Changes since v4:
>>         Added Signed-off-by of James Miller
>>
>>  arch/arm/cpu/armv7/exynos/clock.c      |  124 ++++++++++++++++++++++++++++++++
>>  arch/arm/include/asm/arch-exynos/clk.h |    4 +-
>>  2 files changed, 127 insertions(+), 1 deletions(-)
>>
>> diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c
>> index 4f3b451..44dff2b 100644
>> --- a/arch/arm/cpu/armv7/exynos/clock.c
>> +++ b/arch/arm/cpu/armv7/exynos/clock.c
>> @@ -732,6 +732,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_spi_set_clock_rate(enum periph_id periph_id,
>
> please modify to exynos5_set_spi_clk(int periph_id, ....
>

Done. Reworked patches posted.

>> +                                       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 +919,11 @@ void set_mipi_clk(void)
>>         if (cpu_is_exynos4())
>>                 exynos4_set_mipi_clk();
>>  }
>> +
>> +int spi_set_clock_rate(enum periph_id periph_id, unsigned int rate)
>
> set_spi_clk()
>

Done. Reworked patches posted.

>> +{
>> +       if (cpu_is_exynos5())
>> +               return exynos5_spi_set_clock_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 5529025..4e51402 100644
>> --- a/arch/arm/include/asm/arch-exynos/clk.h
>> +++ b/arch/arm/include/asm/arch-exynos/clk.h
>> @@ -22,6 +22,8 @@
>>  #ifndef __ASM_ARM_ARCH_CLK_H_
>>  #define __ASM_ARM_ARCH_CLK_H_
>>
>> +#include <asm/arch/pinmux.h>
>
> no. we don't need this header file.
>

Actually we do need the asm/arch/periph.h file as we are using the
'enum periph_id'. Otherwise it will give compilation errors.

>> +
>>  #define APLL   0
>>  #define MPLL   1
>>  #define EPLL   2
>> @@ -38,5 +40,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 spi_set_clock_rate(enum periph_id periph_id, unsigned int rate);
>>  #endif
>> --
>> 1.7.2.3
>>
>
> Thanks.
> Minkyu Kang.
> --
> from. prom.
> www.promsoft.net
> _______________________________________________

Regards,
Hatim Ali
Minkyu Kang Oct. 17, 2012, 5:25 a.m. UTC | #3
Dear Hatim,

On 17 October 2012 14:21, Hatim Rv <rv.hatimali@gmail.com> wrote:
>>> diff --git a/arch/arm/include/asm/arch-exynos/clk.h b/arch/arm/include/asm/arch-exynos/clk.h
>>> index 5529025..4e51402 100644
>>> --- a/arch/arm/include/asm/arch-exynos/clk.h
>>> +++ b/arch/arm/include/asm/arch-exynos/clk.h
>>> @@ -22,6 +22,8 @@
>>>  #ifndef __ASM_ARM_ARCH_CLK_H_
>>>  #define __ASM_ARM_ARCH_CLK_H_
>>>
>>> +#include <asm/arch/pinmux.h>
>>
>> no. we don't need this header file.
>>
>
> Actually we do need the asm/arch/periph.h file as we are using the
> 'enum periph_id'. Otherwise it will give compilation errors.

So, I commented.

"please modify to exynos5_set_spi_clk(int periph_id"

we don't have to use enum at parameter.

Thanks.
Minkyu Kang.
Hatim Rv Oct. 17, 2012, 6:26 a.m. UTC | #4
Dear Minkyu Kang,

On Wed, Oct 17, 2012 at 10:55 AM, Minkyu Kang <promsoft@gmail.com> wrote:
> Dear Hatim,
>
> On 17 October 2012 14:21, Hatim Rv <rv.hatimali@gmail.com> wrote:
>>>> diff --git a/arch/arm/include/asm/arch-exynos/clk.h b/arch/arm/include/asm/arch-exynos/clk.h
>>>> index 5529025..4e51402 100644
>>>> --- a/arch/arm/include/asm/arch-exynos/clk.h
>>>> +++ b/arch/arm/include/asm/arch-exynos/clk.h
>>>> @@ -22,6 +22,8 @@
>>>>  #ifndef __ASM_ARM_ARCH_CLK_H_
>>>>  #define __ASM_ARM_ARCH_CLK_H_
>>>>
>>>> +#include <asm/arch/pinmux.h>
>>>
>>> no. we don't need this header file.
>>>
>>
>> Actually we do need the asm/arch/periph.h file as we are using the
>> 'enum periph_id'. Otherwise it will give compilation errors.
>
> So, I commented.
>
> "please modify to exynos5_set_spi_clk(int periph_id"
>
> we don't have to use enum at parameter.

Even if we make the enum as int in the clk.h we still need the enum
definition in clock.c.
Besides, making the enum as int in clk.h does not look good as it will
make the code confusing (by passing  enum as  int and using int as
enum).
So we require the periph.h declaration either in clk.h or clock.c file.
If there is any better way to proceed, please let me know.

>
> Thanks.
> Minkyu Kang.
> --
> from. prom.
> www.promsoft.net

Regards,
Hatim Ali
Minkyu Kang Oct. 17, 2012, 7:12 a.m. UTC | #5
Dear Hatim,

On 17 October 2012 15:26, Hatim Rv <rv.hatimali@gmail.com> wrote:
> Dear Minkyu Kang,
>
> On Wed, Oct 17, 2012 at 10:55 AM, Minkyu Kang <promsoft@gmail.com> wrote:
>> Dear Hatim,
>>
>> On 17 October 2012 14:21, Hatim Rv <rv.hatimali@gmail.com> wrote:
>>>>> diff --git a/arch/arm/include/asm/arch-exynos/clk.h b/arch/arm/include/asm/arch-exynos/clk.h
>>>>> index 5529025..4e51402 100644
>>>>> --- a/arch/arm/include/asm/arch-exynos/clk.h
>>>>> +++ b/arch/arm/include/asm/arch-exynos/clk.h
>>>>> @@ -22,6 +22,8 @@
>>>>>  #ifndef __ASM_ARM_ARCH_CLK_H_
>>>>>  #define __ASM_ARM_ARCH_CLK_H_
>>>>>
>>>>> +#include <asm/arch/pinmux.h>
>>>>
>>>> no. we don't need this header file.
>>>>
>>>
>>> Actually we do need the asm/arch/periph.h file as we are using the
>>> 'enum periph_id'. Otherwise it will give compilation errors.
>>
>> So, I commented.
>>
>> "please modify to exynos5_set_spi_clk(int periph_id"
>>
>> we don't have to use enum at parameter.
>
> Even if we make the enum as int in the clk.h we still need the enum
> definition in clock.c.

then, please include that file in clock.c

> Besides, making the enum as int in clk.h does not look good as it will
> make the code confusing (by passing  enum as  int and using int as
> enum).

No, it's not confusing.
enum is not a specific data type.
Please look pinmux.c we get paramter as int.

> So we require the periph.h declaration either in clk.h or clock.c file.
> If there is any better way to proceed, please let me know.

Actually, I want what you do not use periph_id at clock.c.
Why clock get dependency with pinmux?

Thanks.
Minkyu Kang.
diff mbox

Patch

diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c
index 4f3b451..44dff2b 100644
--- a/arch/arm/cpu/armv7/exynos/clock.c
+++ b/arch/arm/cpu/armv7/exynos/clock.c
@@ -732,6 +732,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_spi_set_clock_rate(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 +919,11 @@  void set_mipi_clk(void)
 	if (cpu_is_exynos4())
 		exynos4_set_mipi_clk();
 }
+
+int spi_set_clock_rate(enum periph_id periph_id, unsigned int rate)
+{
+	if (cpu_is_exynos5())
+		return exynos5_spi_set_clock_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 5529025..4e51402 100644
--- a/arch/arm/include/asm/arch-exynos/clk.h
+++ b/arch/arm/include/asm/arch-exynos/clk.h
@@ -22,6 +22,8 @@ 
 #ifndef __ASM_ARM_ARCH_CLK_H_
 #define __ASM_ARM_ARCH_CLK_H_
 
+#include <asm/arch/pinmux.h>
+
 #define APLL	0
 #define MPLL	1
 #define EPLL	2
@@ -38,5 +40,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 spi_set_clock_rate(enum periph_id periph_id, unsigned int rate);
 #endif