diff mbox

Problems with powermanagement (suspend/wake/shutdown) on OMAP3530.

Message ID 4D875D30.7090809@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Tasslehoff Kjappfot March 21, 2011, 2:14 p.m. UTC
None
diff mbox

Patch

From c8d51a054bc5c6f926846aa8b1e61bc0c705cb83 Mon Sep 17 00:00:00 2001
From: tasslehoff <tasskjapp@gmail.com>
Date: Tue, 15 Mar 2011 15:33:33 +0100
Subject: [PATCH] Add powerscript and i2c driver for twl4030_poweroff.

---
 arch/arm/mach-omap2/board-omap3beagle.c |  109 +++++++++++++++++
 drivers/i2c/chips/Makefile              |    1 +
 drivers/i2c/chips/twl4030-poweroff.c    |  200 +++++++++++++++++++++++++++++++
 3 files changed, 310 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/chips/twl4030-poweroff.c

diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 6dc7b23..fd247d1 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -404,6 +404,105 @@  static int beagle_twl_gpio_setup(struct device *dev,
 	return 0;
 }
 
+
+static struct twl4030_ins __initdata sleep_on_seq[] = {
+	/* Turn OFF VAUX2 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VAUX2, RES_STATE_OFF), 2},
+	/* Turn off HFCLKOUT */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_HFCLKOUT, RES_STATE_OFF), 2},
+	/* Turn OFF VDD1 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VDD1, RES_STATE_OFF), 2},
+	/* Turn OFF VDD2 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VDD2, RES_STATE_OFF), 2},
+	/* Turn OFF VPLL1 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VPLL1, RES_STATE_OFF), 2},
+};
+
+static struct twl4030_script sleep_on_script __initdata = {
+	.script	= sleep_on_seq,
+	.size	= ARRAY_SIZE(sleep_on_seq),
+	.flags	= TWL4030_SLEEP_SCRIPT,
+};
+
+static struct twl4030_ins wakeup_p12_seq[] __initdata = {
+	/* Turn on VAUX2 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VAUX2, RES_STATE_ACTIVE), 2},
+	/* Turn on HFCLKOUT */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_HFCLKOUT, RES_STATE_ACTIVE), 2},
+	/* Turn ON VDD1 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VDD1, RES_STATE_ACTIVE), 2},
+	/* Turn ON VDD2 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VDD2, RES_STATE_ACTIVE), 2},
+	/* Turn ON VPLL1 */
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VPLL1, RES_STATE_ACTIVE), 2},
+};
+
+static struct twl4030_script wakeup_p12_script __initdata = {
+	.script	= wakeup_p12_seq,
+	.size	= ARRAY_SIZE(wakeup_p12_seq),
+	.flags	= TWL4030_WAKEUP12_SCRIPT,
+};
+
+static struct twl4030_ins wakeup_p3_seq[] __initdata = {
+	{MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 2},
+};
+
+static struct twl4030_script wakeup_p3_script __initdata = {
+	.script = wakeup_p3_seq,
+	.size   = ARRAY_SIZE(wakeup_p3_seq),
+	.flags  = TWL4030_WAKEUP3_SCRIPT,
+};
+
+static struct twl4030_ins wrst_seq[] __initdata = {
+/*
+ * Reset twl4030.
+ * Reset VDD1 regulator.
+ * Reset VDD2 regulator.
+ * Reset VPLL1 regulator.
+ * Enable sysclk output.
+ * Reenable twl4030.
+ */
+	{MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_OFF), 2},
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VDD1, RES_STATE_WRST), 15},
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VDD2, RES_STATE_WRST), 15},
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VPLL1, RES_STATE_WRST), 0x60},
+	{MSG_SINGULAR(DEV_GRP_P1, RES_HFCLKOUT, RES_STATE_ACTIVE), 2},
+	{MSG_SINGULAR(DEV_GRP_P1, RES_VAUX2, RES_STATE_ACTIVE), 2},
+	{MSG_SINGULAR(DEV_GRP_NULL, RES_RESET, RES_STATE_ACTIVE), 2},
+};
+
+static struct twl4030_script wrst_script __initdata = {
+	.script = wrst_seq,
+	.size   = ARRAY_SIZE(wrst_seq),
+	.flags  = TWL4030_WRST_SCRIPT,
+};
+
+static struct twl4030_script *twl4030_scripts[] __initdata = {
+	&sleep_on_script,
+	&wakeup_p12_script,
+	&wakeup_p3_script,
+	&wrst_script,
+};
+
+static struct twl4030_resconfig twl4030_rconfig[] = {
+	{ .resource = RES_HFCLKOUT, .devgroup = DEV_GRP_P3, .type = -1,
+		.type2 = -1 },
+/* XXX removed, breaks booting after power-off
+	{ .resource = RES_VDD1, .devgroup = DEV_GRP_P1, .type = -1,
+		.type2 = -1 },
+	{ .resource = RES_VDD2, .devgroup = DEV_GRP_P1, .type = -1,
+		.type2 = -1 },
+*/
+	{ 0, 0},
+};
+
+static struct twl4030_power_data beagle_power_data __initdata = {
+	.scripts	= twl4030_scripts,
+	.num		= ARRAY_SIZE(twl4030_scripts),
+	.resource_config = twl4030_rconfig,
+};
+
+
 static struct twl4030_gpio_platform_data beagle_gpio_data = {
 	.gpio_base	= OMAP_MAX_GPIO_LINES,
 	.irq_base	= TWL4030_GPIO_IRQ_BASE,
@@ -544,6 +643,7 @@  static struct twl4030_platform_data beagle_twldata = {
 	/* platform_data for children goes here */
 	.usb		= &beagle_usb_data,
 	.gpio		= &beagle_gpio_data,
+        .power          = &beagle_power_data,
 	.codec		= &beagle_codec_data,
 	.madc		= &beagle_madc_data,
 	.vmmc1		= &beagle_vmmc1,
@@ -799,6 +899,13 @@  static struct ehci_hcd_omap_platform_data ehci_pdata __initdata = {
 	.reset_gpio_port[2]  = -EINVAL
 };
 
+static void enable_board_wakeup_source(void)
+{
+	/* T2 interrupt line (keypad) */
+	omap_mux_init_signal("sys_nirq",
+		OMAP_WAKEUP_EN | OMAP_PIN_INPUT_PULLUP);
+}
+
 #ifdef CONFIG_OMAP_MUX
 static struct omap_board_mux board_mux[] __initdata = {
 	/* Camera - Parallel Data */
@@ -925,6 +1032,8 @@  static void __init omap3_beagle_init(void)
 
 	usb_musb_init();
 	usb_ehci_init(&ehci_pdata);
+	enable_board_wakeup_source();
+
 	omap3beagle_flash_init();
 
 	/* Ensure SDRC pins are mux'd for self-refresh */
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index fe0af0f..d085c59 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -11,6 +11,7 @@ 
 #
 
 obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o
+obj-$(CONFIG_TWL4030_POWER)     += twl4030-poweroff.o
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/chips/twl4030-poweroff.c b/drivers/i2c/chips/twl4030-poweroff.c
new file mode 100644
index 0000000..dbbba72
--- /dev/null
+++ b/drivers/i2c/chips/twl4030-poweroff.c
@@ -0,0 +1,200 @@ 
+/*
+ * linux/drivers/i2c/chips/twl4030_poweroff.c
+ *
+ * Power off device
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/i2c/twl.h>
+#ifdef CONFIG_MACH_ARCHOS
+#include <mach/board-archos.h>
+#endif
+
+#define PWR_P1_SW_EVENTS	0x10
+#define PWR_P2_SW_EVENTS	0x11
+#define PWR_P3_SW_EVENTS	0x13
+#define PWR_DEVOFF		(1<<0)
+#define PWR_STOPON_POWERON 	(1<<6)
+#define PWR_LVL_WAKEUP		(1<<3)
+
+#define PWR_CFG_P1_TRANSITION	0x00
+#define PWR_CFG_P2_TRANSITION	0x01
+#define PWR_CFG_P3_TRANSITION	0x02
+#define PWR_CFG_P123_TRANSITION	0x03
+
+#define SEQ_OFFSYNC	(1<<0)
+#define STARTON_PWRON		0x01
+#define STARTON_VBUS		0x20
+
+#define R_PROTECT_KEY		0x0E
+#define KEY_1			0xFC
+#define KEY_2 			0x96
+
+#define R_VDD1_DEV_GRP		0x55
+#define R_VDD2_DEV_GRP		0x63
+
+static void twl_dump_power_regs(void)
+{
+	u8 i;
+	
+	for (i = 0; i < 0x25; i++) {
+		u8 regval;
+		twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &regval, i);
+		printk("PM_MASTER reg 0x%02x => %02x\n", i, regval);
+	}
+}
+
+static int unprotect_pm_master(void)
+{
+	int err;
+	
+	/* unlock registers for writing
+	 * FIXME: should this sequence be protected with a spin lock?
+	 */
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_1,
+				R_PROTECT_KEY);
+	err |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_2,
+				R_PROTECT_KEY);
+	if (err)
+		pr_warning("TWL4030 Unable to unlock registers\n");
+
+	return err;
+}
+
+static int protect_pm_master(void)
+{
+	int err;
+
+	/* lock registers again */
+	if ((err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY)) != 0)
+		printk(KERN_ERR
+			"TWL4030 Unable to relock registers\n");
+
+	return err;
+}
+
+static int twl_set_bits(u8 mod_no, u8 val, u8 reg)
+{
+	int err;
+	u8 uninitialized_var(reg_val);
+	
+	err = twl_i2c_read_u8(mod_no, &reg_val, reg);
+	if (err < 0)
+		return err;
+	
+	reg_val |= val;
+	err = twl_i2c_write_u8(mod_no, reg_val, reg);
+	
+	return err;
+}
+
+static int twl_clear_bits(u8 mod_no, u8 val, u8 reg)
+{
+	int err;
+	u8 uninitialized_var(reg_val);
+	
+	err = twl_i2c_read_u8(mod_no, &reg_val, reg);
+	if (err < 0)
+		return err;
+	
+	reg_val &= ~val;
+	err = twl_i2c_write_u8(mod_no, reg_val, reg);
+	
+	return err;	
+}
+
+static void twl4030_poweroff(void)
+{
+	u8 uninitialized_var(val);
+	int err;
+
+	unprotect_pm_master();	
+	/* Make sure SEQ_OFFSYNC is set so that all the res goes to wait-on */
+	err = twl_set_bits(TWL4030_MODULE_PM_MASTER, SEQ_OFFSYNC, 
+			PWR_CFG_P123_TRANSITION);	
+	protect_pm_master();
+
+	if (err < 0) {
+		pr_warning("I2C error %d while setting TWL4030 PM_MASTER CFG_P123_TRANSITION\n", err);
+		return;
+	}
+
+	err = twl_set_bits(TWL4030_MODULE_PM_MASTER, 
+			PWR_STOPON_POWERON | PWR_DEVOFF, PWR_P1_SW_EVENTS);
+	
+	if (err < 0) {
+		pr_warning("I2C error %d while writing TWL4030 PM_MASTER P1_SW_EVENTS\n", err);
+	}
+
+	return;
+}
+
+static int __init twl4030_poweroff_init(void)
+{
+	int err;
+	u8 starton_flags;
+	
+	unprotect_pm_master();
+	
+	starton_flags = STARTON_PWRON;
+	
+	/*if (!machine_has_usbhost_plug())
+          starton_flags |= STARTON_VBUS; */
+	
+	err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, starton_flags, 
+			PWR_CFG_P1_TRANSITION);
+	err |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, starton_flags, 
+			PWR_CFG_P2_TRANSITION);
+	err |= twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, starton_flags, 
+			PWR_CFG_P3_TRANSITION);
+	if (err)
+		pr_warning("TWL4030 Unable to configure STARTON transition\n");
+	
+	protect_pm_master();
+
+	err = twl_set_bits(TWL4030_MODULE_PM_MASTER, PWR_STOPON_POWERON, 
+			PWR_P1_SW_EVENTS);
+	err |= twl_set_bits(TWL4030_MODULE_PM_MASTER, PWR_STOPON_POWERON, 
+			PWR_P2_SW_EVENTS);
+	err |= twl_set_bits(TWL4030_MODULE_PM_MASTER, PWR_STOPON_POWERON,
+			PWR_P3_SW_EVENTS);
+	if (err) {
+		printk(KERN_WARNING "I2C error %d while writing TWL4030"
+					"PM_MASTER P1_SW_EVENTS\n", err);
+	}
+
+	pm_power_off = twl4030_poweroff;
+        printk(KERN_INFO "TWL4030 POWEROFF INIT");
+	return 0;
+}
+
+static void __exit twl4030_poweroff_exit(void)
+{
+	pm_power_off = NULL;
+}
+
+module_init(twl4030_poweroff_init);
+module_exit(twl4030_poweroff_exit);
+
+MODULE_ALIAS("i2c:twl4030-poweroff");
+MODULE_DESCRIPTION("Triton2 device power off");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter De Schrijver");
-- 
1.7.1