xref: /openbmc/linux/drivers/clk/ti/dpll44xx.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
259245ce0STero Kristo /*
359245ce0STero Kristo  * OMAP4-specific DPLL control functions
459245ce0STero Kristo  *
559245ce0STero Kristo  * Copyright (C) 2011 Texas Instruments, Inc.
659245ce0STero Kristo  * Rajendra Nayak
759245ce0STero Kristo  */
859245ce0STero Kristo 
959245ce0STero Kristo #include <linux/kernel.h>
1059245ce0STero Kristo #include <linux/errno.h>
1159245ce0STero Kristo #include <linux/clk.h>
1259245ce0STero Kristo #include <linux/io.h>
1359245ce0STero Kristo #include <linux/bitops.h>
1459245ce0STero Kristo #include <linux/clk/ti.h>
1559245ce0STero Kristo 
1659245ce0STero Kristo #include "clock.h"
1759245ce0STero Kristo 
1859245ce0STero Kristo /*
1959245ce0STero Kristo  * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that
2059245ce0STero Kristo  * can supported when using the DPLL low-power mode. Frequencies are
2159245ce0STero Kristo  * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control,
2259245ce0STero Kristo  * Status, and Low-Power Operation Mode".
2359245ce0STero Kristo  */
2459245ce0STero Kristo #define OMAP4_DPLL_LP_FINT_MAX	1000000
2559245ce0STero Kristo #define OMAP4_DPLL_LP_FOUT_MAX	100000000
2659245ce0STero Kristo 
2759245ce0STero Kristo /*
2859245ce0STero Kristo  * Bitfield declarations
2959245ce0STero Kristo  */
3059245ce0STero Kristo #define OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK		BIT(8)
3159245ce0STero Kristo #define OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK		BIT(10)
3259245ce0STero Kristo #define OMAP4430_DPLL_REGM4XEN_MASK			BIT(11)
3359245ce0STero Kristo 
3459245ce0STero Kristo /* Static rate multiplier for OMAP4 REGM4XEN clocks */
3559245ce0STero Kristo #define OMAP4430_REGM4XEN_MULT				4
3659245ce0STero Kristo 
omap4_dpllmx_allow_gatectrl(struct clk_hw_omap * clk)3759245ce0STero Kristo static void omap4_dpllmx_allow_gatectrl(struct clk_hw_omap *clk)
3859245ce0STero Kristo {
3959245ce0STero Kristo 	u32 v;
4059245ce0STero Kristo 	u32 mask;
4159245ce0STero Kristo 
426c0afb50STero Kristo 	if (!clk)
4359245ce0STero Kristo 		return;
4459245ce0STero Kristo 
4559245ce0STero Kristo 	mask = clk->flags & CLOCK_CLKOUTX2 ?
4659245ce0STero Kristo 			OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
4759245ce0STero Kristo 			OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
4859245ce0STero Kristo 
496c0afb50STero Kristo 	v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);
5059245ce0STero Kristo 	/* Clear the bit to allow gatectrl */
5159245ce0STero Kristo 	v &= ~mask;
526c0afb50STero Kristo 	ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);
5359245ce0STero Kristo }
5459245ce0STero Kristo 
omap4_dpllmx_deny_gatectrl(struct clk_hw_omap * clk)5559245ce0STero Kristo static void omap4_dpllmx_deny_gatectrl(struct clk_hw_omap *clk)
5659245ce0STero Kristo {
5759245ce0STero Kristo 	u32 v;
5859245ce0STero Kristo 	u32 mask;
5959245ce0STero Kristo 
606c0afb50STero Kristo 	if (!clk)
6159245ce0STero Kristo 		return;
6259245ce0STero Kristo 
6359245ce0STero Kristo 	mask = clk->flags & CLOCK_CLKOUTX2 ?
6459245ce0STero Kristo 			OMAP4430_DPLL_CLKOUTX2_GATE_CTRL_MASK :
6559245ce0STero Kristo 			OMAP4430_DPLL_CLKOUT_GATE_CTRL_MASK;
6659245ce0STero Kristo 
676c0afb50STero Kristo 	v = ti_clk_ll_ops->clk_readl(&clk->clksel_reg);
6859245ce0STero Kristo 	/* Set the bit to deny gatectrl */
6959245ce0STero Kristo 	v |= mask;
706c0afb50STero Kristo 	ti_clk_ll_ops->clk_writel(v, &clk->clksel_reg);
7159245ce0STero Kristo }
7259245ce0STero Kristo 
7359245ce0STero Kristo const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {
7459245ce0STero Kristo 	.allow_idle	= omap4_dpllmx_allow_gatectrl,
7559245ce0STero Kristo 	.deny_idle      = omap4_dpllmx_deny_gatectrl,
7659245ce0STero Kristo };
7759245ce0STero Kristo 
7859245ce0STero Kristo /**
7959245ce0STero Kristo  * omap4_dpll_lpmode_recalc - compute DPLL low-power setting
8059245ce0STero Kristo  * @dd: pointer to the dpll data structure
8159245ce0STero Kristo  *
8259245ce0STero Kristo  * Calculates if low-power mode can be enabled based upon the last
8359245ce0STero Kristo  * multiplier and divider values calculated. If low-power mode can be
8459245ce0STero Kristo  * enabled, then the bit to enable low-power mode is stored in the
8559245ce0STero Kristo  * last_rounded_lpmode variable. This implementation is based upon the
8659245ce0STero Kristo  * criteria for enabling low-power mode as described in the OMAP4430/60
8759245ce0STero Kristo  * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power
8859245ce0STero Kristo  * Operation Mode".
8959245ce0STero Kristo  */
omap4_dpll_lpmode_recalc(struct dpll_data * dd)9059245ce0STero Kristo static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
9159245ce0STero Kristo {
9259245ce0STero Kristo 	long fint, fout;
9359245ce0STero Kristo 
94b6f51284STero Kristo 	fint = clk_hw_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1);
9559245ce0STero Kristo 	fout = fint * dd->last_rounded_m;
9659245ce0STero Kristo 
9759245ce0STero Kristo 	if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX))
9859245ce0STero Kristo 		dd->last_rounded_lpmode = 1;
9959245ce0STero Kristo 	else
10059245ce0STero Kristo 		dd->last_rounded_lpmode = 0;
10159245ce0STero Kristo }
10259245ce0STero Kristo 
10359245ce0STero Kristo /**
10459245ce0STero Kristo  * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit
105*d8dbf923SLee Jones  * @hw: pointer to the clock to compute the rate for
106*d8dbf923SLee Jones  * @parent_rate: clock rate of the DPLL parent
10759245ce0STero Kristo  *
10859245ce0STero Kristo  * Compute the output rate for the OMAP4 DPLL represented by @clk.
10959245ce0STero Kristo  * Takes the REGM4XEN bit into consideration, which is needed for the
11059245ce0STero Kristo  * OMAP4 ABE DPLL.  Returns the DPLL's output rate (before M-dividers)
11159245ce0STero Kristo  * upon success, or 0 upon error.
11259245ce0STero Kristo  */
omap4_dpll_regm4xen_recalc(struct clk_hw * hw,unsigned long parent_rate)11359245ce0STero Kristo unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
11459245ce0STero Kristo 					 unsigned long parent_rate)
11559245ce0STero Kristo {
11659245ce0STero Kristo 	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
11759245ce0STero Kristo 	u32 v;
11859245ce0STero Kristo 	unsigned long rate;
11959245ce0STero Kristo 	struct dpll_data *dd;
12059245ce0STero Kristo 
12159245ce0STero Kristo 	if (!clk || !clk->dpll_data)
12259245ce0STero Kristo 		return 0;
12359245ce0STero Kristo 
12459245ce0STero Kristo 	dd = clk->dpll_data;
12559245ce0STero Kristo 
12659245ce0STero Kristo 	rate = omap2_get_dpll_rate(clk);
12759245ce0STero Kristo 
12859245ce0STero Kristo 	/* regm4xen adds a multiplier of 4 to DPLL calculations */
1296c0afb50STero Kristo 	v = ti_clk_ll_ops->clk_readl(&dd->control_reg);
13059245ce0STero Kristo 	if (v & OMAP4430_DPLL_REGM4XEN_MASK)
13159245ce0STero Kristo 		rate *= OMAP4430_REGM4XEN_MULT;
13259245ce0STero Kristo 
13359245ce0STero Kristo 	return rate;
13459245ce0STero Kristo }
13559245ce0STero Kristo 
13659245ce0STero Kristo /**
13759245ce0STero Kristo  * omap4_dpll_regm4xen_round_rate - round DPLL rate, considering REGM4XEN bit
138*d8dbf923SLee Jones  * @hw: struct hw_clk containing the struct clk * of the DPLL to round a rate for
13959245ce0STero Kristo  * @target_rate: the desired rate of the DPLL
140*d8dbf923SLee Jones  * @parent_rate: clock rate of the DPLL parent
14159245ce0STero Kristo  *
14259245ce0STero Kristo  * Compute the rate that would be programmed into the DPLL hardware
14359245ce0STero Kristo  * for @clk if set_rate() were to be provided with the rate
14459245ce0STero Kristo  * @target_rate.  Takes the REGM4XEN bit into consideration, which is
14559245ce0STero Kristo  * needed for the OMAP4 ABE DPLL.  Returns the rounded rate (before
14659245ce0STero Kristo  * M-dividers) upon success, -EINVAL if @clk is null or not a DPLL, or
14759245ce0STero Kristo  * ~0 if an error occurred in omap2_dpll_round_rate().
14859245ce0STero Kristo  */
omap4_dpll_regm4xen_round_rate(struct clk_hw * hw,unsigned long target_rate,unsigned long * parent_rate)14959245ce0STero Kristo long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
15059245ce0STero Kristo 				    unsigned long target_rate,
15159245ce0STero Kristo 				    unsigned long *parent_rate)
15259245ce0STero Kristo {
15359245ce0STero Kristo 	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
15459245ce0STero Kristo 	struct dpll_data *dd;
15559245ce0STero Kristo 	long r;
15659245ce0STero Kristo 
15759245ce0STero Kristo 	if (!clk || !clk->dpll_data)
15859245ce0STero Kristo 		return -EINVAL;
15959245ce0STero Kristo 
16059245ce0STero Kristo 	dd = clk->dpll_data;
16159245ce0STero Kristo 
16259245ce0STero Kristo 	dd->last_rounded_m4xen = 0;
16359245ce0STero Kristo 
16459245ce0STero Kristo 	/*
16559245ce0STero Kristo 	 * First try to compute the DPLL configuration for
16659245ce0STero Kristo 	 * target rate without using the 4X multiplier.
16759245ce0STero Kristo 	 */
16859245ce0STero Kristo 	r = omap2_dpll_round_rate(hw, target_rate, NULL);
16959245ce0STero Kristo 	if (r != ~0)
17059245ce0STero Kristo 		goto out;
17159245ce0STero Kristo 
17259245ce0STero Kristo 	/*
17359245ce0STero Kristo 	 * If we did not find a valid DPLL configuration, try again, but
17459245ce0STero Kristo 	 * this time see if using the 4X multiplier can help. Enabling the
17559245ce0STero Kristo 	 * 4X multiplier is equivalent to dividing the target rate by 4.
17659245ce0STero Kristo 	 */
17759245ce0STero Kristo 	r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT,
17859245ce0STero Kristo 				  NULL);
17959245ce0STero Kristo 	if (r == ~0)
18059245ce0STero Kristo 		return r;
18159245ce0STero Kristo 
18259245ce0STero Kristo 	dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
18359245ce0STero Kristo 	dd->last_rounded_m4xen = 1;
18459245ce0STero Kristo 
18559245ce0STero Kristo out:
18659245ce0STero Kristo 	omap4_dpll_lpmode_recalc(dd);
18759245ce0STero Kristo 
18859245ce0STero Kristo 	return dd->last_rounded_rate;
18959245ce0STero Kristo }
19059245ce0STero Kristo 
19159245ce0STero Kristo /**
19259245ce0STero Kristo  * omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL
19359245ce0STero Kristo  * @hw: pointer to the clock to determine rate for
1944d341056SStephen Boyd  * @req: target rate request
19559245ce0STero Kristo  *
19659245ce0STero Kristo  * Determines which DPLL mode to use for reaching a desired rate.
19759245ce0STero Kristo  * Checks whether the DPLL shall be in bypass or locked mode, and if
19859245ce0STero Kristo  * locked, calculates the M,N values for the DPLL via round-rate.
1994d341056SStephen Boyd  * Returns 0 on success and a negative error value otherwise.
20059245ce0STero Kristo  */
omap4_dpll_regm4xen_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)2014d341056SStephen Boyd int omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
2024d341056SStephen Boyd 				       struct clk_rate_request *req)
20359245ce0STero Kristo {
20459245ce0STero Kristo 	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
20559245ce0STero Kristo 	struct dpll_data *dd;
20659245ce0STero Kristo 
2074d341056SStephen Boyd 	if (!req->rate)
20859245ce0STero Kristo 		return -EINVAL;
20959245ce0STero Kristo 
21059245ce0STero Kristo 	dd = clk->dpll_data;
21159245ce0STero Kristo 	if (!dd)
21259245ce0STero Kristo 		return -EINVAL;
21359245ce0STero Kristo 
214b6f51284STero Kristo 	if (clk_hw_get_rate(dd->clk_bypass) == req->rate &&
21559245ce0STero Kristo 	    (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
216b6f51284STero Kristo 		req->best_parent_hw = dd->clk_bypass;
21759245ce0STero Kristo 	} else {
2184d341056SStephen Boyd 		req->rate = omap4_dpll_regm4xen_round_rate(hw, req->rate,
2194d341056SStephen Boyd 						&req->best_parent_rate);
220b6f51284STero Kristo 		req->best_parent_hw = dd->clk_ref;
22159245ce0STero Kristo 	}
22259245ce0STero Kristo 
2234d341056SStephen Boyd 	req->best_parent_rate = req->rate;
22459245ce0STero Kristo 
2254d341056SStephen Boyd 	return 0;
22659245ce0STero Kristo }
227