diff options
| -rw-r--r-- | Documentation/devicetree/bindings/power/omap-prm-voltsetup.txt | 20 | ||||
| -rw-r--r-- | drivers/power/avs/omap3_prm_voltsetup.c | 248 | ||||
| -rw-r--r-- | drivers/power/avs/omap_prm_voltsetup.h | 15 | ||||
| -rw-r--r-- | include/linux/power/omap_prm.h | 39 | 
4 files changed, 226 insertions, 96 deletions
diff --git a/Documentation/devicetree/bindings/power/omap-prm-voltsetup.txt b/Documentation/devicetree/bindings/power/omap-prm-voltsetup.txt index d1b2c1cff1e..67c5024b86e 100644 --- a/Documentation/devicetree/bindings/power/omap-prm-voltsetup.txt +++ b/Documentation/devicetree/bindings/power/omap-prm-voltsetup.txt @@ -18,10 +18,21 @@ Optional Properties:    description in OMAP TRM.  - sys_off_mode: bool value selects the mode used to send OFF command. If present    (true) sys_off_mode pin will be used, otherwise (false) I2C will be used. -- auto_off: bool vlaue enables/disables send OFF command over I2C. Applicable if -  sys_off_mode is false; -- auto_retention: bool vlaue enables/disables send RETENTION command over I2C. -- auto_sleep: bool vlaue enables/disables send SLEEP command over I2C. +- auto_off: bool vlaue enables/disables OFF command over I2C and assertion +  sys_off_mode signal. +- auto_retention: bool vlaue enables/disables RETENTION command over I2C and +  assertion CLQ_REQ signal. +- offmodesetup_time: voltage setup time in micro seconds when the device exits +  the off mode. It used only if sys_off_mode is true. If this value is not +  defined, then time is calculated based on PMIC slew rate and voltage level +  will be used. Claculated time is used also if value in device tree less then +  calculated. +- setup_time1: VDD1 voltage setup time in micro seconds when the device exits +  the off mode. It used only if sys_off_mode is false. If this value is not +  defined, then time is calculated based on PMIC slew rate and voltage level +  will be used. Claculated time is used also if value in device tree less then +  calculated. +- setup_time2: same as above only for VDD2.  Example:  glbl_prm: glbl_prm@48307250 { @@ -29,6 +40,7 @@ glbl_prm: glbl_prm@48307250 {  	reg = <0x48307250 0x60>;  	reg-names = "base-address";  	sys_off_mode; +	auto_off;  	auto_retention;  	autoextclkmode = <2>;  	clksetup_time = <1000>; diff --git a/drivers/power/avs/omap3_prm_voltsetup.c b/drivers/power/avs/omap3_prm_voltsetup.c index cd7ad4f1064..97951060df5 100644 --- a/drivers/power/avs/omap3_prm_voltsetup.c +++ b/drivers/power/avs/omap3_prm_voltsetup.c @@ -26,6 +26,7 @@  #include <linux/regmap.h>  #include <linux/clk.h>  #include <linux/err.h> +#include <linux/power/omap_prm.h>  #include <linux/regulator/omap-pmic-regulator.h>  #include "omap_prm_voltsetup.h" @@ -47,20 +48,23 @@  #define VOLTCTRL_AUTO_SLEEP_MASK		0x01  #define VOLTCTRL_AUTO_RET_MASK			0x02  #define VOLTCTRL_AUTO_OFF_MASK			0x04 +#define VOLTCTRL_AUTO_MASK			0x07  #define VOLTCTRL_SEL_OFF_MASK			0x08  #define CLKSRC_CTRL_AUTOEXTCLKMODE_MASK		0x18  #define CLKSETUP_MASK				0xffff -#define VOLTSETUP2_MASK				0xffff +#define OFFMODESETUPTIME_MASK			0xffff  #define VOLTOFFSET_MASK				0xffff  #define VOLTSETUP1_TIME1_MASK			0x0000ffff  #define VOLTSETUP1_TIME2_MASK			0xffff0000  struct omap_prm_voltsetup_data { +	struct device *dev;  	struct regmap *regmap;  	const struct omap_vc_common_reg *regs;  	bool sys_off_mode; -	u32 autoextclkmode; +	bool auto_retention; +	bool auto_off;  	u32 clksetup_time; /* uSec */  	u32 sys_clk_rate; /* Hz */  }; @@ -84,81 +88,63 @@ int omap_prm_voltsetup(struct device *dev, struct omap_pmic *pmic, u32 uv)  	struct regmap *regmap;  	const char *str;  	int ret = 0, val; -	u32 clsetup, v1, v2, v2_old, msk; +	u32 v1, v2, v2_old, msk; +	struct device *prm_dev = prm_voltsetup_data->dev;  	if (!dev || !dev->of_node || !pmic || uv == 0) { -		pr_err("Invalid parameters\n"); +		dev_err(prm_dev, "Invalid parameters\n");  		return -EINVAL;  	}  	if (IS_ERR_OR_NULL(prm_voltsetup_data)) { -		pr_err("Device is not initialized\n"); +		dev_err(prm_dev, "Device is not initialized\n");  		return -ENODEV;  	}  	regmap = prm_voltsetup_data->regmap;  	if (prm_voltsetup_data->sys_off_mode) { -		ret = regmap_read(regmap, PRM_CLKSETUP_OFFS, &clsetup); -		if (ret) -			goto fail_reg; -		clsetup &= CLKSETUP_MASK; -		clsetup >>= __ffs(CLKSETUP_MASK); -  		ret = regmap_read(regmap, PRM_VOLTSETUP2_OFFS, &v2_old);  		if (ret)  			goto fail_reg; -		v2_old &= VOLTSETUP2_MASK; -		v2_old >>= __ffs(VOLTSETUP2_MASK); +		v2_old &= OFFMODESETUPTIME_MASK; +		v2_old >>= __ffs(OFFMODESETUPTIME_MASK); + +		v2_old = DIV_ROUND_CLOSEST(v2_old * 1000000, 32768);  		/* voltage / slew_rate, 2uS added as buffer */  		val = DIV_ROUND_UP(uv, pmic->info->slew_rate_uV) + 2; -		/* convert to 32k clk cycles */ -		v2 = DIV_ROUND_UP(val * 32768, 1000000); +  		/*  		 * Update v2 if higher than current value (needed because  		 * we have multiple channels with different ramp times), also  		 * update voltoffset always to value recommended by TRM  		 */ -		if (v2 > v2_old) { -			pr_info("voltsetup2 is set to %d uS\n", val); +		if (val > v2_old) { +			dev_dbg(prm_dev, "voltsetup2 is set to %d uS\n", val); +			v2 = DIV_ROUND_UP(val * 32768, 1000000);  			ret = regmap_update_bits(regmap, PRM_VOLTSETUP2_OFFS, -					VOLTSETUP2_MASK, -					v2 << __ffs(VOLTSETUP2_MASK)); +					OFFMODESETUPTIME_MASK, +					v2 << __ffs(OFFMODESETUPTIME_MASK));  			if (ret)  				goto fail_reg; -			val = clsetup - v2;  		} else -			val = clsetup - v2_old; +			val = v2_old; -		if (val < 0) { -			/* -			* in case of clock setup time is less then voltsetup -			* we should correct it to allow start clock usage after -			* voltage rumped up -			*/ -			val = 1; -			clsetup = v2 > v2_old ? v2 : v2_old; -			clsetup += val; -			ret = regmap_update_bits(regmap, PRM_CLKSETUP_OFFS, -				CLKSETUP_MASK, clsetup << __ffs(CLKSETUP_MASK)); -			if (ret) -				goto fail_reg; -			pr_info("Clock setup time is ajusted to %d uS\n", -				DIV_ROUND_CLOSEST(clsetup * 1000000, 32768)); -		} +		if (prm_voltsetup_data->clksetup_time > val) +			val = prm_voltsetup_data->clksetup_time - val; +		else +			val = 30; +		dev_dbg(prm_dev, "voltoffset is set to %d uS\n", val); +		val = DIV_ROUND_UP(val * 32768, 1000000);  		ret = regmap_update_bits(regmap, PRM_VOLTOFFSET_OFFS,  				VOLTOFFSET_MASK, val << __ffs(VOLTOFFSET_MASK));  		if (ret)  			goto fail_reg;  	} else { -		/* voltage / slew_rate */ -		v1 = DIV_ROUND_UP(uv, pmic->info->slew_rate_uV); -		v1 = DIV_ROUND_UP_ULL( -			(u64)v1 * prm_voltsetup_data->sys_clk_rate, 8000000); -  		ret = of_property_read_string(dev->of_node, "compatible", &str);  		if (ret) { -			pr_err("No compatible property in channel (%d)\n", ret); +			dev_err(prm_dev, +			"No compatible property in channel (%d)\n", ret);  			return ret;  		}  		val = strlen(str); @@ -170,8 +156,20 @@ int omap_prm_voltsetup(struct device *dev, struct omap_pmic *pmic, u32 uv)  			pr_err("Unable to identify vc channel\n");  			return -EINVAL;  		} -		ret = regmap_update_bits(regmap, PRM_VOLTSETUP1_OFFS, -				msk, v1 << __ffs(msk)); +		ret = regmap_read(regmap, PRM_VOLTSETUP1_OFFS, &val); +		if (ret) +			goto fail_reg; +		val &= msk; +		val >>= __ffs(msk); + +		/* voltage / slew_rate */ +		v1 = DIV_ROUND_UP(uv, pmic->info->slew_rate_uV); +		v1 = DIV_ROUND_UP_ULL( +			(u64)v1 * prm_voltsetup_data->sys_clk_rate, 8000000); +		if (val > v1) +			v1 = val; +		v1 <<= __ffs(msk); +		ret = regmap_update_bits(regmap, PRM_VOLTSETUP1_OFFS, msk, v1);  		if (ret)  			goto fail_reg;  	} @@ -182,6 +180,77 @@ fail_reg:  	return ret;  }  EXPORT_SYMBOL_GPL(omap_prm_voltsetup); +/** + * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled + * + * Intended for use only by OMAP PM core code to notify this layer + * that off mode has been enabled. + */ +void omap_pm_enable_off_mode(void) +{ +	prm_voltsetup_data->auto_off = true; +} +EXPORT_SYMBOL_GPL(omap_pm_enable_off_mode); + +/** + * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled + * + * Intended for use only by OMAP PM core code to notify this layer + * that off mode has been disabled. + */ +void omap_pm_disable_off_mode(void) +{ +	prm_voltsetup_data->auto_off = false; +} +EXPORT_SYMBOL_GPL(omap_pm_disable_off_mode); + +/** + * omap_pm_get_off_mode - get OMAP PM off-mode state + * + * Intended for use only by OMAP PM core code to query off mode state + */ +bool omap_pm_get_off_mode(void) +{ +	return prm_voltsetup_data->auto_off; +} +EXPORT_SYMBOL_GPL(omap_pm_get_off_mode); + +static int omap3_prm_suspend_noirq(struct device *dev) +{ +	u32 msk = VOLTCTRL_AUTO_MASK, val = 0; +	struct platform_device *pdev = to_platform_device(dev); +	struct omap_prm_voltsetup_data *data = platform_get_drvdata(pdev); + +	/* clean all auto bits */ +	regmap_update_bits(data->regmap, PRM_VOLTCTRL_OFFS, msk, val); +	/* set auto mode exclusively wiht "off" as priority*/ +	if (data->auto_off) { +		msk = VOLTCTRL_AUTO_OFF_MASK; +		val = 1; +	} else if (data->auto_retention) { +		msk = VOLTCTRL_AUTO_RET_MASK; +		val = 1; +	} +	return regmap_update_bits(data->regmap, PRM_VOLTCTRL_OFFS, msk, +		val << __ffs(msk)); +} + +static int omap3_prm_resume_noirq(struct device *dev) +{ +	u32 msk = VOLTCTRL_AUTO_MASK, val = 0; +	struct platform_device *pdev = to_platform_device(dev); +	struct omap_prm_voltsetup_data *data = platform_get_drvdata(pdev); + +	/* clean all auto bits */ +	regmap_update_bits(data->regmap, PRM_VOLTCTRL_OFFS, msk, val); +	/* restore mode to retention by default if defined */ +	if (data->auto_retention) { +		msk = VOLTCTRL_AUTO_RET_MASK; +		val = 1; +	} +	return regmap_update_bits(data->regmap, PRM_VOLTCTRL_OFFS, msk, +		val << __ffs(msk)); +}  static int omap3_prm_voltsetup_probe(struct platform_device *pdev) @@ -195,7 +264,7 @@ static int omap3_prm_voltsetup_probe(struct platform_device *pdev)  	void __iomem *base;  	struct regmap *regmap;  	struct omap_prm_voltsetup_data *data; -	u32 clkmode, val = 0, msk = 0; +	u32 clkmode, val = 0;  	struct clk *sys_clk;  	if (!nd) { @@ -248,38 +317,58 @@ static int omap3_prm_voltsetup_probe(struct platform_device *pdev)  	if (ret || !data->clksetup_time)  		goto property_err; -	pname = "sys_off_mode"; -	if (of_property_read_bool(nd, pname)) -		data->sys_off_mode = true; - -	if (!data->sys_off_mode) { -		pname = "auto_off"; -		val |= of_property_read_bool(nd, pname) ? -			VOLTCTRL_AUTO_OFF_MASK : 0; -	} else -		val |= VOLTCTRL_SEL_OFF_MASK; - -	pname = "auto_sleep"; -	val |= of_property_read_bool(nd, pname) ? VOLTCTRL_AUTO_SLEEP_MASK : 0; +	pname = "auto_off"; +	data->auto_off = of_property_read_bool(nd, pname);  	pname = "auto_retention"; -	val |= of_property_read_bool(nd, pname) ? VOLTCTRL_AUTO_RET_MASK : 0; -	val |= data->sys_off_mode ? VOLTCTRL_SEL_OFF_MASK : 0; +	data->auto_retention = of_property_read_bool(nd, pname); -	msk = VOLTCTRL_SEL_OFF_MASK | VOLTCTRL_AUTO_OFF_MASK | -		VOLTCTRL_AUTO_RET_MASK | VOLTCTRL_AUTO_SLEEP_MASK; -	ret = regmap_update_bits(regmap, PRM_VOLTCTRL_OFFS, msk, -				val << __ffs(msk)); +	/* clean all auto bits */ +	regmap_update_bits(regmap, PRM_VOLTCTRL_OFFS, VOLTCTRL_AUTO_MASK, 0); +	/* set to retention if enabled by default */ +	val = data->auto_retention ? 1 : 0; +	regmap_update_bits(regmap, PRM_VOLTCTRL_OFFS, +		VOLTCTRL_AUTO_RET_MASK, +		val << __ffs(VOLTCTRL_AUTO_RET_MASK)); -	if (ret) -		goto fail_reg; +	pname = "sys_off_mode"; +	if (of_property_read_bool(nd, pname)) { +		data->sys_off_mode = true; +		regmap_update_bits(regmap, PRM_VOLTCTRL_OFFS, +			VOLTCTRL_SEL_OFF_MASK, +			1 << __ffs(VOLTCTRL_SEL_OFF_MASK)); + +		pname = "offmodesetup_time"; +		val = 0; +		of_property_read_u32(nd, pname, &val); +		/* set value in register even if it is 0 */ +		val = DIV_ROUND_UP(val * 32768, 1000000); +		ret = regmap_update_bits(regmap, PRM_VOLTSETUP2_OFFS, +				OFFMODESETUPTIME_MASK, +				val << __ffs(OFFMODESETUPTIME_MASK)); +		if (ret) +			goto fail_reg; +	} else { +		data->sys_off_mode = false; +		/* set value in register even if it is 0 */ +		pname = "setup_time1"; +		val = 0; +		of_property_read_u32(nd, pname, &val); +		val = DIV_ROUND_UP_ULL((u64)val * data->sys_clk_rate, 8000000); +		ret = regmap_update_bits(regmap, PRM_VOLTSETUP1_OFFS, +			VOLTSETUP1_TIME1_MASK, +			val << __ffs(VOLTSETUP1_TIME1_MASK)); +		pname = "setup_time2"; +		val = 0; +		of_property_read_u32(nd, pname, &val); +		val = DIV_ROUND_UP_ULL((u64)val * data->sys_clk_rate, 8000000); +		ret = regmap_update_bits(regmap, PRM_VOLTSETUP1_OFFS, +			VOLTSETUP1_TIME2_MASK, +			val << __ffs(VOLTSETUP1_TIME2_MASK)); +	} -	ret = regmap_update_bits(regmap, PRM_VOLTCTRL_OFFS, -		VOLTCTRL_SEL_OFF_MASK, -		(data->sys_off_mode ? 1 : 0) << __ffs(VOLTCTRL_SEL_OFF_MASK));  	if (ret)  		goto fail_reg; -  	ret = regmap_update_bits(regmap, PRM_CLKSRC_CTRL_OFFS,  		CLKSRC_CTRL_AUTOEXTCLKMODE_MASK,  		clkmode << __ffs(CLKSRC_CTRL_AUTOEXTCLKMODE_MASK)); @@ -292,16 +381,9 @@ static int omap3_prm_voltsetup_probe(struct platform_device *pdev)  	if (ret)  		goto fail_reg; -	if (data->sys_off_mode) { -		ret = regmap_write(regmap, PRM_VOLTSETUP1_OFFS, 0); -		if (ret) -			goto fail_reg; -	} else { -		ret = regmap_write(regmap, PRM_VOLTSETUP2_OFFS, 0); -		if (ret) -			goto fail_reg; -	} +	data->dev = dev;  	prm_voltsetup_data = data; +	platform_set_drvdata(pdev, prm_voltsetup_data);  	return ret; @@ -318,11 +400,17 @@ fail_reg:  	return ret;  } +static const struct dev_pm_ops omap3_prm_dev_pm_ops = { +	.suspend_noirq = omap3_prm_suspend_noirq, +	.resume_noirq = omap3_prm_resume_noirq, +}; +  static struct platform_driver omap3_prm_voltsetup_driver = {  	.driver = {  		.name = DRIVER_NAME,  		.owner = THIS_MODULE,  		.of_match_table = of_match_ptr(omap3_prm_voltsetup_match_tbl), +		.pm = &omap3_prm_dev_pm_ops,  		},  	.probe = omap3_prm_voltsetup_probe,  }; diff --git a/drivers/power/avs/omap_prm_voltsetup.h b/drivers/power/avs/omap_prm_voltsetup.h index 783dae7304d..75d709623cf 100644 --- a/drivers/power/avs/omap_prm_voltsetup.h +++ b/drivers/power/avs/omap_prm_voltsetup.h @@ -16,23 +16,14 @@   * 02111-1307, USA   */ -#ifndef _POWER_AVS_OMAP_VC_PRM_H -#define _POWER_AVS_OMAP_VC_PRM_H +#ifndef _POWER_OMAP_PRM_VOLTSETUP_H +#define _POWER_OMAP_PRM_VOLTSETUP_H  struct omap_pmic;  #if IS_ENABLED(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL) -int __init omap_glbl_prm_init(void); -void __exit omap_glbl_prm_exit(void);  int omap_prm_voltsetup(struct device *dev, struct omap_pmic *pmic, u32 uv);  #else -static inline int __init omap_glbl_prm_init(void) -{ -	return -ENODEV; -} -static inline void __exit omap_glbl_prm_exit(void) -{ -}  static inline int omap_prm_voltsetup(struct device *dev, struct omap_pmic *pmic,  				u32 uv)  { @@ -40,4 +31,4 @@ static inline int omap_prm_voltsetup(struct device *dev, struct omap_pmic *pmic,  }  #endif -#endif				/* _POWER_AVS_OMAP_VC_PRM_H */ +#endif				/* _POWER_OMAP_PRM_VOLTSETUP_H */ diff --git a/include/linux/power/omap_prm.h b/include/linux/power/omap_prm.h new file mode 100644 index 00000000000..7ce0dbabe3a --- /dev/null +++ b/include/linux/power/omap_prm.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 Motorola Mobility LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#ifndef _POWER_OMAP_PRM_H +#define _POWER_OMAP_PRM_H + +#if IS_ENABLED(CONFIG_POWER_TI_HARDWARE_VOLTAGE_CONTROL) +void omap_pm_enable_off_mode(void); +void omap_pm_disable_off_mode(void); +bool omap_pm_get_off_mode(void); +#else +static inline void omap_pm_enable_off_mode(void) +{ +} +static inline void omap_pm_disable_off_mode(void) +{ +} +static inline bool omap_pm_get_off_mode(void) +{ +	return false; +} +#endif + +#endif				/* _POWER_OMAP_PRM_H */  |