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