From 8fa6979bebc91451d78ae49375a933f9e21cc755 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Thu, 20 Apr 2017 22:05:49 +0200 Subject: [PATCH] rockchip: clk: rk3399: add clock support for SCLK_SPI1 and SCLK_SPI5 This change adds support for configuring the module clocks for SPI1 and SPI5 from the 594MHz GPLL. Note that the driver (rk_spi.c) always sets this to 99MHz, but the implemented functionality is more general and will also support different clock configurations. X-AffectedPlatforms: RK3399-Q7 Signed-off-by: Philipp Tomsich Tested-by: Jakob Unterwurzacher Tested-by: Klaus Goger Acked-by: Simon Glass --- drivers/clk/rockchip/clk_rk3399.c | 115 ++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index ff3cc37af3..1c53ca247e 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -1,5 +1,6 @@ /* * (C) Copyright 2015 Google, Inc + * (C) 2017 Theobroma Systems Design und Consulting GmbH * * SPDX-License-Identifier: GPL-2.0 */ @@ -207,12 +208,15 @@ enum { DCLK_VOP_DIV_CON_SHIFT = 0, /* CLKSEL_CON58 */ - CLK_SPI_PLL_SEL_MASK = 1, - CLK_SPI_PLL_SEL_CPLL = 0, - CLK_SPI_PLL_SEL_GPLL = 1, - CLK_SPI_PLL_DIV_CON_MASK = 0x7f, - CLK_SPI5_PLL_DIV_CON_SHIFT = 8, - CLK_SPI5_PLL_SEL_SHIFT = 15, + CLK_SPI_PLL_SEL_WIDTH = 1, + CLK_SPI_PLL_SEL_MASK = ((1 < CLK_SPI_PLL_SEL_WIDTH) - 1), + CLK_SPI_PLL_SEL_CPLL = 0, + CLK_SPI_PLL_SEL_GPLL = 1, + CLK_SPI_PLL_DIV_CON_WIDTH = 7, + CLK_SPI_PLL_DIV_CON_MASK = ((1 << CLK_SPI_PLL_DIV_CON_WIDTH) - 1), + + CLK_SPI5_PLL_DIV_CON_SHIFT = 8, + CLK_SPI5_PLL_SEL_SHIFT = 15, /* CLKSEL_CON59 */ CLK_SPI1_PLL_SEL_SHIFT = 15, @@ -605,6 +609,96 @@ static ulong rk3399_i2c_set_clk(struct rk3399_cru *cru, ulong clk_id, uint hz) return DIV_TO_RATE(GPLL_HZ, src_clk_div); } +/* + * RK3399 SPI clocks have a common divider-width (7 bits) and a single bit + * to select either CPLL or GPLL as the clock-parent. The location within + * the enclosing CLKSEL_CON (i.e. div_shift and sel_shift) are variable. + */ + +struct spi_clkreg { + uint8_t reg; /* CLKSEL_CON[reg] register in CRU */ + uint8_t div_shift; + uint8_t sel_shift; +}; + +/* + * The entries are numbered relative to their offset from SCLK_SPI0. + * + * Note that SCLK_SPI3 (which is configured via PMUCRU and requires different + * logic is not supported). + */ +static const struct spi_clkreg spi_clkregs[] = { + [0] = { .reg = 59, + .div_shift = CLK_SPI0_PLL_DIV_CON_SHIFT, + .sel_shift = CLK_SPI0_PLL_SEL_SHIFT, }, + [1] = { .reg = 59, + .div_shift = CLK_SPI1_PLL_DIV_CON_SHIFT, + .sel_shift = CLK_SPI1_PLL_SEL_SHIFT, }, + [2] = { .reg = 60, + .div_shift = CLK_SPI2_PLL_DIV_CON_SHIFT, + .sel_shift = CLK_SPI2_PLL_SEL_SHIFT, }, + [3] = { .reg = 60, + .div_shift = CLK_SPI4_PLL_DIV_CON_SHIFT, + .sel_shift = CLK_SPI4_PLL_SEL_SHIFT, }, + [4] = { .reg = 58, + .div_shift = CLK_SPI5_PLL_DIV_CON_SHIFT, + .sel_shift = CLK_SPI5_PLL_SEL_SHIFT, }, +}; + +static inline u32 extract_bits(u32 val, unsigned width, unsigned shift) +{ + return (val >> shift) & ((1 << width) - 1); +} + +static ulong rk3399_spi_get_clk(struct rk3399_cru *cru, ulong clk_id) +{ + const struct spi_clkreg *spiclk = NULL; + u32 div, val; + + switch (clk_id) { + case SCLK_SPI0 ... SCLK_SPI5: + spiclk = &spi_clkregs[clk_id - SCLK_SPI0]; + break; + + default: + error("%s: SPI clk-id %ld not supported\n", __func__, clk_id); + return -EINVAL; + } + + val = readl(&cru->clksel_con[spiclk->reg]); + div = extract_bits(val, CLK_SPI_PLL_DIV_CON_WIDTH, spiclk->div_shift); + + return DIV_TO_RATE(GPLL_HZ, div); +} + +static ulong rk3399_spi_set_clk(struct rk3399_cru *cru, ulong clk_id, uint hz) +{ + const struct spi_clkreg *spiclk = NULL; + int src_clk_div; + + src_clk_div = RATE_TO_DIV(GPLL_HZ, hz); + assert(src_clk_div < 127); + + switch (clk_id) { + case SCLK_SPI1 ... SCLK_SPI5: + spiclk = &spi_clkregs[clk_id - SCLK_SPI0]; + break; + + default: + error("%s: SPI clk-id %ld not supported\n", __func__, clk_id); + return -EINVAL; + } + + rk_clrsetreg(&cru->clksel_con[spiclk->reg], + ((CLK_SPI_PLL_DIV_CON_MASK << spiclk->div_shift) | + (CLK_SPI_PLL_SEL_GPLL << spiclk->sel_shift)), + ((src_clk_div << spiclk->div_shift) | + (CLK_SPI_PLL_SEL_GPLL << spiclk->sel_shift))); + + + return DIV_TO_RATE(GPLL_HZ, src_clk_div); +} + static ulong rk3399_vop_set_clk(struct rk3399_cru *cru, ulong clk_id, u32 hz) { struct pll_div vpll_config = {0}; @@ -780,6 +874,12 @@ static ulong rk3399_clk_get_rate(struct clk *clk) case SCLK_I2C7: rate = rk3399_i2c_get_clk(priv->cru, clk->id); break; + case SCLK_SPI0...SCLK_SPI5: + rate = rk3399_spi_get_clk(priv->cru, clk->id); + break; + case SCLK_UART0: + case SCLK_UART2: + return 24000000; case DCLK_VOP0: case DCLK_VOP1: break; @@ -814,6 +914,9 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate) case SCLK_I2C7: ret = rk3399_i2c_set_clk(priv->cru, clk->id, rate); break; + case SCLK_SPI0...SCLK_SPI5: + ret = rk3399_spi_set_clk(priv->cru, clk->id, rate); + break; case DCLK_VOP0: case DCLK_VOP1: ret = rk3399_vop_set_clk(priv->cru, clk->id, rate); -- 2.25.1