| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 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) | 
 | 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) | 
 | 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_hw_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_hw_get_rate(dd->clk_bypass) == req->rate && | 
 | 216 | 	    (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { | 
 | 217 | 		req->best_parent_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 = dd->clk_ref; | 
 | 222 | 	} | 
 | 223 |  | 
 | 224 | 	req->best_parent_rate = req->rate; | 
 | 225 |  | 
 | 226 | 	return 0; | 
 | 227 | } |