diff options
| -rw-r--r-- | drivers/clk/imx/clk-pllv4.c | 72 |
1 files changed, 63 insertions, 9 deletions
diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c index d38bc9f87c1d..d7e62c3620d3 100644 --- a/drivers/clk/imx/clk-pllv4.c +++ b/drivers/clk/imx/clk-pllv4.c | |||
| @@ -30,6 +30,9 @@ | |||
| 30 | /* PLL Denominator Register (xPLLDENOM) */ | 30 | /* PLL Denominator Register (xPLLDENOM) */ |
| 31 | #define PLL_DENOM_OFFSET 0x14 | 31 | #define PLL_DENOM_OFFSET 0x14 |
| 32 | 32 | ||
| 33 | #define MAX_MFD 0x3fffffff | ||
| 34 | #define DEFAULT_MFD 1000000 | ||
| 35 | |||
| 33 | struct clk_pllv4 { | 36 | struct clk_pllv4 { |
| 34 | struct clk_hw hw; | 37 | struct clk_hw hw; |
| 35 | void __iomem *base; | 38 | void __iomem *base; |
| @@ -64,13 +67,20 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw, | |||
| 64 | unsigned long parent_rate) | 67 | unsigned long parent_rate) |
| 65 | { | 68 | { |
| 66 | struct clk_pllv4 *pll = to_clk_pllv4(hw); | 69 | struct clk_pllv4 *pll = to_clk_pllv4(hw); |
| 67 | u32 div; | 70 | u32 mult, mfn, mfd; |
| 71 | u64 temp64; | ||
| 72 | |||
| 73 | mult = readl_relaxed(pll->base + PLL_CFG_OFFSET); | ||
| 74 | mult &= BM_PLL_MULT; | ||
| 75 | mult >>= BP_PLL_MULT; | ||
| 68 | 76 | ||
| 69 | div = readl_relaxed(pll->base + PLL_CFG_OFFSET); | 77 | mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET); |
| 70 | div &= BM_PLL_MULT; | 78 | mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET); |
| 71 | div >>= BP_PLL_MULT; | 79 | temp64 = parent_rate; |
| 80 | temp64 *= mfn; | ||
| 81 | do_div(temp64, mfd); | ||
| 72 | 82 | ||
| 73 | return parent_rate * div; | 83 | return (parent_rate * mult) + (u32)temp64; |
| 74 | } | 84 | } |
| 75 | 85 | ||
| 76 | static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, | 86 | static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, |
| @@ -78,14 +88,46 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, | |||
| 78 | { | 88 | { |
| 79 | unsigned long parent_rate = *prate; | 89 | unsigned long parent_rate = *prate; |
| 80 | unsigned long round_rate, i; | 90 | unsigned long round_rate, i; |
| 91 | u32 mfn, mfd = DEFAULT_MFD; | ||
| 92 | bool found = false; | ||
| 93 | u64 temp64; | ||
| 81 | 94 | ||
| 82 | for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { | 95 | for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { |
| 83 | round_rate = parent_rate * pllv4_mult_table[i]; | 96 | round_rate = parent_rate * pllv4_mult_table[i]; |
| 84 | if (rate >= round_rate) | 97 | if (rate >= round_rate) { |
| 85 | return round_rate; | 98 | found = true; |
| 99 | break; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | if (!found) { | ||
| 104 | pr_warn("%s: unable to round rate %lu, parent rate %lu\n", | ||
| 105 | clk_hw_get_name(hw), rate, parent_rate); | ||
| 106 | return 0; | ||
| 86 | } | 107 | } |
| 87 | 108 | ||
| 88 | return round_rate; | 109 | if (parent_rate <= MAX_MFD) |
| 110 | mfd = parent_rate; | ||
| 111 | |||
| 112 | temp64 = (u64)(rate - round_rate); | ||
| 113 | temp64 *= mfd; | ||
| 114 | do_div(temp64, parent_rate); | ||
| 115 | mfn = temp64; | ||
| 116 | |||
| 117 | /* | ||
| 118 | * NOTE: The value of numerator must always be configured to be | ||
| 119 | * less than the value of the denominator. If we can't get a proper | ||
| 120 | * pair of mfn/mfd, we simply return the round_rate without using | ||
| 121 | * the frac part. | ||
| 122 | */ | ||
| 123 | if (mfn >= mfd) | ||
| 124 | return round_rate; | ||
| 125 | |||
| 126 | temp64 = (u64)parent_rate; | ||
| 127 | temp64 *= mfn; | ||
| 128 | do_div(temp64, mfd); | ||
| 129 | |||
| 130 | return round_rate + (u32)temp64; | ||
| 89 | } | 131 | } |
| 90 | 132 | ||
| 91 | static bool clk_pllv4_is_valid_mult(unsigned int mult) | 133 | static bool clk_pllv4_is_valid_mult(unsigned int mult) |
| @@ -105,18 +147,30 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate, | |||
| 105 | unsigned long parent_rate) | 147 | unsigned long parent_rate) |
| 106 | { | 148 | { |
| 107 | struct clk_pllv4 *pll = to_clk_pllv4(hw); | 149 | struct clk_pllv4 *pll = to_clk_pllv4(hw); |
| 108 | u32 val, mult; | 150 | u32 val, mult, mfn, mfd = DEFAULT_MFD; |
| 151 | u64 temp64; | ||
| 109 | 152 | ||
| 110 | mult = rate / parent_rate; | 153 | mult = rate / parent_rate; |
| 111 | 154 | ||
| 112 | if (!clk_pllv4_is_valid_mult(mult)) | 155 | if (!clk_pllv4_is_valid_mult(mult)) |
| 113 | return -EINVAL; | 156 | return -EINVAL; |
| 114 | 157 | ||
| 158 | if (parent_rate <= MAX_MFD) | ||
| 159 | mfd = parent_rate; | ||
| 160 | |||
| 161 | temp64 = (u64)(rate - mult * parent_rate); | ||
| 162 | temp64 *= mfd; | ||
| 163 | do_div(temp64, parent_rate); | ||
| 164 | mfn = temp64; | ||
| 165 | |||
| 115 | val = readl_relaxed(pll->base + PLL_CFG_OFFSET); | 166 | val = readl_relaxed(pll->base + PLL_CFG_OFFSET); |
| 116 | val &= ~BM_PLL_MULT; | 167 | val &= ~BM_PLL_MULT; |
| 117 | val |= mult << BP_PLL_MULT; | 168 | val |= mult << BP_PLL_MULT; |
| 118 | writel_relaxed(val, pll->base + PLL_CFG_OFFSET); | 169 | writel_relaxed(val, pll->base + PLL_CFG_OFFSET); |
| 119 | 170 | ||
| 171 | writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET); | ||
| 172 | writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET); | ||
| 173 | |||
| 120 | return 0; | 174 | return 0; |
| 121 | } | 175 | } |
| 122 | 176 | ||
