From patchwork Mon Mar 21 14:14:08 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tasslehoff Kjappfot X-Patchwork-Id: 648031 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p2LEEBjl023088 for ; Mon, 21 Mar 2011 14:14:12 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753194Ab1CUOOK (ORCPT ); Mon, 21 Mar 2011 10:14:10 -0400 Received: from mail-ey0-f174.google.com ([209.85.215.174]:33798 "EHLO mail-ey0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752927Ab1CUOOJ (ORCPT ); Mon, 21 Mar 2011 10:14:09 -0400 Received: by eyx24 with SMTP id 24so1516686eyx.19 for ; Mon, 21 Mar 2011 07:14:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:message-id:date:from:user-agent:mime-version:to :subject:content-type; bh=KhCaquyRxvpQ7GAcTNJRziEDyjjl03rc6Os2bXbRm+8=; b=IivNU4pqgeut+A7X4PdLKiPzTHk2p6mJSVyYTLe4YS6ug3bVWPb2pCCVN5ph7u3C3h BsIV0B/tlyxvnmmtS5jf6lbDmNIcalSic7VcS6DCUMB9ZzX4RaXrwXYo8XWTYexjFpO7 RT3/kDZ0m5BFM0ufn+s4TO6Q1HPO20s8VYJQ8= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:subject :content-type; b=bCcLy1pKMu3B2IuC0iHGF+4bu5vjvf1Rh4GaedFokZIFjLmn4WzpafcRH+oaIQde02 VHfi1yiNeMAUjCNgQfG4PbQA033tIf2YRZAy/vT1tCY5Txf11l+w3gR92i0q2140FMjX SJRyvm219h9tPTZiI527bSc7yRK4xiHadIJ0A= Received: by 10.213.20.142 with SMTP id f14mr2055257ebb.15.1300716846678; Mon, 21 Mar 2011 07:14:06 -0700 (PDT) Received: from [192.168.0.40] (147.84-49-231.nextgentel.com [84.49.231.147]) by mx.google.com with ESMTPS id u45sm1989194eeh.16.2011.03.21.07.14.04 (version=SSLv3 cipher=OTHER); Mon, 21 Mar 2011 07:14:05 -0700 (PDT) Message-ID: <4D875D30.7090809@gmail.com> Date: Mon, 21 Mar 2011 15:14:08 +0100 From: Tasslehoff Kjappfot User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.15pre) Gecko/20110207 Lightning/1.0b2 Shredder/3.1.9pre MIME-Version: 1.0 To: linux-omap@vger.kernel.org Subject: Problems with powermanagement (suspend/wake/shutdown) on OMAP3530. Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Mon, 21 Mar 2011 14:14:12 +0000 (UTC) From c8d51a054bc5c6f926846aa8b1e61bc0c705cb83 Mon Sep 17 00:00:00 2001 From: tasslehoff 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 + * + * 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 +#include +#include +#ifdef CONFIG_MACH_ARCHOS +#include +#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