Change-Id: If20fe16260d2b584d4216d1dbabffcb25478fb1d
Signed-off-by: Elaine Zhang <zhangq...@rock-chips.com>
---
 .../include/asm/arch-rockchip/cru_rk3568.h    |  36 +++
 drivers/clk/rockchip/clk_rk3568.c             | 218 ++++++++++++++++++
 2 files changed, 254 insertions(+)

diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3568.h 
b/arch/arm/include/asm/arch-rockchip/cru_rk3568.h
index 9c7ddd751f72..0306ce57e369 100644
--- a/arch/arm/include/asm/arch-rockchip/cru_rk3568.h
+++ b/arch/arm/include/asm/arch-rockchip/cru_rk3568.h
@@ -211,6 +211,31 @@ enum {
        ACLK_PERIMID_SEL_100M,
        ACLK_PERIMID_SEL_24M,
 
+       /* CRU_CLK_SEL21_CON */
+       I2S3_MCLKOUT_TX_SEL_SHIFT       = 15,
+       I2S3_MCLKOUT_TX_SEL_MASK        = 1 << I2S3_MCLKOUT_TX_SEL_SHIFT,
+       I2S3_MCLKOUT_TX_SEL_MCLK        = 0,
+       I2S3_MCLKOUT_TX_SEL_12M,
+       CLK_I2S3_SEL_SHIFT              = 10,
+       CLK_I2S3_SEL_MASK               = 0x3 << CLK_I2S3_SEL_SHIFT,
+       CLK_I2S3_SEL_SRC                = 0,
+       CLK_I2S3_SEL_FRAC,
+       CLK_I2S3_SEL_CLKIN,
+       CLK_I2S3_SEL_XIN12M,
+       CLK_I2S3_SRC_SEL_SHIFT          = 8,
+       CLK_I2S3_SRC_SEL_MASK           = 0x3 << CLK_I2S3_SRC_SEL_SHIFT,
+       CLK_I2S3_SRC_SEL_GPLL           = 0,
+       CLK_I2S3_SRC_SEL_CPLL,
+       CLK_I2S3_SRC_SEL_NPLL,
+       CLK_I2S3_SRC_DIV_SHIFT          = 0,
+       CLK_I2S3_SRC_DIV_MASK           = 0x7f << CLK_I2S3_SRC_DIV_SHIFT,
+
+       /* CRU_CLK_SEL22_CON */
+       CLK_I2S3_FRAC_NUMERATOR_SHIFT   = 16,
+       CLK_I2S3_FRAC_NUMERATOR_MASK    = 0xffff << 16,
+       CLK_I2S3_FRAC_DENOMINATOR_SHIFT = 0,
+       CLK_I2S3_FRAC_DENOMINATOR_MASK  = 0xffff,
+
        /* CRU_CLK_SEL27_CON */
        CLK_CRYPTO_PKA_SEL_SHIFT        = 6,
        CLK_CRYPTO_PKA_SEL_MASK         = 3 << CLK_CRYPTO_PKA_SEL_SHIFT,
@@ -502,5 +527,16 @@ enum {
        /* CRU_CLK_SEL82_CON */
        CPLL_100M_DIV_SHIFT             = 0,
        CPLL_100M_DIV_MASK              = 0x1f << CPLL_100M_DIV_SHIFT,
+
+       /* GRF_SOC_CON2 */
+       I2S3_MCLKOUT_SEL_SHIFT          = 15,
+       I2S3_MCLKOUT_SEL_MASK           = 0x1 << I2S3_MCLKOUT_SEL_SHIFT,
+       I2S3_MCLKOUT_SEL_RX             = 0,
+       I2S3_MCLKOUT_SEL_TX,
+       I2S3_MCLK_IOE_SEL_SHIFT         = 3,
+       I2S3_MCLK_IOE_SEL_MASK          = 0x1 << I2S3_MCLK_IOE_SEL_SHIFT,
+       I2S3_MCLK_IOE_SEL_CLKIN         = 0,
+       I2S3_MCLK_IOE_SEL_CLKOUT,
+
 };
 #endif
diff --git a/drivers/clk/rockchip/clk_rk3568.c 
b/drivers/clk/rockchip/clk_rk3568.c
index 599b7b130eb9..469014439be4 100644
--- a/drivers/clk/rockchip/clk_rk3568.c
+++ b/drivers/clk/rockchip/clk_rk3568.c
@@ -13,6 +13,7 @@
 #include <asm/arch-rockchip/cru_rk3568.h>
 #include <asm/arch-rockchip/clock.h>
 #include <asm/arch-rockchip/hardware.h>
+#include <asm/arch-rockchip/grf_rk3568.h>
 #include <asm/io.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
@@ -2326,6 +2327,170 @@ static ulong rk3568_uart_set_rate(struct 
rk3568_clk_priv *priv,
        return rk3568_uart_get_rate(priv, clk_id);
 }
 
+#ifndef CONFIG_SPL_BUILD
+static ulong rk3568_i2s3_get_rate(struct rk3568_clk_priv *priv, ulong clk_id)
+{
+       struct rk3568_cru *cru = priv->cru;
+       struct rk3568_grf *grf = priv->grf;
+       u32 con, div, src, p_rate;
+       u32 reg, fracdiv, p_src;
+       unsigned long m, n;
+
+       switch (clk_id) {
+       case I2S3_MCLKOUT_TX:
+               con = readl(&cru->clksel_con[21]);
+               src = (con & I2S3_MCLKOUT_TX_SEL_MASK) >>
+                     I2S3_MCLKOUT_TX_SEL_SHIFT;
+               if (src == I2S3_MCLKOUT_TX_SEL_12M)
+                       p_rate = 12000000;
+               else
+                       p_rate = rk3568_i2s3_get_rate(priv, MCLK_I2S3_2CH_TX);
+               return p_rate;
+       case I2S3_MCLKOUT_RX:
+               con = readl(&cru->clksel_con[83]);
+               src = (con & I2S3_MCLKOUT_TX_SEL_MASK) >>
+                     I2S3_MCLKOUT_TX_SEL_SHIFT;
+               if (src == I2S3_MCLKOUT_TX_SEL_12M)
+                       p_rate = 12000000;
+               else
+                       p_rate = rk3568_i2s3_get_rate(priv, MCLK_I2S3_2CH_RX);
+               return p_rate;
+       case I2S3_MCLKOUT:
+               con = readl(&grf->soc_con2);
+               src = (con & I2S3_MCLKOUT_SEL_MASK)
+                     >> I2S3_MCLKOUT_SEL_SHIFT;
+               if (src == I2S3_MCLKOUT_SEL_RX)
+                       p_rate = rk3568_i2s3_get_rate(priv, I2S3_MCLKOUT_RX);
+               else
+                       p_rate = rk3568_i2s3_get_rate(priv, I2S3_MCLKOUT_TX);
+               return p_rate;
+       case MCLK_I2S3_2CH_RX:
+               reg = 83;
+               break;
+       case MCLK_I2S3_2CH_TX:
+               reg = 21;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       con = readl(&cru->clksel_con[reg]);
+       src = (con & CLK_I2S3_SEL_MASK) >> CLK_I2S3_SEL_SHIFT;
+       div = (con & CLK_I2S3_SRC_DIV_MASK) >> CLK_I2S3_SRC_DIV_SHIFT;
+       p_src = (con & CLK_I2S3_SRC_SEL_MASK) >> CLK_I2S3_SRC_SEL_SHIFT;
+       if (p_src == CLK_I2S3_SRC_SEL_GPLL)
+               p_rate = priv->gpll_hz;
+       else if (p_src == CLK_I2S3_SRC_SEL_CPLL)
+               p_rate = priv->cpll_hz;
+       else
+               p_rate = priv->npll_hz;
+       if (src == CLK_I2S3_SEL_SRC) {
+               return DIV_TO_RATE(p_rate, div);
+       } else if (src == CLK_I2S3_SEL_FRAC) {
+               fracdiv = readl(&cru->clksel_con[reg + 1]);
+               n = fracdiv & CLK_I2S3_FRAC_NUMERATOR_MASK;
+               n >>= CLK_I2S3_FRAC_NUMERATOR_SHIFT;
+               m = fracdiv & CLK_I2S3_FRAC_DENOMINATOR_MASK;
+               m >>= CLK_I2S3_FRAC_DENOMINATOR_SHIFT;
+               return DIV_TO_RATE(p_rate, div) * n / m;
+       } else {
+               return OSC_HZ / 2;
+       }
+}
+
+static ulong rk3568_i2s3_set_rate(struct rk3568_clk_priv *priv,
+                                 ulong clk_id, ulong rate)
+{
+       struct rk3568_cru *cru = priv->cru;
+       struct rk3568_grf *grf = priv->grf;
+       u32 reg, con, clk_src, i2s_src, div;
+       unsigned long m = 0, n = 0, val;
+
+       if (priv->gpll_hz % rate == 0) {
+               clk_src = CLK_I2S3_SRC_SEL_GPLL;
+               i2s_src = CLK_I2S3_SEL_SRC;
+               div = DIV_ROUND_UP(priv->gpll_hz, rate);
+       } else if (priv->cpll_hz % rate == 0) {
+               clk_src = CLK_I2S3_SRC_SEL_CPLL;
+               i2s_src = CLK_I2S3_SEL_SRC;
+               div = DIV_ROUND_UP(priv->gpll_hz, rate);
+       } else if (rate == OSC_HZ / 2) {
+               clk_src = CLK_I2S3_SRC_SEL_GPLL;
+               i2s_src = CLK_I2S3_SEL_XIN12M;
+               div = 1;
+       } else {
+               clk_src = CLK_I2S3_SRC_SEL_GPLL;
+               i2s_src = CLK_I2S3_SEL_FRAC;
+               div = 1;
+               rational_best_approximation(rate, priv->gpll_hz / div,
+                                           GENMASK(16 - 1, 0),
+                                           GENMASK(16 - 1, 0),
+                                           &m, &n);
+       }
+
+       switch (clk_id) {
+       case I2S3_MCLKOUT_TX:
+               if (rate == 12000000) {
+                       rk_clrsetreg(&cru->clksel_con[21],
+                                    I2S3_MCLKOUT_TX_SEL_MASK,
+                                    I2S3_MCLKOUT_TX_SEL_12M <<
+                                    I2S3_MCLKOUT_TX_SEL_SHIFT);
+               } else {
+                       rk3568_i2s3_set_rate(priv, MCLK_I2S3_2CH_TX, rate),
+                       rk_clrsetreg(&cru->clksel_con[21],
+                                    I2S3_MCLKOUT_TX_SEL_MASK,
+                                    I2S3_MCLKOUT_TX_SEL_MCLK <<
+                                    I2S3_MCLKOUT_TX_SEL_SHIFT);
+               }
+               return rk3568_i2s3_get_rate(priv, clk_id);
+       case I2S3_MCLKOUT_RX:
+               if (rate == 12000000) {
+                       rk_clrsetreg(&cru->clksel_con[83],
+                                    I2S3_MCLKOUT_TX_SEL_MASK,
+                                    I2S3_MCLKOUT_TX_SEL_12M <<
+                                    I2S3_MCLKOUT_TX_SEL_SHIFT);
+               } else {
+                       rk3568_i2s3_set_rate(priv, MCLK_I2S3_2CH_RX, rate),
+                       rk_clrsetreg(&cru->clksel_con[21],
+                                    I2S3_MCLKOUT_TX_SEL_MASK,
+                                    I2S3_MCLKOUT_TX_SEL_MCLK <<
+                                    I2S3_MCLKOUT_TX_SEL_SHIFT);
+               }
+               return rk3568_i2s3_get_rate(priv, clk_id);
+       case I2S3_MCLKOUT:
+               con = readl(&grf->soc_con2);
+               clk_src = (con & I2S3_MCLKOUT_SEL_MASK)
+                     >> I2S3_MCLKOUT_SEL_SHIFT;
+               if (clk_src == I2S3_MCLKOUT_SEL_RX)
+                       rk3568_i2s3_set_rate(priv, I2S3_MCLKOUT_RX, rate);
+               else
+                       rk3568_i2s3_set_rate(priv, I2S3_MCLKOUT_TX, rate);
+               return rk3568_i2s3_get_rate(priv, clk_id);
+       case MCLK_I2S3_2CH_RX:
+               reg = 83;
+               break;
+       case MCLK_I2S3_2CH_TX:
+               reg = 21;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       rk_clrsetreg(&cru->clksel_con[reg],
+                    CLK_I2S3_SEL_MASK | CLK_I2S3_SRC_SEL_MASK |
+                    CLK_I2S3_SRC_DIV_MASK,
+                    (clk_src << CLK_I2S3_SRC_SEL_SHIFT) |
+                    (i2s_src << CLK_I2S3_SEL_SHIFT) |
+                    ((div - 1) << CLK_I2S3_SRC_DIV_SHIFT));
+       if (m && n) {
+               val = m << CLK_I2S3_FRAC_NUMERATOR_SHIFT | n;
+               writel(val, &cru->clksel_con[reg + 1]);
+       }
+       return rk3568_i2s3_get_rate(priv, clk_id);
+}
+
+#endif
+
 static ulong rk3568_clk_get_rate(struct clk *clk)
 {
        struct rk3568_clk_priv *priv = dev_get_priv(clk->dev);
@@ -2463,6 +2628,13 @@ static ulong rk3568_clk_get_rate(struct clk *clk)
        case TCLK_WDT_NS:
                rate = OSC_HZ;
                break;
+       case I2S3_MCLKOUT_RX:
+       case I2S3_MCLKOUT_TX:
+       case MCLK_I2S3_2CH_RX:
+       case MCLK_I2S3_2CH_TX:
+       case I2S3_MCLKOUT:
+               rate = rk3568_i2s3_get_rate(priv, clk->id);
+               break;
 #endif
        case SCLK_UART1:
        case SCLK_UART2:
@@ -2648,6 +2820,13 @@ static ulong rk3568_clk_set_rate(struct clk *clk, ulong 
rate)
        case TCLK_WDT_NS:
                ret = OSC_HZ;
                break;
+       case I2S3_MCLKOUT_RX:
+       case I2S3_MCLKOUT_TX:
+       case MCLK_I2S3_2CH_RX:
+       case MCLK_I2S3_2CH_TX:
+       case I2S3_MCLKOUT:
+               ret = rk3568_i2s3_set_rate(priv, clk->id, rate);
+               break;
 #endif
        case SCLK_UART1:
        case SCLK_UART2:
@@ -2824,6 +3003,42 @@ static int rk3568_rkvdec_set_parent(struct clk *clk, 
struct clk *parent)
        return 0;
 }
 
+static int __maybe_unused rk3568_i2s3_set_parent(struct clk *clk,
+                                                struct clk *parent)
+{
+       struct rk3568_clk_priv *priv = dev_get_priv(clk->dev);
+       struct rk3568_grf *grf = priv->grf;
+
+       switch (clk->id) {
+       case I2S3_MCLK_IOE:
+               if (parent->id == I2S3_MCLKOUT) {
+                       rk_clrsetreg(&grf->soc_con2, I2S3_MCLK_IOE_SEL_MASK,
+                                    I2S3_MCLK_IOE_SEL_CLKOUT <<
+                                    I2S3_MCLK_IOE_SEL_SHIFT);
+               } else {
+                       rk_clrsetreg(&grf->soc_con2, I2S3_MCLK_IOE_SEL_MASK,
+                                    I2S3_MCLK_IOE_SEL_CLKIN <<
+                                    I2S3_MCLK_IOE_SEL_SHIFT);
+               }
+               break;
+       case I2S3_MCLKOUT:
+               if (parent->id == I2S3_MCLKOUT_RX) {
+                       rk_clrsetreg(&grf->soc_con2, I2S3_MCLKOUT_SEL_MASK,
+                                    I2S3_MCLKOUT_SEL_RX <<
+                                    I2S3_MCLKOUT_SEL_SHIFT);
+               } else {
+                       rk_clrsetreg(&grf->soc_con2, I2S3_MCLKOUT_SEL_MASK,
+                                    I2S3_MCLKOUT_SEL_TX <<
+                                    I2S3_MCLKOUT_SEL_SHIFT);
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int rk3568_clk_set_parent(struct clk *clk, struct clk *parent)
 {
        switch (clk->id) {
@@ -2848,6 +3063,9 @@ static int rk3568_clk_set_parent(struct clk *clk, struct 
clk *parent)
        case SCLK_GMAC1_RGMII_SPEED:
        case SCLK_GMAC1_RMII_SPEED:
                break;
+       case I2S3_MCLK_IOE:
+       case I2S3_MCLKOUT:
+               return rk3568_i2s3_set_parent(clk, parent);
        default:
                return -ENOENT;
        }
-- 
2.17.1

Reply via email to