diff options
| author | Vladimir Tsunaev <vladimirt@motorola.com> | 2014-05-09 13:21:25 -0400 |
|---|---|---|
| committer | Jim Wylder <jwylder@motorola.com> | 2014-05-12 17:31:21 -0500 |
| commit | a1f7d43b50b0b363463862b6e85dde7cc67bfccf (patch) | |
| tree | 6521e733ac2ce4ff72be01045eaa129e75d28097 | |
| parent | 09f2540f8b92c98f0c8a288ea270051070527b69 (diff) | |
| download | olio-linux-3.10-a1f7d43b50b0b363463862b6e85dde7cc67bfccf.tar.xz olio-linux-3.10-a1f7d43b50b0b363463862b6e85dde7cc67bfccf.zip | |
IKXCLOCK-1044 power: avs: enable OMAP offmode
Support off mode in prm driver
Change-Id: Ie795724c294552dceae997d8981b922f4473d879
Signed-off-by: Vladimir Tsunaev <vladimirt@motorola.com>
| -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 */ |