>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