mirror of
				https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
				synced 2025-10-30 23:58:51 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			864 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			864 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "../pwrcal.h"
 | |
| #include "../pwrcal-clk.h"
 | |
| #include "../pwrcal-env.h"
 | |
| #include "../pwrcal-rae.h"
 | |
| #include "../pwrcal-pmu.h"
 | |
| #include "S5E7570-cmusfr.h"
 | |
| #include "S5E7570-pmusfr.h"
 | |
| #include "S5E7570-cmu.h"
 | |
| 
 | |
| /* PLLs */
 | |
| /* PLL141XX Clock Type */
 | |
| #define PLL141XX_MDIV_SHIFT		16
 | |
| #define PLL141XX_PDIV_SHIFT		8
 | |
| #define PLL141XX_SDIV_SHIFT		0
 | |
| #define PLL141XX_MDIV_MASK		0x3FF
 | |
| #define PLL141XX_PDIV_MASK		0x3F
 | |
| #define PLL141XX_SDIV_MASK		0x7
 | |
| #define PLL141XX_ENABLE			31
 | |
| #define PLL141XX_LOCKED			29
 | |
| #define PLL141XX_BYPASS			22
 | |
| 
 | |
| /* PLL1431X Clock Type */
 | |
| #define PLL1431X_MDIV_SHIFT		16
 | |
| #define PLL1431X_PDIV_SHIFT		8
 | |
| #define PLL1431X_SDIV_SHIFT		0
 | |
| #define PLL1431X_K_SHIFT		0
 | |
| #define PLL1431X_MDIV_MASK		0x3FF
 | |
| #define PLL1431X_PDIV_MASK		0x3F
 | |
| #define PLL1431X_SDIV_MASK		0x7
 | |
| #define PLL1431X_K_MASK		0xFFFF
 | |
| #define PLL1431X_ENABLE			31
 | |
| #define PLL1431X_LOCKED			29
 | |
| #define PLL1431X_BYPASS			4
 | |
| 
 | |
| /* WPLL_USB_PLL Clock Type */
 | |
| #define WIFI2AP_USBPLL_ACK	1
 | |
| #define AP2WIFI_USBPLL_REQ	0
 | |
| #define AP2WLBT_SR7 ((void *)(0x1058009C))
 | |
| 
 | |
| #define USBPLL_WPLL_FREF_SEL	3
 | |
| #define USBPLL_WPLL_AFC_START	2
 | |
| #define USBPLL_WPLL_EN	1
 | |
| #define USBPLL_WPLL_SEL	0
 | |
| 
 | |
| #define FIN_HZ_26M		(26*MHZ)
 | |
| 
 | |
| static const struct pwrcal_pll_rate_table *_clk_get_pll_settings(
 | |
| 					struct pwrcal_pll *pll_clk,
 | |
| 					unsigned long long rate)
 | |
| {
 | |
| 	int i;
 | |
| 	const struct pwrcal_pll_rate_table  *prate_table = pll_clk->rate_table;
 | |
| 
 | |
| 	for (i = 0; i < pll_clk->rate_count; i++) {
 | |
| 		if (rate == prate_table[i].rate)
 | |
| 			return &prate_table[i];
 | |
| 	}
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int _clk_pll141xx_find_pms(struct pll_spec *pll_spec,
 | |
| 				struct pwrcal_pll_rate_table *rate_table,
 | |
| 				unsigned long long rate)
 | |
| {
 | |
| 	unsigned int p, m, s;
 | |
| 	unsigned long long fref, fvco, fout;
 | |
| 	unsigned long long tmprate, tmpfout = 0;
 | |
| 	unsigned long long mindiffrate = 0xFFFFFFFFFFFFFFFF;
 | |
| 	unsigned int min_p = 0, min_m = 0, min_s = 0, min_fout = 0;
 | |
| 
 | |
| 	for (p = pll_spec->pdiv_min; p <= pll_spec->pdiv_max; p++) {
 | |
| 		fref = FIN_HZ_26M / p;
 | |
| 		if ((fref < pll_spec->fref_min) || (fref > pll_spec->fref_max))
 | |
| 			continue;
 | |
| 
 | |
| 		for (s = pll_spec->sdiv_min; s <= pll_spec->sdiv_max; s++) {
 | |
| 			tmprate = rate;
 | |
| 			do_div(tmprate, KHZ);
 | |
| 			tmprate = tmprate * p * (1 << s);
 | |
| 			do_div(tmprate, (FIN_HZ_26M / KHZ));
 | |
| 			m = (unsigned int)tmprate;
 | |
| 
 | |
| 			if ((m < pll_spec->mdiv_min)
 | |
| 					|| (m > pll_spec->mdiv_max))
 | |
| 				continue;
 | |
| 
 | |
| 			fvco = ((unsigned long long)FIN_HZ_26M) * m;
 | |
| 			do_div(fvco, p);
 | |
| 			if ((fvco < pll_spec->fvco_min)
 | |
| 					|| (fvco > pll_spec->fvco_max))
 | |
| 				continue;
 | |
| 
 | |
| 			fout = fvco >> s;
 | |
| 			if ((fout >= pll_spec->fout_min)
 | |
| 					&& (fout <= pll_spec->fout_max)) {
 | |
| 				tmprate = rate;
 | |
| 				do_div(tmprate, KHZ);
 | |
| 				tmpfout = fout;
 | |
| 				do_div(tmpfout, KHZ);
 | |
| 				if (tmprate == tmpfout) {
 | |
| 					rate_table->rate = fout;
 | |
| 					rate_table->pdiv = p;
 | |
| 					rate_table->mdiv = m;
 | |
| 					rate_table->sdiv = s;
 | |
| 					rate_table->kdiv = 0;
 | |
| 					return 0;
 | |
| 				}
 | |
| 				if (tmpfout < tmprate && mindiffrate > tmprate - tmpfout) {
 | |
| 					mindiffrate = tmprate - tmpfout;
 | |
| 					min_fout = fout;
 | |
| 					min_p = p;
 | |
| 					min_m = m;
 | |
| 					min_s = s;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (mindiffrate != 0xFFFFFFFFFFFFFFFF) {
 | |
| 		rate_table->rate = min_fout;
 | |
| 		rate_table->pdiv = min_p;
 | |
| 		rate_table->mdiv = min_m;
 | |
| 		rate_table->sdiv = min_s;
 | |
| 		rate_table->kdiv = 0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int _clk_pll1419x_find_pms(struct pll_spec *pll_spec,
 | |
| 				struct pwrcal_pll_rate_table *rate_table,
 | |
| 				unsigned long long rate)
 | |
| {
 | |
| 	unsigned int p, m, s;
 | |
| 	unsigned long long fref, fvco, fout;
 | |
| 	unsigned long long tmprate, tmpfout = 0;
 | |
| 	unsigned long long mindiffrate = 0xFFFFFFFFFFFFFFFF;
 | |
| 	unsigned int min_p = 0, min_m = 0, min_s = 0, min_fout = 0;
 | |
| 
 | |
| 	for (p = pll_spec->pdiv_min; p <= pll_spec->pdiv_max; p++) {
 | |
| 		fref = FIN_HZ_26M / p;
 | |
| 		if ((fref < pll_spec->fref_min) || (fref > pll_spec->fref_max))
 | |
| 			continue;
 | |
| 
 | |
| 		for (s = pll_spec->sdiv_min; s <= pll_spec->sdiv_max; s++) {
 | |
| 			/*tmprate = rate;*/
 | |
| 			tmprate = rate/2; /*for PLL1419*/
 | |
| 			do_div(tmprate, KHZ);
 | |
| 			tmprate = tmprate * p * (1 << s);
 | |
| 			do_div(tmprate, (FIN_HZ_26M / KHZ));
 | |
| 			m = (unsigned int)tmprate;
 | |
| 
 | |
| 			if ((m < pll_spec->mdiv_min)
 | |
| 					|| (m > pll_spec->mdiv_max))
 | |
| 				continue;
 | |
| 
 | |
| 			fvco = ((unsigned long long)FIN_HZ_26M) * m;
 | |
| 			do_div(fvco, p);
 | |
| 			if ((fvco < pll_spec->fvco_min)
 | |
| 					|| (fvco > pll_spec->fvco_max))
 | |
| 				continue;
 | |
| 
 | |
| 			fout = fvco >> s;
 | |
| 			if ((fout >= pll_spec->fout_min)
 | |
| 					&& (fout <= pll_spec->fout_max)) {
 | |
| 				tmprate = rate;
 | |
| 				do_div(tmprate, KHZ);
 | |
| 				tmpfout = fout;
 | |
| 				do_div(tmpfout, KHZ);
 | |
| 				if (tmprate == tmpfout) {
 | |
| 					rate_table->rate = fout;
 | |
| 					rate_table->pdiv = p;
 | |
| 					rate_table->mdiv = m;
 | |
| 					rate_table->sdiv = s;
 | |
| 					rate_table->kdiv = 0;
 | |
| 					return 0;
 | |
| 				}
 | |
| 			}
 | |
| 			if (tmpfout < tmprate && mindiffrate > tmprate - tmpfout) {
 | |
| 				mindiffrate = tmprate - tmpfout;
 | |
| 				min_fout = fout;
 | |
| 				min_p = p;
 | |
| 				min_m = m;
 | |
| 				min_s = s;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (mindiffrate != 0xFFFFFFFFFFFFFFFF) {
 | |
| 		rate_table->rate = min_fout;
 | |
| 		rate_table->pdiv = min_p;
 | |
| 		rate_table->mdiv = min_m;
 | |
| 		rate_table->sdiv = min_s;
 | |
| 		rate_table->kdiv = 0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int _clk_pll141xx_is_enabled(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	return (int)(pwrcal_getbit(clk->offset, PLL141XX_ENABLE));
 | |
| }
 | |
| 
 | |
| static int _clk_pll141xx_enable(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	int timeout;
 | |
| 
 | |
| 	pwrcal_setbit(clk->offset, PLL141XX_ENABLE, 1);
 | |
| 
 | |
| 	for (timeout = 0;; timeout++) {
 | |
| 		if (pwrcal_getbit(clk->offset, PLL141XX_LOCKED))
 | |
| 			break;
 | |
| 		if (timeout > CLK_WAIT_CNT)
 | |
| 			return -1;
 | |
| 		cpu_relax();
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_pll1419x_enable(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	int timeout;
 | |
| 
 | |
| 	pwrcal_setbit(clk->offset, PLL141XX_ENABLE, 1);
 | |
| 
 | |
| 	for (timeout = 0;; timeout++) {
 | |
| 		if (pwrcal_getbit(clk->offset, PLL141XX_LOCKED))
 | |
| 			break;
 | |
| 		if (timeout > CLK_WAIT_CNT)
 | |
| 			return -1;
 | |
| 		cpu_relax();
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_pll141xx_disable(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	pwrcal_setbit(clk->offset, PLL141XX_ENABLE, 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int _clk_pll141xx_is_disabled_bypass(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	if (pwrcal_getbit(clk->offset + 1, PLL141XX_BYPASS))
 | |
| 		return 0;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| int _clk_pll141xx_set_bypass(struct pwrcal_clk *clk, int bypass_disable)
 | |
| {
 | |
| 	if (bypass_disable == 0)
 | |
| 		pwrcal_setbit(clk + 1, PLL141XX_BYPASS, 1);
 | |
| 	else
 | |
| 		pwrcal_setbit(clk + 1, PLL141XX_BYPASS, 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_pll141xx_set_pms(struct pwrcal_clk *clk,
 | |
| 				const struct pwrcal_pll_rate_table  *rate_table)
 | |
| {
 | |
| 	unsigned int mdiv, pdiv, sdiv, pll_con0;
 | |
| 
 | |
| 	int timeout;
 | |
| 
 | |
| 	pdiv = rate_table->pdiv;
 | |
| 	mdiv = rate_table->mdiv;
 | |
| 	sdiv = rate_table->sdiv;
 | |
| 
 | |
| 	pll_con0 = pwrcal_readl(clk->offset);
 | |
| 	pll_con0 &= ~((PLL141XX_MDIV_MASK << PLL141XX_MDIV_SHIFT)
 | |
| 			| (PLL141XX_PDIV_MASK << PLL141XX_PDIV_SHIFT)
 | |
| 			| (PLL141XX_SDIV_MASK << PLL141XX_SDIV_SHIFT));
 | |
| 	pll_con0 |=  (mdiv << PLL141XX_MDIV_SHIFT)
 | |
| 			| (pdiv << PLL141XX_PDIV_SHIFT)
 | |
| 			| (sdiv << PLL141XX_SDIV_SHIFT);
 | |
| 	pll_con0 &= ~(1<<26);
 | |
| 	pll_con0 |= (1<<5);
 | |
| 
 | |
| 	pwrcal_writel(clk->status, pdiv*150);
 | |
| 	pwrcal_writel(clk->offset, pll_con0);
 | |
| 
 | |
| 	if (pll_con0 & (1 << PLL141XX_ENABLE)) {
 | |
| 		for (timeout = 0;; timeout++) {
 | |
| 			if (pwrcal_getbit(clk->offset, PLL141XX_LOCKED))
 | |
| 				break;
 | |
| 			if (timeout > CLK_WAIT_CNT)
 | |
| 				return -1;
 | |
| 			cpu_relax();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| static int _clk_pll1419x_set_pms(struct pwrcal_clk *clk,
 | |
| 				const struct pwrcal_pll_rate_table  *rate_table)
 | |
| {
 | |
| 	unsigned int mdiv, pdiv, sdiv, pll_con0;
 | |
| 
 | |
| 	int timeout;
 | |
| 
 | |
| 	pdiv = rate_table->pdiv;
 | |
| 	mdiv = rate_table->mdiv;
 | |
| 	sdiv = rate_table->sdiv;
 | |
| 
 | |
| 	pll_con0 = pwrcal_readl(clk->offset);
 | |
| 	pll_con0 &= ~((PLL141XX_MDIV_MASK << PLL141XX_MDIV_SHIFT)
 | |
| 			| (PLL141XX_PDIV_MASK << PLL141XX_PDIV_SHIFT)
 | |
| 			| (PLL141XX_SDIV_MASK << PLL141XX_SDIV_SHIFT));
 | |
| 	pll_con0 |=  (mdiv << PLL141XX_MDIV_SHIFT)
 | |
| 			| (pdiv << PLL141XX_PDIV_SHIFT)
 | |
| 			| (sdiv << PLL141XX_SDIV_SHIFT);
 | |
| 
 | |
| 	pwrcal_writel(clk->status, pdiv * 150);
 | |
| 	pwrcal_writel(clk->offset, pll_con0);
 | |
| 
 | |
| 	if (pll_con0 & (1 << PLL141XX_ENABLE)) {
 | |
| 		for (timeout = 0;; timeout++) {
 | |
| 			if (pwrcal_getbit(clk->offset, PLL141XX_LOCKED))
 | |
| 				break;
 | |
| 			if (timeout > CLK_WAIT_CNT)
 | |
| 				return -1;
 | |
| 			cpu_relax();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_pll141xx_set_rate(struct pwrcal_clk *clk,
 | |
| 					unsigned long long rate)
 | |
| {
 | |
| 	struct pwrcal_pll *pll = to_pll(clk);
 | |
| 	struct pll_spec *pll_spec;
 | |
| 	struct pwrcal_pll_rate_table  tmp_rate_table;
 | |
| 	const struct pwrcal_pll_rate_table  *rate_table;
 | |
| 
 | |
| 	if (rate == 0) {
 | |
| 		if (_clk_pll141xx_is_enabled(clk) != 0)
 | |
| 			if (_clk_pll141xx_disable(clk))
 | |
| 				goto errorout;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	rate_table = _clk_get_pll_settings(pll, rate);
 | |
| 	if (rate_table == NULL) {
 | |
| 		pll_spec = clk_pll_get_spec(clk);
 | |
| 		if (pll_spec == NULL)
 | |
| 			goto errorout;
 | |
| 
 | |
| 		if (_clk_pll141xx_find_pms(pll_spec, &tmp_rate_table, rate)) {
 | |
| 			pr_err("can't find pms value for rate(%lldHz) of \'%s\'",
 | |
| 				rate,
 | |
| 				clk->name);
 | |
| 			goto errorout;
 | |
| 		}
 | |
| 
 | |
| 		rate_table = &tmp_rate_table;
 | |
| 
 | |
| 		pr_warn("not exist in rate table, p(%d), m(%d), s(%d), fout(%lldHz) %s",
 | |
| 				rate_table->pdiv,
 | |
| 				rate_table->mdiv,
 | |
| 				rate_table->sdiv,
 | |
| 				rate,
 | |
| 				clk->name);
 | |
| 	}
 | |
| 
 | |
| 	if (_clk_pll141xx_set_pms(clk, rate_table))
 | |
| 		goto errorout;
 | |
| 
 | |
| 	if (rate != 0) {
 | |
| 		if (_clk_pll141xx_is_enabled(clk) == 0)
 | |
| 			_clk_pll141xx_enable(clk);
 | |
| 	}
 | |
| 	return 0;
 | |
| 
 | |
| errorout:
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int _clk_pll1419x_set_rate(struct pwrcal_clk *clk,
 | |
| 					unsigned long long rate)
 | |
| {
 | |
| 	struct pwrcal_pll *pll = to_pll(clk);
 | |
| 	struct pll_spec *pll_spec;
 | |
| 	struct pwrcal_pll_rate_table  tmp_rate_table;
 | |
| 	const struct pwrcal_pll_rate_table  *rate_table;
 | |
| 
 | |
| 	if (rate == 0) {
 | |
| 		if (_clk_pll141xx_is_enabled(clk) != 0)
 | |
| 			if (_clk_pll141xx_disable(clk))
 | |
| 				goto errorout;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	rate_table = _clk_get_pll_settings(pll, rate);
 | |
| 	if (rate_table == NULL) {
 | |
| 		pll_spec = clk_pll_get_spec(clk);
 | |
| 		if (pll_spec == NULL)
 | |
| 			goto errorout;
 | |
| 
 | |
| 		if (_clk_pll1419x_find_pms(pll_spec, &tmp_rate_table, rate)) {
 | |
| 			pr_err("can't find pms value for rate(%lldHz) of \'%s\'",
 | |
| 				rate,
 | |
| 				clk->name);
 | |
| 			goto errorout;
 | |
| 		}
 | |
| 
 | |
| 		rate_table = &tmp_rate_table;
 | |
| 
 | |
| 		pr_warn("not exist in rate table, p(%d), m(%d), s(%d), fout(%lldHz) %s",
 | |
| 				rate_table->pdiv,
 | |
| 				rate_table->mdiv,
 | |
| 				rate_table->sdiv,
 | |
| 				rate,
 | |
| 				clk->name);
 | |
| 	}
 | |
| 
 | |
| 	_clk_pll141xx_disable(clk);
 | |
| 
 | |
| 	if (_clk_pll1419x_set_pms(clk, rate_table))
 | |
| 		goto errorout;
 | |
| 
 | |
| 	if (rate != 0) {
 | |
| 		if (_clk_pll141xx_is_enabled(clk) == 0)
 | |
| 			_clk_pll1419x_enable(clk);
 | |
| 	}
 | |
| 	return 0;
 | |
| 
 | |
| errorout:
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static unsigned long long _clk_pll141xx_get_rate(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	unsigned int mdiv, pdiv, sdiv, pll_con0;
 | |
| 	unsigned long long fout;
 | |
| 
 | |
| 	if (_clk_pll141xx_is_enabled(clk) == 0)
 | |
| 		return 0;
 | |
| 	pll_con0 = pwrcal_readl(clk->offset);
 | |
| 	mdiv = (pll_con0 >> PLL141XX_MDIV_SHIFT) & PLL141XX_MDIV_MASK;
 | |
| 	pdiv = (pll_con0 >> PLL141XX_PDIV_SHIFT) & PLL141XX_PDIV_MASK;
 | |
| 	sdiv = (pll_con0 >> PLL141XX_SDIV_SHIFT) & PLL141XX_SDIV_MASK;
 | |
| 
 | |
| 	if (pdiv == 0) {
 | |
| 		pr_err("pdiv is 0, id(%s)", clk->name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	fout = FIN_HZ_26M * mdiv;
 | |
| 	do_div(fout, (pdiv << sdiv));
 | |
| 	return (unsigned long long)fout;
 | |
| }
 | |
| 
 | |
| static unsigned long long _clk_pll1419x_get_rate(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	unsigned int mdiv, pdiv, sdiv, pll_con0;
 | |
| 	unsigned long long fout;
 | |
| 
 | |
| 	if (_clk_pll141xx_is_enabled(clk) == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	pll_con0 = pwrcal_readl(clk->offset);
 | |
| 	mdiv = (pll_con0 >> PLL141XX_MDIV_SHIFT) & PLL141XX_MDIV_MASK;
 | |
| 	pdiv = (pll_con0 >> PLL141XX_PDIV_SHIFT) & PLL141XX_PDIV_MASK;
 | |
| 	sdiv = (pll_con0 >> PLL141XX_SDIV_SHIFT) & PLL141XX_SDIV_MASK;
 | |
| 
 | |
| 	if (pdiv == 0) {
 | |
| 		pr_err("pdiv is 0, id(%s)", clk->name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	fout = FIN_HZ_26M * 2 * mdiv;
 | |
| 	do_div(fout, (pdiv << sdiv));
 | |
| 	return (unsigned long long)fout;
 | |
| }
 | |
| 
 | |
| static int _clk_pll1431x_find_pms(struct pll_spec *pll_spec,
 | |
| 					struct pwrcal_pll_rate_table *rate_table,
 | |
| 					unsigned long long rate)
 | |
| {
 | |
| 	unsigned int p, m, s;
 | |
| 	signed short k;
 | |
| 	unsigned long long fref, fvco, fout;
 | |
| 	unsigned long long tmprate, tmpfout;
 | |
| 
 | |
| 	for (p = pll_spec->pdiv_min; p <= pll_spec->pdiv_max; p++) {
 | |
| 		fref = FIN_HZ_26M / p;
 | |
| 		if ((fref < pll_spec->fref_min) || (fref > pll_spec->fref_max))
 | |
| 			continue;
 | |
| 
 | |
| 		for (s = pll_spec->sdiv_min; s <= pll_spec->sdiv_max; s++) {
 | |
| 			tmprate = rate;
 | |
| 			do_div(tmprate, KHZ);
 | |
| 			tmprate = tmprate * p * (1 << s);
 | |
| 			do_div(tmprate, (FIN_HZ_26M / KHZ));
 | |
| 			m = (unsigned int)tmprate;
 | |
| 
 | |
| 			if ((m < pll_spec->mdiv_min)
 | |
| 					|| (m > pll_spec->mdiv_max))
 | |
| 				continue;
 | |
| 
 | |
| 			tmprate = rate;
 | |
| 			do_div(tmprate, KHZ);
 | |
| 			tmprate = tmprate * p * (1 << s);
 | |
| 			do_div(tmprate, (FIN_HZ_26M / KHZ));
 | |
| 			tmprate = (tmprate - m) * 65536;
 | |
| 			k = (unsigned int)tmprate;
 | |
| 			if ((k < pll_spec->kdiv_min)
 | |
| 					|| (k > pll_spec->kdiv_max))
 | |
| 				continue;
 | |
| 
 | |
| 			fvco = FIN_HZ_26M * ((m << 16) + k);
 | |
| 			do_div(fvco, p);
 | |
| 			fvco >>= 16;
 | |
| 			if ((fvco < pll_spec->fvco_min)
 | |
| 					|| (fvco > pll_spec->fvco_max))
 | |
| 				continue;
 | |
| 
 | |
| 			fout = fvco >> s;
 | |
| 			if ((fout >= pll_spec->fout_min)
 | |
| 				&& (fout <= pll_spec->fout_max)) {
 | |
| 				tmprate = rate;
 | |
| 				do_div(tmprate, KHZ);
 | |
| 				tmpfout = fout;
 | |
| 				do_div(tmpfout, KHZ);
 | |
| 				if (tmprate == tmpfout) {
 | |
| 					rate_table->rate = fout;
 | |
| 					rate_table->pdiv = p;
 | |
| 					rate_table->mdiv = m;
 | |
| 					rate_table->sdiv = s;
 | |
| 					rate_table->kdiv = k;
 | |
| 					return 0;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int _clk_pll1431x_is_enabled(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	return (int)(pwrcal_getbit(clk->offset, PLL1431X_ENABLE));
 | |
| }
 | |
| 
 | |
| static int _clk_pll1431x_enable(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	int timeout;
 | |
| 
 | |
| 	pwrcal_setbit(clk->offset, PLL1431X_ENABLE, 1);
 | |
| 
 | |
| 	for (timeout = 0;; timeout++) {
 | |
| 		if (pwrcal_getbit(clk->offset, PLL1431X_LOCKED))
 | |
| 			break;
 | |
| 		if (timeout > CLK_WAIT_CNT)
 | |
| 			return -1;
 | |
| 		cpu_relax();
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_pll1431x_disable(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	pwrcal_setbit(clk->offset, PLL1431X_ENABLE, 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int _clk_pll1431x_is_disabled_bypass(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	if (pwrcal_getbit(clk->offset + 2, PLL1431X_BYPASS))
 | |
| 		return 0;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| int _clk_pll1431x_set_bypass(struct pwrcal_clk *clk, int bypass_disable)
 | |
| {
 | |
| 	if (bypass_disable == 0)
 | |
| 		pwrcal_setbit(clk->offset + 2, PLL1431X_BYPASS, 1);
 | |
| 	else
 | |
| 		pwrcal_setbit(clk->offset + 2, PLL1431X_BYPASS, 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_pll1431x_set_pms(struct pwrcal_clk *clk,
 | |
| 			const struct pwrcal_pll_rate_table  *rate_table)
 | |
| {
 | |
| 	unsigned int mdiv, pdiv, sdiv, pll_con0, pll_con1;
 | |
| 	signed short kdiv;
 | |
| 	int timeout;
 | |
| 
 | |
| 	pdiv = rate_table->pdiv;
 | |
| 	mdiv = rate_table->mdiv;
 | |
| 	sdiv = rate_table->sdiv;
 | |
| 	kdiv = rate_table->kdiv;
 | |
| 
 | |
| 	pll_con0 = pwrcal_readl(clk->offset);
 | |
| 	pll_con1 = pwrcal_readl(clk->offset + 1);
 | |
| 	pll_con0 &= ~((PLL1431X_MDIV_MASK << PLL1431X_MDIV_SHIFT)
 | |
| 			| (PLL1431X_PDIV_MASK << PLL1431X_PDIV_SHIFT)
 | |
| 			| (PLL1431X_SDIV_MASK << PLL1431X_SDIV_SHIFT));
 | |
| 	pll_con0 |=  (mdiv << PLL1431X_MDIV_SHIFT)
 | |
| 			| (pdiv << PLL1431X_PDIV_SHIFT)
 | |
| 			| (sdiv << PLL1431X_SDIV_SHIFT);
 | |
| 	pll_con0 &= ~(1<<26);
 | |
| 	pll_con0 |= (1<<5);
 | |
| 
 | |
| 	pll_con1 &= ~(PLL1431X_K_MASK << PLL1431X_K_SHIFT);
 | |
| 	pll_con1 |=  (kdiv << PLL1431X_K_SHIFT);
 | |
| 
 | |
| 	if (kdiv == 0)
 | |
| 		pwrcal_writel(clk->status, pdiv*3000);
 | |
| 	else
 | |
| 		pwrcal_writel(clk->status, pdiv*3000);
 | |
| 	pwrcal_writel(clk->offset, pll_con0);
 | |
| 	pwrcal_writel(clk->offset + 1, pll_con1);
 | |
| 
 | |
| 	if (pll_con0 & (1 << PLL1431X_ENABLE)) {
 | |
| 		for (timeout = 0;; timeout++) {
 | |
| 			if (pwrcal_getbit(clk->offset, PLL1431X_LOCKED))
 | |
| 				break;
 | |
| 			if (timeout > CLK_WAIT_CNT)
 | |
| 				return -1;
 | |
| 			cpu_relax();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_pll1431x_set_rate(struct pwrcal_clk *clk,
 | |
| 					unsigned long long rate)
 | |
| {
 | |
| 	struct pwrcal_pll *pll = to_pll(clk);
 | |
| 	struct pwrcal_pll_rate_table  tmp_rate_table;
 | |
| 	const struct pwrcal_pll_rate_table  *rate_table;
 | |
| 	struct pll_spec *pll_spec;
 | |
| 
 | |
| 	if (rate == 0) {
 | |
| 		if (_clk_pll1431x_is_enabled(clk) != 0)
 | |
| 			if (_clk_pll1431x_disable(clk))
 | |
| 				goto errorout;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	rate_table = _clk_get_pll_settings(pll, rate);
 | |
| 	if (rate_table == NULL) {
 | |
| 		pll_spec = clk_pll_get_spec(clk);
 | |
| 		if (pll_spec == NULL)
 | |
| 			goto errorout;
 | |
| 
 | |
| 		if (_clk_pll1431x_find_pms(pll_spec, &tmp_rate_table, rate) < 0) {
 | |
| 			pr_err("can't find pms value for rate(%lldHz) of %s",
 | |
| 				rate,
 | |
| 				clk->name);
 | |
| 			goto errorout;
 | |
| 		}
 | |
| 
 | |
| 		rate_table = &tmp_rate_table;
 | |
| 		pr_warn("not exist in rate table, p(%d) m(%d) s(%d) k(%d) fout(%lld Hz) of %s",
 | |
| 				rate_table->pdiv,
 | |
| 				rate_table->mdiv,
 | |
| 				rate_table->sdiv,
 | |
| 				rate_table->kdiv,
 | |
| 				rate,
 | |
| 				clk->name);
 | |
| 	}
 | |
| 
 | |
| 	if (_clk_pll1431x_set_pms(clk, rate_table))
 | |
| 		goto errorout;
 | |
| 
 | |
| 	if (rate != 0) {
 | |
| 		if (_clk_pll1431x_is_enabled(clk) == 0)
 | |
| 			_clk_pll1431x_enable(clk);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| errorout:
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static unsigned long long _clk_pll1431x_get_rate(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	unsigned int mdiv, pdiv, sdiv, pll_con0, pll_con1;
 | |
| 	signed short kdiv;
 | |
| 	unsigned long long fout;
 | |
| 
 | |
| 	if (_clk_pll1431x_is_enabled(clk) == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	pll_con0 = pwrcal_readl(clk->offset);
 | |
| 	pll_con1 = pwrcal_readl(clk->offset + 1);
 | |
| 	mdiv = (pll_con0 >> PLL1431X_MDIV_SHIFT) & PLL1431X_MDIV_MASK;
 | |
| 	pdiv = (pll_con0 >> PLL1431X_PDIV_SHIFT) & PLL1431X_PDIV_MASK;
 | |
| 	sdiv = (pll_con0 >> PLL1431X_SDIV_SHIFT) & PLL1431X_SDIV_MASK;
 | |
| 
 | |
| 	kdiv = (short)(pll_con1 >> PLL1431X_K_SHIFT) & PLL1431X_K_MASK;
 | |
| 
 | |
| 	if (pdiv == 0) {
 | |
| 		pr_err("pdiv is 0, id(%s)", clk->name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	fout = FIN_HZ_26M * ((mdiv << 16) + kdiv);
 | |
| 	do_div(fout, (pdiv << sdiv));
 | |
| 	fout >>= 16;
 | |
| 
 | |
| 	return (unsigned long long)fout;
 | |
| }
 | |
| 
 | |
| static int _clk_wpll_usbpll_is_enabled(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	if (pwrcal_getbit(clk->offset, USBPLL_WPLL_SEL) == 1) {
 | |
| 		pr_err("%s: USBPLL_WPLL_SEL==1\n", __func__);
 | |
| 		return (int)(pwrcal_getbit(clk->offset, USBPLL_WPLL_EN));
 | |
| 	}
 | |
| 	pr_err("%s: USBPLL_WPLL_SEL==0\n", __func__);
 | |
| 	return (int)pwrcal_getbit(clk->status, WIFI2AP_USBPLL_ACK);
 | |
| }
 | |
| 
 | |
| static int _clk_wpll_usbpll_enable(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	int timeout;
 | |
| 
 | |
| 	/* check changed WPLL input selection to AP (AP2WLBT_USBPLL_WPLL_SEL). */
 | |
| 	if (pwrcal_getbit(clk->offset, USBPLL_WPLL_SEL) == 0x1) {
 | |
| 		pr_info("%s AP %p\n", __func__, clk->offset);
 | |
| 
 | |
| 		if (pwrcal_getbit(clk->offset, USBPLL_WPLL_EN) == 0x1) {
 | |
| 			pr_info("%s USBPLL_WPLL_EN==1\n", __func__);
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		/* pwrcal_setbit(clk->offset, 0, 0x1); */
 | |
| 		if (pwrcal_getbit(TCXO_SHARED_STATUS, 8) == 1)
 | |
| 			pwrcal_setbit(clk->offset, USBPLL_WPLL_FREF_SEL, 0x1); /* TCXO_SHARED_STATUS = 52Mhz */
 | |
| 		else
 | |
| 			pwrcal_setbit(clk->offset, USBPLL_WPLL_FREF_SEL, 0x0); /* 26Mhz */
 | |
| 
 | |
| 		/* Set WPLL enable (AP2WLBT_USBPLL_WPLL_EN) */
 | |
| 		pwrcal_setbit(clk->offset, USBPLL_WPLL_EN, 0x1);
 | |
| 
 | |
| 		/* wait 20us for power settle time. */
 | |
| 		for (timeout = 0;; timeout++) {
 | |
| 			if (timeout >= 20)
 | |
| 				break;
 | |
| 			cpu_relax();
 | |
| 		}
 | |
| 
 | |
| 		/* Set WPLL AFC Start (AP2WLBT_USBPLL_WPLL_AFC_START) */
 | |
| 		pwrcal_setbit(clk->offset, USBPLL_WPLL_AFC_START, 0x1);
 | |
| 
 | |
| 		/* wait 60us for clock stabilization. */
 | |
| 		for (timeout = 0;; timeout++) {
 | |
| 			if (timeout >= 60)
 | |
| 				break;
 | |
| 			cpu_relax();
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| 		pr_info("%s WLBT\n", __func__);
 | |
| 
 | |
| 		/* WPLL input selection to WLBT */
 | |
| 
 | |
| 		/* (IP) use ack */
 | |
| 		if (pwrcal_getbit(clk->status, WIFI2AP_USBPLL_ACK) == 0x1) {
 | |
| 			pr_info("%s USBPLL_ACK==1, already\n", __func__);
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		/* AP2WIFI_USBPLL_REQ */
 | |
| 		pwrcal_setbit(clk->status, AP2WIFI_USBPLL_REQ, 0x1);
 | |
| 		pr_info("%s AP2WIFI_USBPLL_REQ=1\n", __func__);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int _clk_wpll_usbpll_disable(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	if (pwrcal_getbit(clk->offset, USBPLL_WPLL_SEL) == 0) {
 | |
| 		pwrcal_setbit(clk->status, AP2WIFI_USBPLL_REQ, 0x0);
 | |
| 		pr_err("%s AP2WIFI_USBPLL_REQ=0\n", __func__);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int _clk_wpll_usbpll_set_rate(struct pwrcal_clk *clk,
 | |
| 					unsigned long long rate)
 | |
| {
 | |
| 
 | |
| 	if (rate == 0) {
 | |
| 		if (_clk_wpll_usbpll_is_enabled(clk) != 0)
 | |
| 			if (_clk_wpll_usbpll_disable(clk))
 | |
| 				goto errorout;
 | |
| 	} else { /* rate != 0  */
 | |
| 		if (_clk_wpll_usbpll_is_enabled(clk) == 0)
 | |
| 			_clk_wpll_usbpll_enable(clk);
 | |
| 		/*	pr_warn("WPLL_USBPLL set 20Mhz"); */
 | |
| 	}
 | |
| 	return 0;
 | |
| 
 | |
| errorout:
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static unsigned long long _clk_wpll_usbpll_get_rate(struct pwrcal_clk *clk)
 | |
| {
 | |
| 	unsigned long long fout;
 | |
| 
 | |
| 	if (_clk_wpll_usbpll_is_enabled(clk) == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	fout = (20*MHZ);
 | |
| 	return (unsigned long long)fout;
 | |
| }
 | |
| 
 | |
| struct pwrcal_pll_ops pll141xx_ops = {
 | |
| 	.is_enabled = _clk_pll141xx_is_enabled,
 | |
| 	.enable = _clk_pll141xx_enable,
 | |
| 	.disable = _clk_pll141xx_disable,
 | |
| 	.set_rate = _clk_pll141xx_set_rate,
 | |
| 	.get_rate = _clk_pll141xx_get_rate,
 | |
| };
 | |
| 
 | |
| struct pwrcal_pll_ops pll1419x_ops = {
 | |
| 	.is_enabled = _clk_pll141xx_is_enabled,
 | |
| 	.enable = _clk_pll1419x_enable,
 | |
| 	.disable = _clk_pll141xx_disable,
 | |
| 	.set_rate = _clk_pll1419x_set_rate,
 | |
| 	.get_rate = _clk_pll1419x_get_rate,
 | |
| };
 | |
| 
 | |
| struct pwrcal_pll_ops pll1431x_ops = {
 | |
| 	.is_enabled = _clk_pll1431x_is_enabled,
 | |
| 	.enable = _clk_pll1431x_enable,
 | |
| 	.disable = _clk_pll1431x_disable,
 | |
| 	.set_rate = _clk_pll1431x_set_rate,
 | |
| 	.get_rate = _clk_pll1431x_get_rate,
 | |
| };
 | |
| 
 | |
| struct pwrcal_pll_ops wpll_usbpll_ops = {
 | |
| 	.is_enabled = _clk_wpll_usbpll_is_enabled,
 | |
| 	.enable = _clk_wpll_usbpll_enable,
 | |
| 	.disable = _clk_wpll_usbpll_disable,
 | |
| 	.set_rate = _clk_wpll_usbpll_set_rate,
 | |
| 	.get_rate = _clk_wpll_usbpll_get_rate,
 | |
| };
 | 
