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 * @clk: struct clk * of the DPLL to compute the rate for 106 * 107 * Compute the output rate for the OMAP4 DPLL represented by @clk. 108 * Takes the REGM4XEN bit into consideration, which is needed for the 109 * OMAP4 ABE DPLL. Returns the DPLL's output rate (before M-dividers) 110 * upon success, or 0 upon error. 111 */ 112 unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, 113 unsigned long parent_rate) 114 { 115 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 116 u32 v; 117 unsigned long rate; 118 struct dpll_data *dd; 119 120 if (!clk || !clk->dpll_data) 121 return 0; 122 123 dd = clk->dpll_data; 124 125 rate = omap2_get_dpll_rate(clk); 126 127 /* regm4xen adds a multiplier of 4 to DPLL calculations */ 128 v = ti_clk_ll_ops->clk_readl(&dd->control_reg); 129 if (v & OMAP4430_DPLL_REGM4XEN_MASK) 130 rate *= OMAP4430_REGM4XEN_MULT; 131 132 return rate; 133 } 134 135 /** 136 * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit 137 * @clk: struct clk * of the DPLL to round a rate for 138 * @target_rate: the desired rate of the DPLL 139 * 140 * Compute the rate that would be programmed into the DPLL hardware 141 * for @clk if set_rate() were to be provided with the rate 142 * @target_rate. Takes the REGM4XEN bit into consideration, which is 143 * needed for the OMAP4 ABE DPLL. Returns the rounded rate (before 144 * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or 145 * ~0 if an error occurred in omap2_dpll_round_rate(). 146 */ 147 long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, 148 unsigned long target_rate, 149 unsigned long *parent_rate) 150 { 151 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 152 struct dpll_data *dd; 153 long r; 154 155 if (!clk || !clk->dpll_data) 156 return -EINVAL; 157 158 dd = clk->dpll_data; 159 160 dd->last_rounded_m4xen = 0; 161 162 /* 163 * First try to compute the DPLL configuration for 164 * target rate without using the 4X multiplier. 165 */ 166 r = omap2_dpll_round_rate(hw, target_rate, NULL); 167 if (r != ~0) 168 goto out; 169 170 /* 171 * If we did not find a valid DPLL configuration, try again, but 172 * this time see if using the 4X multiplier can help. Enabling the 173 * 4X multiplier is equivalent to dividing the target rate by 4. 174 */ 175 r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT, 176 NULL); 177 if (r == ~0) 178 return r; 179 180 dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; 181 dd->last_rounded_m4xen = 1; 182 183 out: 184 omap4_dpll_lpmode_recalc(dd); 185 186 return dd->last_rounded_rate; 187 } 188 189 /** 190 * omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL 191 * @hw: pointer to the clock to determine rate for 192 * @req: target rate request 193 * 194 * Determines which DPLL mode to use for reaching a desired rate. 195 * Checks whether the DPLL shall be in bypass or locked mode, and if 196 * locked, calculates the M,N values for the DPLL via round-rate. 197 * Returns 0 on success and a negative error value otherwise. 198 */ 199 int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, 200 struct clk_rate_request *req) 201 { 202 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 203 struct dpll_data *dd; 204 205 if (!req->rate) 206 return -EINVAL; 207 208 dd = clk->dpll_data; 209 if (!dd) 210 return -EINVAL; 211 212 if (clk_hw_get_rate(dd->clk_bypass) == req->rate && 213 (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { 214 req->best_parent_hw = dd->clk_bypass; 215 } else { 216 req->rate = omap4_dpll_regm4xen_round_rate(hw, req->rate, 217 &req->best_parent_rate); 218 req->best_parent_hw = dd->clk_ref; 219 } 220 221 req->best_parent_rate = req->rate; 222 223 return 0; 224 } 225