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
@@ -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 */
@@ -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
new file mode 100644
@@ -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, ®val, 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, ®_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, ®_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