Exynos5420: Add clock initialization for 5420

This patch adds code for clock initialization and clock settings
of various IP's and controllers, required for Exynos5420

Signed-off-by: Rajeshwari S Shinde <rajeshwari.s@samsung.com>
Signed-off-by: Akshay Saraswat <akshay.s@samsung.com>
Acked-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Minkyu Kang <mk7.kang@samsung.com>
diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c
index 84a5047..5bde9d1 100644
--- a/arch/arm/cpu/armv7/exynos/clock.c
+++ b/arch/arm/cpu/armv7/exynos/clock.c
@@ -96,7 +96,7 @@
 
 	freq = CONFIG_SYS_CLK_FREQ;
 
-	if (pllreg == EPLL) {
+	if (pllreg == EPLL || pllreg == RPLL) {
 		k = k & 0xffff;
 		/* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
 		fout = (m + k / PLL_DIV_65536) * (freq / (p * (1 << s)));
@@ -117,7 +117,7 @@
 			div = PLL_DIV_1024;
 		else if (proid_is_exynos4412())
 			div = PLL_DIV_65535;
-		else if (proid_is_exynos5250())
+		else if (proid_is_exynos5250() || proid_is_exynos5420())
 			div = PLL_DIV_65536;
 		else
 			return 0;
@@ -362,6 +362,43 @@
 		return 0;
 }
 
+/* exynos5420: return pll clock frequency */
+static unsigned long exynos5420_get_pll_clk(int pllreg)
+{
+	struct exynos5420_clock *clk =
+		(struct exynos5420_clock *)samsung_get_base_clock();
+	unsigned long r, k = 0;
+
+	switch (pllreg) {
+	case APLL:
+		r = readl(&clk->apll_con0);
+		break;
+	case MPLL:
+		r = readl(&clk->mpll_con0);
+		break;
+	case EPLL:
+		r = readl(&clk->epll_con0);
+		k = readl(&clk->epll_con1);
+		break;
+	case VPLL:
+		r = readl(&clk->vpll_con0);
+		k = readl(&clk->vpll_con1);
+		break;
+	case BPLL:
+		r = readl(&clk->bpll_con0);
+		break;
+	case RPLL:
+		r = readl(&clk->rpll_con0);
+		k = readl(&clk->rpll_con1);
+		break;
+	default:
+		printf("Unsupported PLL (%d)\n", pllreg);
+		return 0;
+	}
+
+	return exynos_get_pll_clk(pllreg, r, k);
+}
+
 /* exynos4: return ARM clock frequency */
 static unsigned long exynos4_get_arm_clk(void)
 {
@@ -485,6 +522,27 @@
 	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_PERIC0
+	 * PWM_RATIO [31:28]
+	 */
+	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)
 {
@@ -624,6 +682,53 @@
 	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 [6:4]
+	 * UART1_SEL [10:8]
+	 * UART2_SEL [14:12]
+	 * UART3_SEL [18:16]
+	 * generalised calculation as follows
+	 * sel = (sel >> ((dev_index * 4) + 4)) & mask;
+	 */
+	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 [11:8]
+	 * UART1_RATIO [15:12]
+	 * UART2_RATIO [19:16]
+	 * UART3_RATIO [23:20]
+	 * generalised calculation as follows
+	 * ratio = (ratio >> ((dev_index * 4) + 8)) & mask;
+	 */
+	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 =
@@ -718,6 +823,47 @@
 	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;
+
+	/*
+	 * CLK_SRC_FSYS
+	 * MMC0_SEL [10:8]
+	 * MMC1_SEL [14:12]
+	 * MMC2_SEL [18:16]
+	 * generalised calculation as follows
+	 * sel = (sel >> ((dev_index * 4) + 8)) & mask
+	 */
+	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;
+
+	/*
+	 * CLK_DIV_FSYS1
+	 * MMC0_RATIO [9:0]
+	 * MMC1_RATIO [19:10]
+	 * MMC2_RATIO [29:20]
+	 * generalised calculation as follows
+	 * ratio = (ratio >> (dev_index * 10)) & mask
+	 */
+	ratio = readl(&clk->div_fsys1);
+	ratio = (ratio >> (dev_index * 10)) & 0x3ff;
+
+	uclk = (sclk / (ratio + 1));
+
+	return uclk;
+}
+
 /* exynos4: set the mmc clock */
 static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
 {
@@ -804,6 +950,29 @@
 	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;
+
+	/*
+	 * CLK_DIV_FSYS1
+	 * MMC0_RATIO [9:0]
+	 * MMC1_RATIO [19:10]
+	 * MMC2_RATIO [29:20]
+	 */
+	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)
 {
@@ -1324,6 +1493,71 @@
 	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;
+	unsigned shift, pre_shift;
+	unsigned div_mask = 0xf, pre_div_mask = 0xff;
+	u32 *reg;
+	u32 *pre_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 = 20;
+		pre_reg = &clk->div_peric4;
+		pre_shift = 8;
+		break;
+	case PERIPH_ID_SPI1:
+		reg = &clk->div_peric1;
+		shift = 24;
+		pre_reg = &clk->div_peric4;
+		pre_shift = 16;
+		break;
+	case PERIPH_ID_SPI2:
+		reg = &clk->div_peric1;
+		shift = 28;
+		pre_reg = &clk->div_peric4;
+		pre_shift = 24;
+		break;
+	case PERIPH_ID_SPI3:
+		reg = &clk->div_isp1;
+		shift = 16;
+		pre_reg = &clk->div_isp1;
+		pre_shift = 0;
+		break;
+	case PERIPH_ID_SPI4:
+		reg = &clk->div_isp1;
+		shift = 20;
+		pre_reg = &clk->div_isp1;
+		pre_shift = 8;
+		break;
+	default:
+		debug("%s: Unsupported peripheral ID %d\n", __func__,
+		      periph_id);
+		return -1;
+	}
+
+	clrsetbits_le32(reg, div_mask << shift, (main & div_mask) << shift);
+	clrsetbits_le32(pre_reg, pre_div_mask << pre_shift,
+			(fine & pre_div_mask) << pre_shift);
+
+	return 0;
+}
+
 static unsigned long exynos4_get_i2c_clk(void)
 {
 	struct exynos4_clock *clk =
@@ -1341,9 +1575,11 @@
 
 unsigned long get_pll_clk(int pllreg)
 {
-	if (cpu_is_exynos5())
+	if (cpu_is_exynos5()) {
+		if (proid_is_exynos5420())
+			return exynos5420_get_pll_clk(pllreg);
 		return exynos5_get_pll_clk(pllreg);
-	else {
+	} else {
 		if (proid_is_exynos4412())
 			return exynos4x12_get_pll_clk(pllreg);
 		return exynos4_get_pll_clk(pllreg);
@@ -1375,9 +1611,11 @@
 
 unsigned long get_pwm_clk(void)
 {
-	if (cpu_is_exynos5())
+	if (cpu_is_exynos5()) {
+		if (proid_is_exynos5420())
+			return exynos5420_get_pwm_clk();
 		return clock_get_periph_rate(PERIPH_ID_PWM0);
-	else {
+	} else {
 		if (proid_is_exynos4412())
 			return exynos4x12_get_pwm_clk();
 		return exynos4_get_pwm_clk();
@@ -1386,9 +1624,11 @@
 
 unsigned long get_uart_clk(int dev_index)
 {
-	if (cpu_is_exynos5())
+	if (cpu_is_exynos5()) {
+		if (proid_is_exynos5420())
+			return exynos5420_get_uart_clk(dev_index);
 		return exynos5_get_uart_clk(dev_index);
-	else {
+	} else {
 		if (proid_is_exynos4412())
 			return exynos4x12_get_uart_clk(dev_index);
 		return exynos4_get_uart_clk(dev_index);
@@ -1397,17 +1637,23 @@
 
 unsigned long get_mmc_clk(int dev_index)
 {
-	if (cpu_is_exynos5())
+	if (cpu_is_exynos5()) {
+		if (proid_is_exynos5420())
+			return exynos5420_get_mmc_clk(dev_index);
 		return exynos5_get_mmc_clk(dev_index);
-	else
+	} else {
 		return exynos4_get_mmc_clk(dev_index);
+	}
 }
 
 void set_mmc_clk(int dev_index, unsigned int div)
 {
-	if (cpu_is_exynos5())
-		exynos5_set_mmc_clk(dev_index, div);
-	else {
+	if (cpu_is_exynos5()) {
+		if (proid_is_exynos5420())
+			exynos5420_set_mmc_clk(dev_index, div);
+		else
+			exynos5_set_mmc_clk(dev_index, div);
+	} else {
 		if (proid_is_exynos4412())
 			exynos4x12_set_mmc_clk(dev_index, div);
 		else
@@ -1439,10 +1685,13 @@
 
 int set_spi_clk(int periph_id, unsigned int rate)
 {
-	if (cpu_is_exynos5())
+	if (cpu_is_exynos5()) {
+		if (proid_is_exynos5420())
+			return exynos5420_set_spi_clk(periph_id, rate);
 		return exynos5_set_spi_clk(periph_id, rate);
-	else
+	} else {
 		return 0;
+	}
 }
 
 int set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq,