1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * OMAP4-specific DPLL control functions 4 * 5 * Copyright (C) 2011 Texas Instruments, Inc. 6 * Rajendra Nayak 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/errno.h> 11 #include <linux/clk.h> 12 #include <linux/io.h> 13 #include <linux/bitops.h> 14 #include <linux/clk/ti.h> 15 16 #include "clock.h" 17 18 /* 19 * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that 20 * can supported when using the DPLL low-power mode. Frequencies are 21 * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control, 22 * Status, and Low-Power Operation Mode". 23 */ 24 #define OMAP4_DPLL_LP_FINT_MAX 1000000 25 #define OMAP4_DPLL_LP_FOUT_MAX 100000000 26 27 /* 28 * Bitfield declarations 29 */ 30 #define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK BIT(8) 31 #define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK BIT(10) 32 #define OMAP4430_DPLL_REGM4XEN_MASK BIT(11) 33 34 /* Static rate multiplier for OMAP4 REGM4XEN clocks */ 35 #define OMAP4430_REGM4XEN_MULT 4 36 37 static void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk) 38 { 39 u32 v; 40 u32 mask; 41 42 if (!clk) 43 return; 44 45 mask = clk->flags & CLOCK_CLKOUTX2 ? 46 OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK : 47 OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK; 48 49 v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg); 50 /* Clear the bit to allow gatectrl */ 51 v &= ~mask; 52 ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg); 53 } 54 55 static void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk) 56 { 57 u32 v; 58 u32 mask; 59 60 if (!clk) 61 return; 62 63 mask = clk->flags & CLOCK_CLKOUTX2 ? 64 OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK : 65 OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK; 66 67 v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg); 68 /* Set the bit to deny gatectrl */ 69 v |= mask; 70 ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg); 71 } 72 73 const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = { 74 .allow_idle = omap4_dpllmx_allow_gatectrl, 75 .deny_idle = omap4_dpllmx_deny_gatectrl, 76 }; 77 78 /** 79 * omap4_dpll_lpmode_recalc - compute DPLL low-power setting 80 * @dd: pointer to the dpll data structure 81 * 82 * Calculates if low-power mode can be enabled based upon the last 83 * multiplier and divider values calculated. If low-power mode can be 84 * enabled, then the bit to enable low-power mode is stored in the 85 * last_rounded_lpmode variable. This implementation is based upon the 86 * criteria for enabling low-power mode as described in the OMAP4430/60 87 * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power 88 * Operation Mode". 89 */ 90 static void omap4_dpll_lpmode_recalc(struct dpll_data *dd) 91 { 92 long fint, fout; 93 94 fint = clk_hw_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1); 95 fout = fint * dd->last_rounded_m; 96 97 if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX)) 98 dd->last_rounded_lpmode = 1; 99 else 100 dd->last_rounded_lpmode = 0; 101 } 102 103 /** 104 * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit 105 * @hw: pointer to the clock to compute the rate for 106 * @parent_rate: clock rate of the DPLL parent 107 * 108 * Compute the output rate for the OMAP4 DPLL represented by @clk. 109 * Takes the REGM4XEN bit into consideration, which is needed for the 110 * OMAP4 ABE DPLL. Returns the DPLL's output rate (before M-dividers) 111 * upon success, or 0 upon error. 112 */ 113 unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, 114 unsigned long parent_rate) 115 { 116 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 117 u32 v; 118 unsigned long rate; 119 struct dpll_data *dd; 120 121 if (!clk || !clk->dpll_data) 122 return 0; 123 124 dd = clk->dpll_data; 125 126 rate = omap2_get_dpll_rate(clk); 127 128 /* regm4xen adds a multiplier of 4 to DPLL calculations */ 129 v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 130 if (v & OMAP4430_DPLL_REGM4XEN_MASK) 131 rate *= OMAP4430_REGM4XEN_MULT; 132 133 return rate; 134 } 135 136 /** 137 * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit 138 * @hw: struct hw_clk containing the struct clk * of the DPLL to round a rate for 139 * @target_rate: the desired rate of the DPLL 140 * @parent_rate: clock rate of the DPLL parent 141 * 142 * Compute the rate that would be programmed into the DPLL hardware 143 * for @clk if set_rate() were to be provided with the rate 144 * @target_rate. Takes the REGM4XEN bit into consideration, which is 145 * needed for the OMAP4 ABE DPLL. Returns the rounded rate (before 146 * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or 147 * ~0 if an error occurred in omap2_dpll_round_rate(). 148 */ 149 long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, 150 unsigned long target_rate, 151 unsigned long *parent_rate) 152 { 153 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 154 struct dpll_data *dd; 155 long r; 156 157 if (!clk || !clk->dpll_data) 158 return -EINVAL; 159 160 dd = clk->dpll_data; 161 162 dd->last_rounded_m4xen = 0; 163 164 /* 165 * First try to compute the DPLL configuration for 166 * target rate without using the 4X multiplier. 167 */ 168 r = omap2_dpll_round_rate(hw, target_rate, NULL); 169 if (r != ~0) 170 goto out; 171 172 /* 173 * If we did not find a valid DPLL configuration, try again, but 174 * this time see if using the 4X multiplier can help. Enabling the 175 * 4X multiplier is equivalent to dividing the target rate by 4. 176 */ 177 r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT, 178 NULL); 179 if (r == ~0) 180 return r; 181 182 dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; 183 dd->last_rounded_m4xen = 1; 184 185 out: 186 omap4_dpll_lpmode_recalc(dd); 187 188 return dd->last_rounded_rate; 189 } 190 191 /** 192 * omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL 193 * @hw: pointer to the clock to determine rate for 194 * @req: target rate request 195 * 196 * Determines which DPLL mode to use for reaching a desired rate. 197 * Checks whether the DPLL shall be in bypass or locked mode, and if 198 * locked, calculates the M,N values for the DPLL via round-rate. 199 * Returns 0 on success and a negative error value otherwise. 200 */ 201 int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, 202 struct clk_rate_request *req) 203 { 204 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 205 struct dpll_data *dd; 206 207 if (!req->rate) 208 return -EINVAL; 209 210 dd = clk->dpll_data; 211 if (!dd) 212 return -EINVAL; 213 214 if (clk_hw_get_rate(dd->clk_bypass) == req->rate && 215 (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { 216 req->best_parent_hw = dd->clk_bypass; 217 } else { 218 req->rate = omap4_dpll_regm4xen_round_rate(hw, req->rate, 219 &req->best_parent_rate); 220 req->best_parent_hw = dd->clk_ref; 221 } 222 223 req->best_parent_rate = req->rate; 224 225 return 0; 226 } 227