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