diff options
author | Nitin Garg <nitin.garg@freescale.com> | 2012-03-06 15:49:27 -0600 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2012-03-06 15:49:27 -0600 |
commit | bf6e9fd7d957c8e4677b226c6403d14328d2ff0c (patch) | |
tree | 77813fa95f3413e1306a593deaf16d4c904162f2 | |
parent | 90841a2089d500c8ce1405b720349214093ab292 (diff) |
ENGR00175602-2: Suspend/Resume: Alarm cannot wake up system from suspend mode
Alarm cannot wakeup the system from suspend mode due to missing da9053
PMIC fixups.
Signed-off-by: Nitin Garg <nitin.garg@freescale.com>
-rw-r--r-- | arch/arm/configs/imx5_android_defconfig | 32 | ||||
-rw-r--r-- | arch/arm/mach-mx5/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-mx5/board-mx53_smd.c | 34 | ||||
-rw-r--r-- | arch/arm/mach-mx5/pm.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-mx5/pm_da9053.c | 232 | ||||
-rw-r--r-- | arch/arm/mach-mx5/pm_i2c.c | 257 | ||||
-rw-r--r-- | arch/arm/mach-mx5/pmic.h | 30 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/mxc.h | 3 |
8 files changed, 574 insertions, 22 deletions
diff --git a/arch/arm/configs/imx5_android_defconfig b/arch/arm/configs/imx5_android_defconfig index 5937c525a472..3de391a4f1f9 100644 --- a/arch/arm/configs/imx5_android_defconfig +++ b/arch/arm/configs/imx5_android_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux/arm 2.6.38 Kernel Configuration -# Tue Feb 14 15:31:13 2012 +# Mon Mar 5 13:52:19 2012 # CONFIG_ARM=y CONFIG_HAVE_PWM=y @@ -81,19 +81,23 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_CGROUPS=y CONFIG_CGROUP_DEBUG=y -# CONFIG_CGROUP_NS is not set +CONFIG_CGROUP_NS=y CONFIG_CGROUP_FREEZER=y -# CONFIG_CGROUP_DEVICE is not set +CONFIG_CGROUP_DEVICE=y # CONFIG_CPUSETS is not set CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y -# CONFIG_CGROUP_MEM_RES_CTLR is not set +CONFIG_CGROUP_MEM_RES_CTLR=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y +CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y -CONFIG_RT_GROUP_SCHED=y -# CONFIG_BLK_CGROUP is not set +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set # CONFIG_NAMESPACES is not set -# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_SCHED_AUTOGROUP=y +CONFIG_MM_OWNER=y # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -103,7 +107,7 @@ CONFIG_RD_GZIP=y # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y CONFIG_ANON_INODES=y CONFIG_PANIC_TIMEOUT=0 @@ -159,13 +163,14 @@ CONFIG_BASE_SMALL=0 CONFIG_MODULES=y # CONFIG_MODULE_FORCE_LOAD is not set CONFIG_MODULE_UNLOAD=y -# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_BLOCK=y CONFIG_LBDAF=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_THROTTLING is not set # # IO Schedulers @@ -173,6 +178,7 @@ CONFIG_LBDAF=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y +# CONFIG_CFQ_GROUP_IOSCHED is not set # CONFIG_DEFAULT_DEADLINE is not set CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_NOOP is not set @@ -1206,7 +1212,7 @@ CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_KEYBOARD=y # CONFIG_KEYBOARD_ADP5588 is not set -CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_ATKBD is not set # CONFIG_KEYBOARD_QT2160 is not set # CONFIG_KEYBOARD_LKKBD is not set CONFIG_KEYBOARD_GPIO=y @@ -1885,10 +1891,8 @@ CONFIG_SND_PCM_OSS_PLUGINS=y # CONFIG_SND_DYNAMIC_MINORS is not set CONFIG_SND_SUPPORT_OLD_API=y CONFIG_SND_VERBOSE_PROCFS=y -CONFIG_SND_VERBOSE_PRINTK=y -CONFIG_SND_DEBUG=y -CONFIG_SND_DEBUG_VERBOSE=y -# CONFIG_SND_PCM_XRUN_DEBUG is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set # CONFIG_SND_RAWMIDI_SEQ is not set # CONFIG_SND_OPL3_LIB_SEQ is not set # CONFIG_SND_OPL4_LIB_SEQ is not set diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile index 191cc0d7a3cc..5f645c5ef436 100644 --- a/arch/arm/mach-mx5/Makefile +++ b/arch/arm/mach-mx5/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_SOC_IMX53) += cpu_op-mx51.o cpu_op-mx53.o cpu_op-mx50.o obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o mx51_babbage_pmic_mc13892.o obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o obj-$(CONFIG_MACH_MX53_EVK) += board-mx53_evk.o -obj-$(CONFIG_MACH_MX53_SMD) += board-mx53_smd.o mx53_smd_pmic_da9053.o +obj-$(CONFIG_MACH_MX53_SMD) += board-mx53_smd.o pm_i2c.o pm_da9053.o mx53_smd_pmic_da9053.o obj-$(CONFIG_MACH_IMX_BLUETOOTH_RFKILL) += imx_bt_rfkill.o obj-$(CONFIG_MACH_MX53_LOCO) += board-mx53_loco.o mx53_loco_pmic_da9053.o mx53_loco_pmic_mc34708.o obj-$(CONFIG_MACH_MX53_ARD) += board-mx53_ard.o diff --git a/arch/arm/mach-mx5/board-mx53_smd.c b/arch/arm/mach-mx5/board-mx53_smd.c index ec509898d368..f3f7354151f5 100644 --- a/arch/arm/mach-mx5/board-mx53_smd.c +++ b/arch/arm/mach-mx5/board-mx53_smd.c @@ -62,6 +62,7 @@ #include "devices.h" #include "usb.h" #include "android.h" +#include "pmic.h" /* MX53 SMD GPIO PIN configurations */ #define MX53_SMD_KEY_RESET IMX_GPIO_NR(1, 2) @@ -135,6 +136,12 @@ #define MX53_SMD_PMIC_INT IMX_GPIO_NR(7, 11) #define MX53_SMD_CAP_TCH_FUN1 IMX_GPIO_NR(7, 13) +#define TZIC_WAKEUP0_OFFSET 0x0E00 +#define TZIC_WAKEUP1_OFFSET 0x0E04 +#define TZIC_WAKEUP2_OFFSET 0x0E08 +#define TZIC_WAKEUP3_OFFSET 0x0E0C +#define GPIO7_0_11_IRQ_BIT (0x1<<11) + void __init early_console_setup(unsigned long base, struct clk *clk); static struct clk *sata_clk, *sata_ref_clk; static int fs_in_sdcard; @@ -487,12 +494,38 @@ static const struct imxi2c_platform_data mx53_smd_i2c_data __initconst = { .bitrate = 100000, }; +extern void __iomem *tzic_base; +static void smd_da9053_irq_wakeup_only_fixup(void) +{ + if (NULL == tzic_base) { + pr_err("fail to map MX53_TZIC_BASE_ADDR\n"); + return; + } + __raw_writel(0, tzic_base + TZIC_WAKEUP0_OFFSET); + __raw_writel(0, tzic_base + TZIC_WAKEUP1_OFFSET); + __raw_writel(0, tzic_base + TZIC_WAKEUP2_OFFSET); + /* only enable irq wakeup for da9053 */ + __raw_writel(GPIO7_0_11_IRQ_BIT, tzic_base + TZIC_WAKEUP3_OFFSET); + pr_info("only da9053 irq is wakeup-enabled\n"); +} + static void smd_suspend_enter(void) { + if (board_is_rev(IMX_BOARD_REV_4)) { + smd_da9053_irq_wakeup_only_fixup(); + da9053_suspend_cmd_sw(); + } else { + if (da9053_get_chip_version() != DA9053_VERSION_BB) + smd_da9053_irq_wakeup_only_fixup(); + + da9053_suspend_cmd_hw(); + } } static void smd_suspend_exit(void) { + if (da9053_get_chip_version()) + da9053_restore_volt_settings(); } static struct mxc_pm_platform_data smd_pm_data = { @@ -1272,6 +1305,7 @@ static void __init mx53_smd_board_init(void) */ imx53_add_mxc_scc2(); smd_add_device_battery(); + pm_i2c_init(MX53_I2C1_BASE_ADDR); imx53_add_dvfs_core(&smd_dvfs_core_data); imx53_add_busfreq(); diff --git a/arch/arm/mach-mx5/pm.c b/arch/arm/mach-mx5/pm.c index 6968cef0d7c7..be466c4bed3e 100644 --- a/arch/arm/mach-mx5/pm.c +++ b/arch/arm/mach-mx5/pm.c @@ -73,12 +73,6 @@ void *suspend_iram_base; void (*suspend_in_iram)(void *param1, void *param2, void* param3) = NULL; void __iomem *suspend_param1; -#define TZIC_WAKEUP0_OFFSET 0x0E00 -#define TZIC_WAKEUP1_OFFSET 0x0E04 -#define TZIC_WAKEUP2_OFFSET 0x0E08 -#define TZIC_WAKEUP3_OFFSET 0x0E0C -#define GPIO7_0_11_IRQ_BIT (0x1<<11) - static int mx5_suspend_enter(suspend_state_t state) { if (gpc_dvfs_clk == NULL) diff --git a/arch/arm/mach-mx5/pm_da9053.c b/arch/arm/mach-mx5/pm_da9053.c new file mode 100644 index 000000000000..1313aa78d8c3 --- /dev/null +++ b/arch/arm/mach-mx5/pm_da9053.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/mfd/da9052/reg.h> + +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <mach/i2c.h> +#include "pmic.h" + +/** Defines ******************************************************************** +*******************************************************************************/ +/* have to hard-code the preset voltage here for they share the register +as the normal setting on Da9053 */ +/* preset buck core to 850 mv */ +#define BUCKCORE_SUSPEND_PRESET 0xCE +/* preset buck core to 950 mv */ +#define BUCKPRO_SUSPEND_PRESET 0xD2 +/* preset ldo6 to 1200 mv */ +#define LDO6_SUSPEND_PRESET 0xC0 +/* preset ldo10 to 1200 mv */ +#define iLDO10_SUSPEND_PRESET 0xC0 +/* set VUSB 2V5 active during suspend */ +#define BUCKPERI_SUSPEND_SW_STEP 0x50 +/* restore VUSB 2V5 active after suspend */ +#define BUCKPERI_RESTORE_SW_STEP 0x55 +/* restore VUSB 2V5 power supply after suspend */ +#define SUPPLY_RESTORE_VPERISW_EN 0x20 +#define CONF_BIT 0x80 + +#define DA9053_SLEEP_DELAY 0x1f +#define DA9052_CONTROLC_SMD_SET 0x62 +#define DA9052_GPIO0809_SMD_SET 0x18 +#define DA9052_ID1415_SMD_SET 0x1 +#define DA9052_GPI9_IRQ_MASK 0x2 +#define DA9052_ALARM_IRQ_EN (0x1<<6) +#define DA9052_SEQ_RDY_IRQ_MASK (0x1<<6) + +static u8 volt_settings[DA9052_LDO10_REG - DA9052_BUCKCORE_REG + 1]; + +static void pm_da9053_read_reg(u8 reg, u8 *value) +{ + unsigned char buf[2] = {0, 0}; + struct i2c_msg i2cmsg[2]; + buf[0] = reg; + i2cmsg[0].addr = 0x48 ; + i2cmsg[0].len = 1; + i2cmsg[0].buf = &buf[0]; + + i2cmsg[0].flags = 0; + + i2cmsg[1].addr = 0x48 ; + i2cmsg[1].len = 1; + i2cmsg[1].buf = &buf[1]; + + i2cmsg[1].flags = I2C_M_RD; + + pm_i2c_imx_xfer(i2cmsg, 2); + *value = buf[1]; +} + +static void pm_da9053_write_reg(u8 reg, u8 value) +{ + unsigned char buf[2] = {0, 0}; + struct i2c_msg i2cmsg[2]; + buf[0] = reg; + buf[1] = value; + i2cmsg[0].addr = 0x48 ; + i2cmsg[0].len = 2; + i2cmsg[0].buf = &buf[0]; + i2cmsg[0].flags = 0; + pm_i2c_imx_xfer(i2cmsg, 1); +} + +static void pm_da9053_preset_voltage(void) +{ + u8 reg, data; + for (reg = DA9052_BUCKCORE_REG; + reg <= DA9052_LDO10_REG; reg++) { + pm_da9053_read_reg(reg, &data); + volt_settings[reg - DA9052_BUCKCORE_REG] = data; + data |= CONF_BIT; + pm_da9053_write_reg(reg, data); + } + pm_da9053_write_reg(DA9052_BUCKCORE_REG, BUCKCORE_SUSPEND_PRESET); + pm_da9053_write_reg(DA9052_BUCKPRO_REG, BUCKPRO_SUSPEND_PRESET); + pm_da9053_write_reg(DA9052_LDO6_REG, LDO6_SUSPEND_PRESET); + pm_da9053_write_reg(DA9052_LDO10_REG, iLDO10_SUSPEND_PRESET); + pm_da9053_write_reg(DA9052_ID1213_REG, BUCKPERI_SUSPEND_SW_STEP); +} + +#if 0 +static void pm_da9053_dump(int start, int end) +{ + u8 reg, data; + for (reg = start; reg <= end; reg++) { + pm_da9053_read_reg(reg, &data); + pr_info("reg %u = 0x%2x\n", + reg, data); + } +} +#endif + +int da9053_suspend_cmd_sw(void) +{ + unsigned char buf[2] = {0, 0}; + struct clk *i2c_clk; + u8 data; + buf[0] = 29; + + i2c_clk = clk_get(NULL, "i2c_clk"); + if (IS_ERR(i2c_clk)) { + pr_err("unable to get i2c clk\n"); + return PTR_ERR(i2c_clk); + } + clk_enable(i2c_clk); + + pm_da9053_preset_voltage(); + + pm_da9053_read_reg(DA9052_ID01_REG, &data); + data &= ~(DA9052_ID01_DEFSUPPLY | DA9052_ID01_nRESMODE); + pm_da9053_write_reg(DA9052_ID01_REG, data); + + pm_da9053_write_reg(DA9052_SEQB_REG, DA9053_SLEEP_DELAY); + + pm_da9053_read_reg(DA9052_CONTROLB_REG, &data); + data |= DA9052_CONTROLB_DEEPSLEEP; + pm_da9053_write_reg(DA9052_CONTROLB_REG, data); + + clk_disable(i2c_clk); + clk_put(i2c_clk); + return 0; +} + +int da9053_suspend_cmd_hw(void) +{ + unsigned char buf[2] = {0, 0}; + struct clk *i2c_clk; + u8 data; + buf[0] = 29; + + i2c_clk = clk_get(NULL, "i2c_clk"); + if (IS_ERR(i2c_clk)) { + pr_err("unable to get i2c clk\n"); + return PTR_ERR(i2c_clk); + } + clk_enable(i2c_clk); + + pm_da9053_preset_voltage(); + pm_da9053_write_reg(DA9052_CONTROLC_REG, + DA9052_CONTROLC_SMD_SET); + + pm_da9053_read_reg(DA9052_ID01_REG, &data); + data &= ~(DA9052_ID01_DEFSUPPLY | DA9052_ID01_nRESMODE); + pm_da9053_write_reg(DA9052_ID01_REG, data); + + pm_da9053_write_reg(DA9052_GPIO0809_REG, + DA9052_GPIO0809_SMD_SET); + pm_da9053_read_reg(DA9052_IRQMASKD_REG, &data); + data |= DA9052_GPI9_IRQ_MASK; + pm_da9053_write_reg(DA9052_IRQMASKD_REG, data); +#ifdef CONFIG_RTC_DRV_DA9052 + pm_da9053_read_reg(DA9052_ALARMY_REG, &data); + data |= DA9052_ALARM_IRQ_EN; + pm_da9053_write_reg(DA9052_ALARMY_REG, data); +#endif + /* Mask SEQ_RDY_IRQ to avoid some suspend/resume issues */ + pm_da9053_read_reg(DA9052_IRQMASKA_REG, &data); + data |= DA9052_SEQ_RDY_IRQ_MASK; + pm_da9053_write_reg(DA9052_IRQMASKA_REG, data); + + pm_da9053_read_reg(DA9052_ID1415_REG, &data); + data &= 0xf0; + data |= DA9052_ID1415_SMD_SET; + pm_da9053_write_reg(DA9052_ID1415_REG, data); + + pm_da9053_write_reg(DA9052_SEQTIMER_REG, 0); + /* pm_da9053_write_reg(DA9052_SEQB_REG, 0x1f); */ + + clk_disable(i2c_clk); + clk_put(i2c_clk); + return 0; +} + +int da9053_restore_volt_settings(void) +{ + u8 data; + struct clk *i2c_clk; + + i2c_clk = clk_get(NULL, "i2c_clk"); + if (IS_ERR(i2c_clk)) { + pr_err("unable to get i2c clk\n"); + return PTR_ERR(i2c_clk); + } + clk_enable(i2c_clk); + + pm_da9053_write_reg(DA9052_ID1213_REG, BUCKPERI_RESTORE_SW_STEP); + pm_da9053_read_reg(DA9052_SUPPLY_REG, &data); + data |= SUPPLY_RESTORE_VPERISW_EN; + pm_da9053_write_reg(DA9052_SUPPLY_REG, data); + + clk_disable(i2c_clk); + clk_put(i2c_clk); + return 0; +} diff --git a/arch/arm/mach-mx5/pm_i2c.c b/arch/arm/mach-mx5/pm_i2c.c new file mode 100644 index 000000000000..0b0525aa5aca --- /dev/null +++ b/arch/arm/mach-mx5/pm_i2c.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/clk.h> + +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <mach/i2c.h> + +/** Defines ******************************************************************** +*******************************************************************************/ + + +/* IMX I2C registers */ +#define IMX_I2C_IADR 0x00 /* i2c slave address */ +#define IMX_I2C_IFDR 0x04 /* i2c frequency divider */ +#define IMX_I2C_I2CR 0x08 /* i2c control */ +#define IMX_I2C_I2SR 0x0C /* i2c status */ +#define IMX_I2C_I2DR 0x10 /* i2c transfer data */ + +/* Bits of IMX I2C registers */ +#define I2SR_RXAK 0x01 +#define I2SR_IIF 0x02 +#define I2SR_SRW 0x04 +#define I2SR_IAL 0x10 +#define I2SR_IBB 0x20 +#define I2SR_IAAS 0x40 +#define I2SR_ICF 0x80 +#define I2CR_RSTA 0x04 +#define I2CR_TXAK 0x08 +#define I2CR_MTX 0x10 +#define I2CR_MSTA 0x20 +#define I2CR_IIEN 0x40 +#define I2CR_IEN 0x80 + +static void __iomem *base; +static int stopped; + +/** Functions for IMX I2C adapter driver *************************************** +*******************************************************************************/ + +static int pm_i2c_imx_bus_busy(int for_busy) +{ + unsigned int temp; + + while (1) { + temp = readb(base + IMX_I2C_I2SR); + if (for_busy && (temp & I2SR_IBB)) + break; + if (!for_busy && !(temp & I2SR_IBB)) + break; + pr_debug("waiting bus busy=%d\n", for_busy); + } + + return 0; +} + +static int pm_i2c_imx_trx_complete(void) +{ + unsigned int temp; + while (!((temp = readb(base + IMX_I2C_I2SR)) & I2SR_IIF)) + pr_debug("waiting or I2SR_IIF\n"); + temp &= ~I2SR_IIF; + writeb(temp, base + IMX_I2C_I2SR); + + return 0; +} + +static int pm_i2c_imx_acked(void) +{ + if (readb(base + IMX_I2C_I2SR) & I2SR_RXAK) { + pr_info("<%s> No ACK\n", __func__); + return -EIO; /* No ACK */ + } + return 0; +} + +static int pm_i2c_imx_start(void) +{ + unsigned int temp = 0; + int result; + + /* Enable I2C controller */ + writeb(0, base + IMX_I2C_I2SR); + writeb(I2CR_IEN, base + IMX_I2C_I2CR); + + /* Wait controller to be stable */ + udelay(50); + + /* Start I2C transaction */ + temp = readb(base + IMX_I2C_I2CR); + temp |= I2CR_MSTA; + writeb(temp, base + IMX_I2C_I2CR); + result = pm_i2c_imx_bus_busy(1); + + temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; + writeb(temp, base + IMX_I2C_I2CR); + return result; +} + +static void pm_i2c_imx_stop(void) +{ + unsigned int temp = 0; + + /* Stop I2C transaction */ + temp = readb(base + IMX_I2C_I2CR); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, base + IMX_I2C_I2CR); + + pm_i2c_imx_bus_busy(0); + + /* Disable I2C controller */ + writeb(0, base + IMX_I2C_I2CR); +} + +static int pm_i2c_imx_write(struct i2c_msg *msgs) +{ + int i, result; + + /* write slave address */ + writeb(msgs->addr << 1, base + IMX_I2C_I2DR); + result = pm_i2c_imx_trx_complete(); + if (result) + return result; + result = pm_i2c_imx_acked(); + if (result) + return result; + + /* write data */ + for (i = 0; i < msgs->len; i++) { + writeb(msgs->buf[i], base + IMX_I2C_I2DR); + result = pm_i2c_imx_trx_complete(); + if (result) + return result; + result = pm_i2c_imx_acked(); + if (result) + return result; + } + return 0; +} + +static int pm_i2c_imx_read(struct i2c_msg *msgs) +{ + int i, result; + unsigned int temp; + + /* write slave address */ + writeb((msgs->addr << 1) | 0x01, base + IMX_I2C_I2DR); + result = pm_i2c_imx_trx_complete(); + if (result) + return result; + result = pm_i2c_imx_acked(); + if (result) + return result; + + /* setup bus to read data */ + temp = readb(base + IMX_I2C_I2CR); + temp &= ~I2CR_MTX; + if (msgs->len - 1) + temp &= ~I2CR_TXAK; + writeb(temp, base + IMX_I2C_I2CR); + readb(base + IMX_I2C_I2DR); /* dummy read */ + + /* read data */ + for (i = 0; i < msgs->len; i++) { + result = pm_i2c_imx_trx_complete(); + if (result) + return result; + if (i == (msgs->len - 1)) { + /* It must generate STOP before read I2DR to prevent + controller from generating another clock cycle */ + temp = readb(base + IMX_I2C_I2CR); + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, base + IMX_I2C_I2CR); + pm_i2c_imx_bus_busy(0); + stopped = 1; + } else if (i == (msgs->len - 2)) { + temp = readb(base + IMX_I2C_I2CR); + temp |= I2CR_TXAK; + writeb(temp, base + IMX_I2C_I2CR); + } + msgs->buf[i] = readb(base + IMX_I2C_I2DR); + } + return 0; +} + +int pm_i2c_imx_xfer(struct i2c_msg *msgs, int num) +{ + unsigned int i, temp; + int result; + + /* Start I2C transfer */ + result = pm_i2c_imx_start(); + if (result) + goto fail0; + + /* read/write data */ + for (i = 0; i < num; i++) { + if (i) { + temp = readb(base + IMX_I2C_I2CR); + temp |= I2CR_RSTA; + writeb(temp, base + IMX_I2C_I2CR); + result = pm_i2c_imx_bus_busy(1); + if (result) + goto fail0; + } + /* write/read data */ + if (msgs[i].flags & I2C_M_RD) + result = pm_i2c_imx_read(&msgs[i]); + else + result = pm_i2c_imx_write(&msgs[i]); + if (result) + goto fail0; + } + +fail0: + /* Stop I2C transfer */ + pm_i2c_imx_stop(); + + return (result < 0) ? result : num; +} + +void pm_i2c_init(u32 base_addr) +{ + base = ioremap(base_addr, SZ_4K); +} + +void pm_i2c_deinit(void) +{ + iounmap(base); +} diff --git a/arch/arm/mach-mx5/pmic.h b/arch/arm/mach-mx5/pmic.h new file mode 100644 index 000000000000..72d7f6a42fe3 --- /dev/null +++ b/arch/arm/mach-mx5/pmic.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __ASM_ARCH_MACH_PMIC_H__ +#define __ASM_ARCH_MACH_PMIC_H__ + +extern int __init mx53_loco_init_da9052(void); +extern int __init mx53_loco_init_mc34708(void); +extern int da9053_suspend_cmd_sw(void); +extern int da9053_suspend_cmd_hw(void); +extern int da9053_restore_volt_settings(void); +extern void pm_i2c_init(u32 base_addr); +extern int pm_i2c_imx_xfer(struct i2c_msg *msgs, int num); + +#endif diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h index b8ad52fac513..112860d7c0b8 100644 --- a/arch/arm/plat-mxc/include/mach/mxc.h +++ b/arch/arm/plat-mxc/include/mach/mxc.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2007, 2011 Freescale Semiconductor, Inc. + * Copyright 2004-2012 Freescale Semiconductor, Inc. * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) * * This program is free software; you can redistribute it and/or @@ -70,6 +70,7 @@ #define IMX_BOARD_REV_2 0x100 #define IMX_BOARD_REV_3 0x200 #define IMX_BOARD_REV_4 0x300 +#define IMX_BOARD_REV_5 0x400 #ifndef __ASSEMBLY__ extern unsigned int system_rev; |