diff options
Diffstat (limited to 'drivers/mfd/twl6040-core.c')
| -rw-r--r-- | drivers/mfd/twl6040-core.c | 128 | 
1 files changed, 77 insertions, 51 deletions
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c index dda86293dc9..b2d8e512d3c 100644 --- a/drivers/mfd/twl6040-core.c +++ b/drivers/mfd/twl6040-core.c @@ -282,6 +282,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)  		/* Default PLL configuration after power up */  		twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL;  		twl6040->sysclk = 19200000; +		twl6040->mclk = 32768;  	} else {  		/* already powered-down */  		if (!twl6040->power_count) { @@ -305,6 +306,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)  			twl6040_power_down(twl6040);  		}  		twl6040->sysclk = 0; +		twl6040->mclk = 0;  	}  out: @@ -324,23 +326,38 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,  	hppllctl = twl6040_reg_read(twl6040, TWL6040_REG_HPPLLCTL);  	lppllctl = twl6040_reg_read(twl6040, TWL6040_REG_LPPLLCTL); +	/* Force full reconfiguration when switching between PLL */ +	if (pll_id != twl6040->pll) { +		twl6040->sysclk = 0; +		twl6040->mclk = 0; +	} +  	switch (pll_id) {  	case TWL6040_SYSCLK_SEL_LPPLL:  		/* low-power PLL divider */ -		switch (freq_out) { -		case 17640000: -			lppllctl |= TWL6040_LPLLFIN; -			break; -		case 19200000: -			lppllctl &= ~TWL6040_LPLLFIN; -			break; -		default: -			dev_err(twl6040->dev, -				"freq_out %d not supported\n", freq_out); -			ret = -EINVAL; -			goto pll_out; +		/* Change the sysclk configuration only if it has been canged */ +		if (twl6040->sysclk != freq_out) { +			switch (freq_out) { +			case 17640000: +				lppllctl |= TWL6040_LPLLFIN; +				break; +			case 19200000: +				lppllctl &= ~TWL6040_LPLLFIN; +				break; +			default: +				dev_err(twl6040->dev, +					"freq_out %d not supported\n", +					freq_out); +				ret = -EINVAL; +				goto pll_out; +			} +			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, +					  lppllctl);  		} -		twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); + +		/* The PLL in use has not been change, we can exit */ +		if (twl6040->pll == pll_id) +			break;  		switch (freq_in) {  		case 32768: @@ -371,48 +388,56 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,  			goto pll_out;  		} -		hppllctl &= ~TWL6040_MCLK_MSK; +		if (twl6040->mclk != freq_in) { +			hppllctl &= ~TWL6040_MCLK_MSK; + +			switch (freq_in) { +			case 12000000: +				/* PLL enabled, active mode */ +				hppllctl |= TWL6040_MCLK_12000KHZ | +					    TWL6040_HPLLENA; +				break; +			case 19200000: +				/* +				* PLL disabled +				* (enable PLL if MCLK jitter quality +				*  doesn't meet specification) +				*/ +				hppllctl |= TWL6040_MCLK_19200KHZ; +				break; +			case 26000000: +				/* PLL enabled, active mode */ +				hppllctl |= TWL6040_MCLK_26000KHZ | +					    TWL6040_HPLLENA; +				break; +			case 38400000: +				/* PLL enabled, active mode */ +				hppllctl |= TWL6040_MCLK_38400KHZ | +					    TWL6040_HPLLENA; +				break; +			default: +				dev_err(twl6040->dev, +					"freq_in %d not supported\n", freq_in); +				ret = -EINVAL; +				goto pll_out; +			} -		switch (freq_in) { -		case 12000000: -			/* PLL enabled, active mode */ -			hppllctl |= TWL6040_MCLK_12000KHZ | -				    TWL6040_HPLLENA; -			break; -		case 19200000:  			/* -			 * PLL disabled -			 * (enable PLL if MCLK jitter quality -			 *  doesn't meet specification) +			 * enable clock slicer to ensure input waveform is +			 * square  			 */ -			hppllctl |= TWL6040_MCLK_19200KHZ; -			break; -		case 26000000: -			/* PLL enabled, active mode */ -			hppllctl |= TWL6040_MCLK_26000KHZ | -				    TWL6040_HPLLENA; -			break; -		case 38400000: -			/* PLL enabled, active mode */ -			hppllctl |= TWL6040_MCLK_38400KHZ | -				    TWL6040_HPLLENA; -			break; -		default: -			dev_err(twl6040->dev, -				"freq_in %d not supported\n", freq_in); -			ret = -EINVAL; -			goto pll_out; -		} +			hppllctl |= TWL6040_HPLLSQRENA; -		/* enable clock slicer to ensure input waveform is square */ -		hppllctl |= TWL6040_HPLLSQRENA; - -		twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, hppllctl); -		usleep_range(500, 700); -		lppllctl |= TWL6040_HPLLSEL; -		twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); -		lppllctl &= ~TWL6040_LPLLENA; -		twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl); +			twl6040_reg_write(twl6040, TWL6040_REG_HPPLLCTL, +					  hppllctl); +			usleep_range(500, 700); +			lppllctl |= TWL6040_HPLLSEL; +			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, +					  lppllctl); +			lppllctl &= ~TWL6040_LPLLENA; +			twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, +					  lppllctl); +		}  		break;  	default:  		dev_err(twl6040->dev, "unknown pll id %d\n", pll_id); @@ -421,6 +446,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,  	}  	twl6040->sysclk = freq_out; +	twl6040->mclk = freq_in;  	twl6040->pll = pll_id;  pll_out:  |