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_determine_rate - determine rate for a DPLL 138 * @hw: pointer to the clock to determine rate for 139 * @req: target rate request 140 * 141 * Determines which DPLL mode to use for reaching a desired rate. 142 * Checks whether the DPLL shall be in bypass or locked mode, and if 143 * locked, calculates the M,N values for the DPLL. 144 * Returns 0 on success and a negative error value otherwise. 145 */ 146 int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, 147 struct clk_rate_request *req) 148 { 149 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 150 struct dpll_data *dd; 151 152 if (!req->rate) 153 return -EINVAL; 154 155 dd = clk->dpll_data; 156 if (!dd) 157 return -EINVAL; 158 159 if (clk_hw_get_rate(dd->clk_bypass) == req->rate && 160 (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { 161 req->best_parent_hw = dd->clk_bypass; 162 } else { 163 struct clk_rate_request tmp_req; 164 long r; 165 166 clk_hw_init_rate_request(hw, &tmp_req, req->rate); 167 dd->last_rounded_m4xen = 0; 168 169 /* 170 * First try to compute the DPLL configuration for 171 * target rate without using the 4X multiplier. 172 */ 173 174 r = omap2_dpll_determine_rate(hw, &tmp_req); 175 if (r < 0) { 176 /* 177 * If we did not find a valid DPLL configuration, try again, but 178 * this time see if using the 4X multiplier can help. Enabling the 179 * 4X multiplier is equivalent to dividing the target rate by 4. 180 */ 181 tmp_req.rate /= OMAP4430_REGM4XEN_MULT; 182 r = omap2_dpll_determine_rate(hw, &tmp_req); 183 if (r < 0) 184 return r; 185 186 dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; 187 dd->last_rounded_m4xen = 1; 188 } 189 190 omap4_dpll_lpmode_recalc(dd); 191 192 req->rate = dd->last_rounded_rate; 193 req->best_parent_hw = dd->clk_ref; 194 } 195 196 req->best_parent_rate = req->rate; 197 198 return 0; 199 } 200