From d3387979e7f70afd710016e83cdae489b31809f7 Mon Sep 17 00:00:00 2001 From: Chanho Park Date: Tue, 8 Dec 2015 10:11:22 +0900 Subject: [PATCH] arm: exynos: support spi clock setting for exynos4 This patch supports spi clock setting of exynos4. The exynos3250 also uses same setting with exynos4. Change-Id: Ibfb255f9c6dcac5b35732d9387e39a90af2545f9 Signed-off-by: Jaeyong Lee Signed-off-by: Chanho Park --- arch/arm/cpu/armv7/exynos/clock.c | 169 +++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c index 318e11604..8c52e5284 100644 --- a/arch/arm/cpu/armv7/exynos/clock.c +++ b/arch/arm/cpu/armv7/exynos/clock.c @@ -1441,6 +1441,73 @@ void exynos4_set_mipi_clk(void) writel(cfg, &clk->div_lcd0); } +/* get_spi_clk: return spi clock frequency */ +static unsigned long exynos4_get_spi_clk(int id) +{ + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); + unsigned long spiclk, dclk, sclk; + unsigned int sel; + unsigned int ratio; + unsigned int prescaler; + + /* + * CLK_SRC_PERIL1 + * SPI1_SEL [23:20] + * SPI0_SEL [19:16] + */ + sel = readl(&clk->src_peril1); + if (id) + sel = (sel >> 20) & 0xf; /* SPI1_SEL */ + else + sel = (sel >> 16) & 0xf; /* SPI0_SEL */ + + /* + * 0x6: SCLK_MPLL * 3250 : SCLK_MPLL_PRE_DIV (MPLL/2) + * 0x7: SCLK_EPLL + * 0x8: SCLK_VPLL + */ + if (sel == 0x6) { +#ifdef CONFIG_CPU_EXYNOS3250 + sclk = get_pll_clk(MPLL) / 8; +#else + sclk = get_pll_clk(MPLL); +#endif + } else if (sel == 0x7) + sclk = get_pll_clk(EPLL); + else if (sel == 0x8) + sclk = get_pll_clk(VPLL); + else + return 0; + + debug("%s : sclk %ld\n", __func__, sclk); + + /* + * CLK_DIV_PERIL1 + * SPI1_PRE_RATIO [31:24] + * SPI1_RATIO [19:16] + * SPI0_PRE_RATIO [15:8] + * SPI0_RATIO [3:0] + */ + if (id) { + ratio = readl(&clk->div_peril1); + prescaler = (ratio >> 24) & 0xff; + ratio = (ratio >> 16) & 0xf; + + dclk = sclk / (ratio + 1); + spiclk = dclk / (prescaler + 1); + } else { + ratio = readl(&clk->div_peril1); + prescaler = (ratio >> 8) & 0xff; + ratio = ratio & 0xf; + + dclk = sclk / (ratio + 1); + spiclk = dclk / (prescaler + 1); + } + + return spiclk; +} + /** * Linearly searches for the most accurate main and fine stage clock scalars * (divisors) for a specified target frequency and scalar bit sizes by checking @@ -1500,6 +1567,98 @@ static int clock_calc_best_scalar(unsigned int main_scaler_bits, return best_main_scalar; } +static int exynos4_set_spi_clk(enum periph_id periph_id, + unsigned int rate) +{ + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); + int main; + unsigned int fine; + unsigned shift, pre_shift; + unsigned mask = 0xff; + unsigned pre_mask = 0xf; + u32 *reg; + unsigned int source_clk = 0; + unsigned int cfg = 0; + static unsigned int spi_clock[2] = { 0, 0 }; + int id; + + switch (periph_id) { + case PERIPH_ID_SPI0: + id = 0; + break; + case PERIPH_ID_SPI1: + id = 1; + break; + default: + debug("%s: Unsupported peripheral ID %d\n", __func__, + periph_id); + return -1; + } + + /* check for spi clock update */ + if (spi_clock[id] == rate) { + debug("%s : spi clock is setup as %d.\n", __func__, rate); + return 0; + } else { + spi_clock[id] = rate; + } + + debug("%s : rate %d\n", __func__, rate); + + /* + * CLK_SRC_PERIL1 + * SPI1_SEL [23:20] + * SPI0_SEL [19:16] + */ + cfg = readl(&clk->src_peril1); + if (id) { + cfg &= ~(0xf << 20); + cfg |= (0x6 << 20); + } else { + cfg &= ~(0xf << 16); + cfg |= (0x6 << 16); + } + writel(cfg, &clk->src_peril1); + reg = &clk->src_peril1; + + source_clk = exynos4_get_spi_clk(id); + + debug("%s: id %d source_clk %d\n", __func__, id, source_clk); + + main = clock_calc_best_scalar(4, 8, source_clk, 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 (id) { + case 0: + reg = &clk->div_peril1; + shift = 0; + pre_shift = 8; + break; + case 1: + reg = &clk->div_peril1; + shift = 16; + pre_shift = 24; + 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, pre_mask << pre_shift, + (fine & pre_mask) << pre_shift); + + return 0; +} + static int exynos5_set_spi_clk(enum periph_id periph_id, unsigned int rate) { @@ -1661,6 +1820,14 @@ unsigned long get_lcd_clk(void) return 0; } +unsigned long get_spi_clk(int id) +{ + if (cpu_is_exynos4()) + return exynos4_get_spi_clk(id); + else + return 0; +} + void set_lcd_clk(void) { if (cpu_is_exynos4()) @@ -1678,5 +1845,5 @@ 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; + return exynos4_set_spi_clk(periph_id, rate); } -- 2.34.1