ARM64: DTS: Add rk3399-firefly uart4 device, node as /dev/ttyS1
[firefly-linux-kernel-4.4.55.git] / drivers / clk / rockchip / clk-pll.c
index 097eaef9dc6a699152747b08741d0740108acb16..d61d2a170496c69e019c9bb2cfa1f95354764934 100644 (file)
 #include <linux/clk-provider.h>
 #include <linux/regmap.h>
 #include <linux/clk.h>
+#include <linux/gcd.h>
 #include "clk.h"
 
 #define PLL_MODE_MASK          0x3
 #define PLL_MODE_SLOW          0x0
 #define PLL_MODE_NORM          0x1
 #define PLL_MODE_DEEP          0x2
+#define PLL_RK3328_MODE_MASK   0x1
 
 struct rockchip_clk_pll {
        struct clk_hw           hw;
@@ -59,6 +61,198 @@ static void rockchip_rk3366_pll_get_params(struct rockchip_clk_pll *pll,
 static int rockchip_rk3366_pll_set_params(struct rockchip_clk_pll *pll,
                                const struct rockchip_pll_rate_table *rate);
 
+#define MHZ                    (1000UL * 1000UL)
+#define KHZ                    (1000UL)
+
+/* CLK_PLL_TYPE_RK3066_AUTO type ops */
+#define PLL_FREF_MIN           (269 * KHZ)
+#define PLL_FREF_MAX           (2200 * MHZ)
+
+#define PLL_FVCO_MIN           (440 * MHZ)
+#define PLL_FVCO_MAX           (2200 * MHZ)
+
+#define PLL_FOUT_MIN           (27500 * KHZ)
+#define PLL_FOUT_MAX           (2200 * MHZ)
+
+#define PLL_NF_MAX             (4096)
+#define PLL_NR_MAX             (64)
+#define PLL_NO_MAX             (16)
+
+/* CLK_PLL_TYPE_RK3036/3366/3399_AUTO type ops */
+#define MIN_FOUTVCO_FREQ       (800 * MHZ)
+#define MAX_FOUTVCO_FREQ       (2000 * MHZ)
+
+static struct rockchip_pll_rate_table auto_table;
+
+static struct rockchip_pll_rate_table *rk_pll_rate_table_get(void)
+{
+       return &auto_table;
+}
+
+static int rockchip_pll_clk_set_postdiv(unsigned long fout_hz,
+                                       u32 *postdiv1,
+                                       u32 *postdiv2,
+                                       u32 *foutvco)
+{
+       unsigned long freq;
+
+       if (fout_hz < MIN_FOUTVCO_FREQ) {
+               for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) {
+                       for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) {
+                               freq = fout_hz * (*postdiv1) * (*postdiv2);
+                               if (freq >= MIN_FOUTVCO_FREQ &&
+                                   freq <= MAX_FOUTVCO_FREQ) {
+                                       *foutvco = freq;
+                                       return 0;
+                               }
+                       }
+               }
+               pr_err("CANNOT FIND postdiv1/2 to make fout in range from 800M to 2000M,fout = %lu\n",
+                      fout_hz);
+       } else {
+               *postdiv1 = 1;
+               *postdiv2 = 1;
+       }
+       return 0;
+}
+
+static struct rockchip_pll_rate_table *
+rockchip_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
+                            unsigned long fin_hz,
+                            unsigned long fout_hz)
+{
+       struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get();
+       /* FIXME set postdiv1/2 always 1*/
+       u32 foutvco = fout_hz;
+       u64 fin_64, frac_64;
+       u32 f_frac, postdiv1, postdiv2;
+       unsigned long clk_gcd = 0;
+
+       if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
+               return NULL;
+
+       rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco);
+       rate_table->postdiv1 = postdiv1;
+       rate_table->postdiv2 = postdiv2;
+       rate_table->dsmpd = 1;
+
+       if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
+               fin_hz /= MHZ;
+               foutvco /= MHZ;
+               clk_gcd = gcd(fin_hz, foutvco);
+               rate_table->refdiv = fin_hz / clk_gcd;
+               rate_table->fbdiv = foutvco / clk_gcd;
+
+               rate_table->frac = 0;
+
+               pr_debug("fin = %lu, fout = %lu, clk_gcd = %lu, refdiv = %u, fbdiv = %u, postdiv1 = %u, postdiv2 = %u, frac = %u\n",
+                        fin_hz, fout_hz, clk_gcd, rate_table->refdiv,
+                        rate_table->fbdiv, rate_table->postdiv1,
+                        rate_table->postdiv2, rate_table->frac);
+       } else {
+               pr_debug("frac div running, fin_hz = %lu, fout_hz = %lu, fin_INT_mhz = %lu, fout_INT_mhz = %lu\n",
+                        fin_hz, fout_hz,
+                        fin_hz / MHZ * MHZ,
+                        fout_hz / MHZ * MHZ);
+               pr_debug("frac get postdiv1 = %u,  postdiv2 = %u, foutvco = %u\n",
+                        rate_table->postdiv1, rate_table->postdiv2, foutvco);
+               clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ);
+               rate_table->refdiv = fin_hz / MHZ / clk_gcd;
+               rate_table->fbdiv = foutvco / MHZ / clk_gcd;
+               pr_debug("frac get refdiv = %u,  fbdiv = %u\n",
+                        rate_table->refdiv, rate_table->fbdiv);
+
+               rate_table->frac = 0;
+
+               f_frac = (foutvco % MHZ);
+               fin_64 = fin_hz;
+               do_div(fin_64, (u64)rate_table->refdiv);
+               frac_64 = (u64)f_frac << 24;
+               do_div(frac_64, fin_64);
+               rate_table->frac = (u32)frac_64;
+               if (rate_table->frac > 0)
+                       rate_table->dsmpd = 0;
+               pr_debug("frac = %x\n", rate_table->frac);
+       }
+       return rate_table;
+}
+
+static struct rockchip_pll_rate_table *
+rockchip_rk3066_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
+                                   unsigned long fin_hz,
+                                   unsigned long fout_hz)
+{
+       struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get();
+       u32 nr, nf, no, nonr;
+       u32 nr_out, nf_out, no_out;
+       u32 n;
+       u32 numerator, denominator;
+       u64 fref, fvco, fout;
+       unsigned long clk_gcd = 0;
+
+       nr_out = PLL_NR_MAX + 1;
+       no_out = 0;
+       nf_out = 0;
+
+       if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
+               return NULL;
+
+       clk_gcd = gcd(fin_hz, fout_hz);
+
+       numerator = fout_hz / clk_gcd;
+       denominator = fin_hz / clk_gcd;
+
+       for (n = 1;; n++) {
+               nf = numerator * n;
+               nonr = denominator * n;
+               if (nf > PLL_NF_MAX || nonr > (PLL_NO_MAX * PLL_NR_MAX))
+                       break;
+
+               for (no = 1; no <= PLL_NO_MAX; no++) {
+                       if (!(no == 1 || !(no % 2)))
+                               continue;
+
+                       if (nonr % no)
+                               continue;
+                       nr = nonr / no;
+
+                       if (nr > PLL_NR_MAX)
+                               continue;
+
+                       fref = fin_hz / nr;
+                       if (fref < PLL_FREF_MIN || fref > PLL_FREF_MAX)
+                               continue;
+
+                       fvco = fref * nf;
+                       if (fvco < PLL_FVCO_MIN || fvco > PLL_FVCO_MAX)
+                               continue;
+
+                       fout = fvco / no;
+                       if (fout < PLL_FOUT_MIN || fout > PLL_FOUT_MAX)
+                               continue;
+
+                       /* select the best from all available PLL settings */
+                       if ((no > no_out) ||
+                           ((no == no_out) && (nr < nr_out))) {
+                               nr_out = nr;
+                               nf_out = nf;
+                               no_out = no;
+                       }
+               }
+       }
+
+       /* output the best PLL setting */
+       if ((nr_out <= PLL_NR_MAX) && (no_out > 0)) {
+               rate_table->nr = nr_out;
+               rate_table->nf = nf_out;
+               rate_table->no = no_out;
+       } else {
+               return NULL;
+       }
+
+       return rate_table;
+}
+
 static const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
                            struct rockchip_clk_pll *pll, unsigned long rate)
 {
@@ -70,24 +264,16 @@ static const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
                        return &rate_table[i];
        }
 
-       return NULL;
+       if (pll->type == pll_rk3066)
+               return rockchip_rk3066_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
+       else
+               return rockchip_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
 }
 
 static long rockchip_pll_round_rate(struct clk_hw *hw,
                            unsigned long drate, unsigned long *prate)
 {
-       struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
-       const struct rockchip_pll_rate_table *rate_table = pll->rate_table;
-       int i;
-
-       /* Assumming rate_table is in descending order */
-       for (i = 0; i < pll->rate_count; i++) {
-               if (drate >= rate_table[i].rate)
-                       return rate_table[i].rate;
-       }
-
-       /* return minimum supported value */
-       return rate_table[i - 1].rate;
+       return drate;
 }
 
 /*
@@ -287,6 +473,7 @@ static int rockchip_rk3036_pll_enable(struct clk_hw *hw)
 
        writel(HIWORD_UPDATE(0, RK3036_PLLCON1_PWRDOWN, 0),
               pll->reg_base + RK3036_PLLCON(1));
+       rockchip_pll_wait_lock(pll);
 
        return 0;
 }
@@ -532,6 +719,7 @@ static int rockchip_rk3066_pll_enable(struct clk_hw *hw)
 
        writel(HIWORD_UPDATE(0, RK3066_PLLCON3_PWRDOWN, 0),
               pll->reg_base + RK3066_PLLCON(3));
+       rockchip_pll_wait_lock(pll);
 
        return 0;
 }
@@ -756,6 +944,278 @@ static const struct clk_ops rockchip_rk3366_pll_clk_ops = {
        .init = rockchip_rk3036_pll_init,
 };
 
+/**
+ * PLL used in RK3399
+ */
+
+#define RK3399_PLLCON(i)                       (i * 0x4)
+#define RK3399_PLLCON0_FBDIV_MASK              0xfff
+#define RK3399_PLLCON0_FBDIV_SHIFT             0
+#define RK3399_PLLCON1_REFDIV_MASK             0x3f
+#define RK3399_PLLCON1_REFDIV_SHIFT            0
+#define RK3399_PLLCON1_POSTDIV1_MASK           0x7
+#define RK3399_PLLCON1_POSTDIV1_SHIFT          8
+#define RK3399_PLLCON1_POSTDIV2_MASK           0x7
+#define RK3399_PLLCON1_POSTDIV2_SHIFT          12
+#define RK3399_PLLCON2_FRAC_MASK               0xffffff
+#define RK3399_PLLCON2_FRAC_SHIFT              0
+#define RK3399_PLLCON2_LOCK_STATUS             BIT(31)
+#define RK3399_PLLCON3_PWRDOWN                 BIT(0)
+#define RK3399_PLLCON3_DSMPD_MASK              0x1
+#define RK3399_PLLCON3_DSMPD_SHIFT             3
+
+static int rockchip_rk3399_pll_wait_lock(struct rockchip_clk_pll *pll)
+{
+       u32 pllcon;
+       int delay = 24000000;
+
+       /* poll check the lock status in rk3399 xPLLCON2 */
+       while (delay > 0) {
+               pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2));
+               if (pllcon & RK3399_PLLCON2_LOCK_STATUS)
+                       return 0;
+
+               delay--;
+       }
+
+       pr_err("%s: timeout waiting for pll to lock\n", __func__);
+       return -ETIMEDOUT;
+}
+
+static void rockchip_rk3399_pll_get_params(struct rockchip_clk_pll *pll,
+                                       struct rockchip_pll_rate_table *rate)
+{
+       u32 pllcon;
+
+       pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(0));
+       rate->fbdiv = ((pllcon >> RK3399_PLLCON0_FBDIV_SHIFT)
+                               & RK3399_PLLCON0_FBDIV_MASK);
+
+       pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(1));
+       rate->refdiv = ((pllcon >> RK3399_PLLCON1_REFDIV_SHIFT)
+                               & RK3399_PLLCON1_REFDIV_MASK);
+       rate->postdiv1 = ((pllcon >> RK3399_PLLCON1_POSTDIV1_SHIFT)
+                               & RK3399_PLLCON1_POSTDIV1_MASK);
+       rate->postdiv2 = ((pllcon >> RK3399_PLLCON1_POSTDIV2_SHIFT)
+                               & RK3399_PLLCON1_POSTDIV2_MASK);
+
+       pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2));
+       rate->frac = ((pllcon >> RK3399_PLLCON2_FRAC_SHIFT)
+                               & RK3399_PLLCON2_FRAC_MASK);
+
+       pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(3));
+       rate->dsmpd = ((pllcon >> RK3399_PLLCON3_DSMPD_SHIFT)
+                               & RK3399_PLLCON3_DSMPD_MASK);
+}
+
+static unsigned long rockchip_rk3399_pll_recalc_rate(struct clk_hw *hw,
+                                                    unsigned long prate)
+{
+       struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+       struct rockchip_pll_rate_table cur;
+       u64 rate64 = prate;
+
+       rockchip_rk3399_pll_get_params(pll, &cur);
+
+       rate64 *= cur.fbdiv;
+       do_div(rate64, cur.refdiv);
+
+       if (cur.dsmpd == 0) {
+               /* fractional mode */
+               u64 frac_rate64 = prate * cur.frac;
+
+               do_div(frac_rate64, cur.refdiv);
+               rate64 += frac_rate64 >> 24;
+       }
+
+       do_div(rate64, cur.postdiv1);
+       do_div(rate64, cur.postdiv2);
+
+       return (unsigned long)rate64;
+}
+
+static int rockchip_rk3399_pll_set_params(struct rockchip_clk_pll *pll,
+                               const struct rockchip_pll_rate_table *rate)
+{
+       const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
+       struct clk_mux *pll_mux = &pll->pll_mux;
+       struct rockchip_pll_rate_table cur;
+       u32 pllcon;
+       int rate_change_remuxed = 0;
+       int cur_parent;
+       int ret;
+
+       pr_debug("%s: rate settings for %lu fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
+               __func__, rate->rate, rate->fbdiv, rate->postdiv1, rate->refdiv,
+               rate->postdiv2, rate->dsmpd, rate->frac);
+
+       rockchip_rk3399_pll_get_params(pll, &cur);
+       cur.rate = 0;
+
+       cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
+       if (cur_parent == PLL_MODE_NORM) {
+               pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
+               rate_change_remuxed = 1;
+       }
+
+       /* set pll power down */
+       writel(HIWORD_UPDATE(RK3399_PLLCON3_PWRDOWN,
+                            RK3399_PLLCON3_PWRDOWN, 0),
+              pll->reg_base + RK3399_PLLCON(3));
+
+       /* update pll values */
+       writel_relaxed(HIWORD_UPDATE(rate->fbdiv, RK3399_PLLCON0_FBDIV_MASK,
+                                                 RK3399_PLLCON0_FBDIV_SHIFT),
+                      pll->reg_base + RK3399_PLLCON(0));
+
+       writel_relaxed(HIWORD_UPDATE(rate->refdiv, RK3399_PLLCON1_REFDIV_MASK,
+                                                  RK3399_PLLCON1_REFDIV_SHIFT) |
+                      HIWORD_UPDATE(rate->postdiv1, RK3399_PLLCON1_POSTDIV1_MASK,
+                                                    RK3399_PLLCON1_POSTDIV1_SHIFT) |
+                      HIWORD_UPDATE(rate->postdiv2, RK3399_PLLCON1_POSTDIV2_MASK,
+                                                    RK3399_PLLCON1_POSTDIV2_SHIFT),
+                      pll->reg_base + RK3399_PLLCON(1));
+
+       /* xPLL CON2 is not HIWORD_MASK */
+       pllcon = readl_relaxed(pll->reg_base + RK3399_PLLCON(2));
+       pllcon &= ~(RK3399_PLLCON2_FRAC_MASK << RK3399_PLLCON2_FRAC_SHIFT);
+       pllcon |= rate->frac << RK3399_PLLCON2_FRAC_SHIFT;
+       writel_relaxed(pllcon, pll->reg_base + RK3399_PLLCON(2));
+
+       writel_relaxed(HIWORD_UPDATE(rate->dsmpd, RK3399_PLLCON3_DSMPD_MASK,
+                                           RK3399_PLLCON3_DSMPD_SHIFT),
+                      pll->reg_base + RK3399_PLLCON(3));
+
+       /* set pll power up */
+       writel(HIWORD_UPDATE(0,
+                            RK3399_PLLCON3_PWRDOWN, 0),
+              pll->reg_base + RK3399_PLLCON(3));
+
+       /* wait for the pll to lock */
+       ret = rockchip_rk3399_pll_wait_lock(pll);
+       if (ret) {
+               pr_warn("%s: pll update unsucessful, trying to restore old params\n",
+                       __func__);
+               rockchip_rk3399_pll_set_params(pll, &cur);
+       }
+
+       if (rate_change_remuxed)
+               pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
+
+       return ret;
+}
+
+static int rockchip_rk3399_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+                                       unsigned long prate)
+{
+       struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+       const struct rockchip_pll_rate_table *rate;
+       unsigned long old_rate = rockchip_rk3399_pll_recalc_rate(hw, prate);
+
+       pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n",
+                __func__, __clk_get_name(hw->clk), old_rate, drate, prate);
+
+       /* Get required rate settings from table */
+       rate = rockchip_get_pll_settings(pll, drate);
+       if (!rate) {
+               pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+                       drate, __clk_get_name(hw->clk));
+               return -EINVAL;
+       }
+
+       return rockchip_rk3399_pll_set_params(pll, rate);
+}
+
+static int rockchip_rk3399_pll_enable(struct clk_hw *hw)
+{
+       struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+       writel(HIWORD_UPDATE(0, RK3399_PLLCON3_PWRDOWN, 0),
+              pll->reg_base + RK3399_PLLCON(3));
+       rockchip_rk3399_pll_wait_lock(pll);
+
+       return 0;
+}
+
+static void rockchip_rk3399_pll_disable(struct clk_hw *hw)
+{
+       struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+       writel(HIWORD_UPDATE(RK3399_PLLCON3_PWRDOWN,
+                            RK3399_PLLCON3_PWRDOWN, 0),
+              pll->reg_base + RK3399_PLLCON(3));
+}
+
+static int rockchip_rk3399_pll_is_enabled(struct clk_hw *hw)
+{
+       struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+       u32 pllcon = readl(pll->reg_base + RK3399_PLLCON(3));
+
+       return !(pllcon & RK3399_PLLCON3_PWRDOWN);
+}
+
+static void rockchip_rk3399_pll_init(struct clk_hw *hw)
+{
+       struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+       const struct rockchip_pll_rate_table *rate;
+       struct rockchip_pll_rate_table cur;
+       unsigned long drate;
+
+       if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
+               return;
+
+       drate = clk_hw_get_rate(hw);
+       rate = rockchip_get_pll_settings(pll, drate);
+
+       /* when no rate setting for the current rate, rely on clk_set_rate */
+       if (!rate)
+               return;
+
+       rockchip_rk3399_pll_get_params(pll, &cur);
+
+       pr_debug("%s: pll %s@%lu: Hz\n", __func__, __clk_get_name(hw->clk),
+                drate);
+       pr_debug("old - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
+                cur.fbdiv, cur.postdiv1, cur.refdiv, cur.postdiv2,
+                cur.dsmpd, cur.frac);
+       pr_debug("new - fbdiv: %d, postdiv1: %d, refdiv: %d, postdiv2: %d, dsmpd: %d, frac: %d\n",
+                rate->fbdiv, rate->postdiv1, rate->refdiv, rate->postdiv2,
+                rate->dsmpd, rate->frac);
+
+       if (rate->fbdiv != cur.fbdiv || rate->postdiv1 != cur.postdiv1 ||
+               rate->refdiv != cur.refdiv || rate->postdiv2 != cur.postdiv2 ||
+               rate->dsmpd != cur.dsmpd || rate->frac != cur.frac) {
+               struct clk *parent = clk_get_parent(hw->clk);
+
+               if (!parent) {
+                       pr_warn("%s: parent of %s not available\n",
+                               __func__, __clk_get_name(hw->clk));
+                       return;
+               }
+
+               pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n",
+                        __func__, __clk_get_name(hw->clk));
+               rockchip_rk3399_pll_set_params(pll, rate);
+       }
+}
+
+static const struct clk_ops rockchip_rk3399_pll_clk_norate_ops = {
+       .recalc_rate = rockchip_rk3399_pll_recalc_rate,
+       .enable = rockchip_rk3399_pll_enable,
+       .disable = rockchip_rk3399_pll_disable,
+       .is_enabled = rockchip_rk3399_pll_is_enabled,
+};
+
+static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
+       .recalc_rate = rockchip_rk3399_pll_recalc_rate,
+       .round_rate = rockchip_pll_round_rate,
+       .set_rate = rockchip_rk3399_pll_set_rate,
+       .enable = rockchip_rk3399_pll_enable,
+       .disable = rockchip_rk3399_pll_disable,
+       .is_enabled = rockchip_rk3399_pll_is_enabled,
+       .init = rockchip_rk3399_pll_init,
+};
+
 /*
  * Common registering of pll clocks
  */
@@ -766,7 +1226,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
                u8 num_parents, int con_offset, int grf_lock_offset,
                int lock_shift, int mode_offset, int mode_shift,
                struct rockchip_pll_rate_table *rate_table,
-               u8 clk_pll_flags)
+               unsigned long flags, u8 clk_pll_flags)
 {
        const char *pll_parents[3];
        struct clk_init_data init;
@@ -775,7 +1235,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
        struct clk *pll_clk, *mux_clk;
        char pll_name[20];
 
-       if (num_parents != 2) {
+       if ((pll_type != pll_rk3328 && num_parents != 2) ||
+           (pll_type == pll_rk3328 && num_parents != 1)) {
                pr_err("%s: needs two parent clocks\n", __func__);
                return ERR_PTR(-EINVAL);
        }
@@ -792,13 +1253,19 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
        pll_mux = &pll->pll_mux;
        pll_mux->reg = ctx->reg_base + mode_offset;
        pll_mux->shift = mode_shift;
-       pll_mux->mask = PLL_MODE_MASK;
+       if (pll_type == pll_rk3328)
+               pll_mux->mask = PLL_RK3328_MODE_MASK;
+       else
+               pll_mux->mask = PLL_MODE_MASK;
        pll_mux->flags = 0;
        pll_mux->lock = &ctx->lock;
        pll_mux->hw.init = &init;
 
-       if (pll_type == pll_rk3036 || pll_type == pll_rk3066 ||
-               pll_type == pll_rk3366)
+       if (pll_type == pll_rk3036 ||
+           pll_type == pll_rk3066 ||
+           pll_type == pll_rk3328 ||
+           pll_type == pll_rk3366 ||
+           pll_type == pll_rk3399)
                pll_mux->flags |= CLK_MUX_HIWORD_MASK;
 
        /* the actual muxing is xin24m, pll-output, xin32k */
@@ -810,7 +1277,10 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
        init.flags = CLK_SET_RATE_PARENT;
        init.ops = pll->pll_mux_ops;
        init.parent_names = pll_parents;
-       init.num_parents = ARRAY_SIZE(pll_parents);
+       if (pll_type == pll_rk3328)
+               init.num_parents = 2;
+       else
+               init.num_parents = ARRAY_SIZE(pll_parents);
 
        mux_clk = clk_register(NULL, &pll_mux->hw);
        if (IS_ERR(mux_clk))
@@ -820,7 +1290,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
        init.name = pll_name;
 
        /* keep all plls untouched for now */
-       init.flags = CLK_IGNORE_UNUSED;
+       init.flags = flags | CLK_IGNORE_UNUSED;
 
        init.parent_names = &parent_names[0];
        init.num_parents = 1;
@@ -844,6 +1314,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 
        switch (pll_type) {
        case pll_rk3036:
+       case pll_rk3328:
                if (!pll->rate_table)
                        init.ops = &rockchip_rk3036_pll_clk_norate_ops;
                else
@@ -861,6 +1332,12 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
                else
                        init.ops = &rockchip_rk3366_pll_clk_ops;
                break;
+       case pll_rk3399:
+               if (!pll->rate_table)
+                       init.ops = &rockchip_rk3399_pll_clk_norate_ops;
+               else
+                       init.ops = &rockchip_rk3399_pll_clk_ops;
+               break;
        default:
                pr_warn("%s: Unknown pll type for pll clk %s\n",
                        __func__, name);