summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Tsunaev <vladimirt@motorola.com>2014-05-09 13:21:25 -0400
committerJim Wylder <jwylder@motorola.com>2014-05-12 17:31:21 -0500
commita1f7d43b50b0b363463862b6e85dde7cc67bfccf (patch)
tree6521e733ac2ce4ff72be01045eaa129e75d28097
parent09f2540f8b92c98f0c8a288ea270051070527b69 (diff)
downloadolio-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.txt20
-rw-r--r--drivers/power/avs/omap3_prm_voltsetup.c248
-rw-r--r--drivers/power/avs/omap_prm_voltsetup.h15
-rw-r--r--include/linux/power/omap_prm.h39
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 */