From: Robin Gong Date: Thu, 3 May 2012 10:11:06 +0000 (+0800) Subject: ENGR00181348-1 :sabresd pfuze support cpu internal LDO bypass X-Git-Tag: v3.0.35-fsl~1090 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=00c3d351f008aff5190bf6dc2ce2054f3c6bdf0d;p=karo-tx-linux.git ENGR00181348-1 :sabresd pfuze support cpu internal LDO bypass VDDCORE output directly from pfuze not internal anatop regulator,VDDCORE can be adjust by pfuze regulator with deifferent cpu frequency, these patch should be used with u-boot related patch, because LDO bypass is set on u-boot. u-boot and kernel can be configured by CONFIG_MX6_INTER_LDO_BYPASS, by default it is disabled, can be used on RevC. These code is put in arch/arm. Signed-off-by: Robin Gong --- diff --git a/arch/arm/mach-mx6/Kconfig b/arch/arm/mach-mx6/Kconfig index 1db924a0975e..c202f0c56612 100644 --- a/arch/arm/mach-mx6/Kconfig +++ b/arch/arm/mach-mx6/Kconfig @@ -172,4 +172,13 @@ config IMX_PCIE bool "PCI Express support" select PCI +config MX6_INTER_LDO_BYPASS + bool "Internal LDO in MX6Q/DL bypass" + depends on REGULATOR_PFUZE100 && CPU_FREQ_IMX && ARCH_MX6 + default n + help + This is choosed for bypass internal LDO in MX6. If choose it, internal + LDO will replaced by external pmic regulator(e.g. pfuze100), VDDCORE + can be adjust automatically adjust by cpu frequency. + endif diff --git a/arch/arm/mach-mx6/board-mx6q_sabresd.c b/arch/arm/mach-mx6/board-mx6q_sabresd.c index 37e4e284e3e8..2192abbd2814 100644 --- a/arch/arm/mach-mx6/board-mx6q_sabresd.c +++ b/arch/arm/mach-mx6/board-mx6q_sabresd.c @@ -1470,7 +1470,11 @@ static struct platform_pwm_backlight_data mx6_sabresd_pwm_backlight_data = { }; static struct mxc_dvfs_platform_data sabresd_dvfscore_data = { + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + .reg_id = "VDDCORE", + #else .reg_id = "cpu_vddgp", + #endif .clk1_id = "cpu_clk", .clk2_id = "gpc_dvfs_clk", .gpc_cntr_offset = MXC_GPC_CNTR_OFFSET, @@ -1669,8 +1673,9 @@ static void __init mx6_sabresd_board_init(void) imx6q_add_dma(); imx6q_add_dvfs_core(&sabresd_dvfscore_data); + #ifndef CONFIG_MX6_INTER_LDO_BYPASS mx6_cpu_regulator_init(); - + #endif imx6q_add_device_buttons(); /* enable sensor 3v3 and 1v8 */ diff --git a/arch/arm/mach-mx6/mx6q_sabresd_pmic_pfuze100.c b/arch/arm/mach-mx6/mx6q_sabresd_pmic_pfuze100.c index 2d3863beba31..ed6bfb115d31 100644 --- a/arch/arm/mach-mx6/mx6q_sabresd_pmic_pfuze100.c +++ b/arch/arm/mach-mx6/mx6q_sabresd_pmic_pfuze100.c @@ -65,6 +65,14 @@ #define PFUZE100_SWBSTCON1_SWBSTMOD_M (0x3<<2) +#ifdef CONFIG_MX6_INTER_LDO_BYPASS +static struct regulator_consumer_supply sw1_consumers[] = { + { + .supply = "VDDCORE", + } +}; +#endif + static struct regulator_consumer_supply sw2_consumers[] = { { .supply = "MICVDD", @@ -139,6 +147,11 @@ static struct regulator_init_data sw1a_init = { .boot_on = 1, .always_on = 1, }, + + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + .num_consumer_supplies = ARRAY_SIZE(sw1_consumers), + .consumer_supplies = sw1_consumers, + #endif }; static struct regulator_init_data sw1b_init = { @@ -368,11 +381,6 @@ static int pfuze100_init(struct mc_pfuze *pfuze) PFUZE100_SW1ASTANDBY_STBY_VAL); if (ret) goto err; - ret = pfuze_reg_rmw(pfuze, PFUZE100_SW1BSTANDBY, - PFUZE100_SW1BSTANDBY_STBY_M, - PFUZE100_SW1BSTANDBY_STBY_VAL); - if (ret) - goto err; ret = pfuze_reg_rmw(pfuze, PFUZE100_SW1CSTANDBY, PFUZE100_SW1CSTANDBY_STBY_M, PFUZE100_SW1CSTANDBY_STBY_VAL); diff --git a/arch/arm/plat-mxc/cpufreq.c b/arch/arm/plat-mxc/cpufreq.c index 9dfdd39be28c..3a0b818f5b22 100755 --- a/arch/arm/plat-mxc/cpufreq.c +++ b/arch/arm/plat-mxc/cpufreq.c @@ -22,7 +22,7 @@ #include #include #include - +#include #include #include @@ -32,6 +32,47 @@ #define CLK32_FREQ 32768 #define NANOSECOND (1000 * 1000 * 1000) +/*If using cpu internal ldo bypass,we need config pmic by I2C in suspend +interface, but cpufreq driver as sys_dev is more later to suspend than I2C +driver, so we should implement another I2C operate function which isolated +with kernel I2C driver, these code is copied from u-boot*/ +#ifdef CONFIG_MX6_INTER_LDO_BYPASS +#define IADR 0x00 +#define IFDR 0x04 +#define I2CR 0x08 +#define I2SR 0x0c +#define I2DR 0x10 + +#define I2CR_IEN (1 << 7) +#define I2CR_IIEN (1 << 6) +#define I2CR_MSTA (1 << 5) +#define I2CR_MTX (1 << 4) +#define I2CR_TX_NO_AK (1 << 3) +#define I2CR_RSTA (1 << 2) + +#define I2SR_ICF (1 << 7) +#define I2SR_IBB (1 << 5) +#define I2SR_IIF (1 << 1) +#define I2SR_RX_NO_AK (1 << 0) + +#define I2C_MAX_TIMEOUT 100000 +#define I2C_TIMEOUT_TICKET 1 +#define CCM_CCGR2 0x70 + +/*#define MX6_I2CRAW_DEBUG*/ +#ifdef MX6_I2CRAW_DEBUG +#define DPRINTF(args...) printk(args) +#else +#define DPRINTF(args...) +#endif + +/*judge for pfuze regulator driver suspend or not, after pfuze regulator +suspend and before resume, should use i2c raw read/write to configure +voltage, it's safe enough, otherwise mxc_cpufreq_suspend will be failed +since i2c/pfuze have been suspend firstly.*/ +extern int cpu_freq_suspend_in; +#endif + static int cpu_freq_khz_min; static int cpu_freq_khz_max; @@ -51,11 +92,108 @@ extern int set_low_bus_freq(void); extern int set_high_bus_freq(int high_bus_speed); extern int low_freq_bus_used(void); +#ifdef CONFIG_MX6_INTER_LDO_BYPASS +static void __iomem *i2c_base;/*i2c for pmic*/ +static void __iomem *ccm_base;/*ccm_base*/ +static int wait_busy(void) +{ + int timeout = I2C_MAX_TIMEOUT; + + while ((!(readb(i2c_base + I2SR) & I2SR_IBB) && (--timeout))) { + writeb(0, i2c_base + I2SR); + udelay(I2C_TIMEOUT_TICKET); + } + return timeout ? timeout : (readb(i2c_base + I2SR) & I2SR_IBB); +} + +static int wait_complete(void) +{ + int timeout = I2C_MAX_TIMEOUT; + + while ((!(readb(i2c_base + I2SR) & I2SR_ICF)) && (--timeout)) { + writeb(0, i2c_base + I2SR); + udelay(I2C_TIMEOUT_TICKET); + } + DPRINTF("%s:%x\n", __func__, readb(i2c_base + I2SR)); + { + int i; + for (i = 0; i < 200; i++) + udelay(10); + + } + writeb(0, i2c_base + I2SR); /* clear interrupt */ + + return timeout; +} + +static int tx_byte(u8 byte) +{ + writeb(byte, i2c_base + I2DR); + + if (!wait_complete() || readb(i2c_base + I2SR) & I2SR_RX_NO_AK) { + DPRINTF("%s:%x <= %x\n", __func__, readb(i2c_base + I2SR), + byte); + return -1; + } + DPRINTF("%s:%x\n", __func__, byte); + return 0; +} + +static int i2c_addr(unsigned char chip, u32 addr, int alen) +{ + writeb(I2CR_IEN | I2CR_MSTA | I2CR_MTX, i2c_base + I2CR); + if (!wait_busy()) { + DPRINTF("%s:trigger start fail(%x)\n", + __func__, readb(i2c_base + I2SR)); + return -1; + } + if (tx_byte(chip << 1) || (readb(i2c_base + I2SR) & I2SR_RX_NO_AK)) { + DPRINTF("%s:chip address cycle fail(%x)\n", + __func__, readb(i2c_base + I2SR)); + return -1; + } + while (alen--) + if (tx_byte((addr >> (alen * 8)) & 0xff) || + (readb(i2c_base + I2SR) & I2SR_RX_NO_AK)) { + DPRINTF("%s:device address cycle fail(%x)\n", + __func__, readb(i2c_base + I2SR)); + return -1; + } + return 0; +} + + +static int i2c_write(unsigned char chip, u32 addr, int alen, unsigned char *buf, + int len) +{ + int timeout = I2C_MAX_TIMEOUT; + DPRINTF("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", + __func__, chip, addr, alen, len); + + if (i2c_addr(chip, addr, alen)) + return -1; + + while (len--) + if (tx_byte(*buf++)) + return -1; + + writeb(I2CR_IEN, i2c_base + I2CR); + + while (readb(i2c_base + I2SR) & I2SR_IBB && --timeout) + udelay(I2C_TIMEOUT_TICKET); + + return 0; +} + +#endif int set_cpu_freq(int freq) { int i, ret = 0; int org_cpu_rate; int gp_volt = 0; + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + unsigned char data; + #endif org_cpu_rate = clk_get_rate(cpu_clk); if (org_cpu_rate == freq) @@ -68,13 +206,56 @@ int set_cpu_freq(int freq) if (gp_volt == 0) return ret; + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + if (cpu_freq_suspend_in) { + u32 value; + /*init I2C*/ + value = __raw_readl(ccm_base + CCM_CCGR2); + __raw_writel(value | 0x300, ccm_base + CCM_CCGR2); + udelay(1); + value = readb(i2c_base + I2CR); + writeb(value | (1 << 7), i2c_base + I2CR); + value = readb(i2c_base + I2SR); + writeb(0, i2c_base + I2SR); + switch (freq) { + case 1200000000:/*1.275*/ + data = 0x27; + break; + case 996000000:/*1.225V*/ + data = 0x25; + break; + case 792000000:/*1.1V*/ + case 672000000: + data = 0x20; + break; + case 396000000:/*0.95V*/ + data = 0x1a; + break; + case 198000000:/*0.85V*/ + data = 0x16; + break; + default: + printk(KERN_ERR "suspend freq error:%d!!!\n", freq); + break; + } + } + #endif /*Set the voltage for the GP domain. */ if (freq > org_cpu_rate) { if (low_bus_freq_mode) set_high_bus_freq(0); + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + if (cpu_freq_suspend_in) { + ret = i2c_write(0x8, 0x20, 1, &data, 1); + udelay(10); + } else + ret = regulator_set_voltage(cpu_regulator, gp_volt, + gp_volt); + #else ret = regulator_set_voltage(cpu_regulator, gp_volt, gp_volt); + #endif if (ret < 0) { printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!!\n"); return ret; @@ -89,8 +270,18 @@ int set_cpu_freq(int freq) } if (freq < org_cpu_rate) { + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + + if (cpu_freq_suspend_in) { + ret = i2c_write(0x8, 0x20, 1, &data, 1); + udelay(10); + } else + ret = regulator_set_voltage(cpu_regulator, gp_volt, + gp_volt); + #else ret = regulator_set_voltage(cpu_regulator, gp_volt, gp_volt); + #endif if (ret < 0) { printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!!\n"); return ret; @@ -184,8 +375,17 @@ static int mxc_cpufreq_suspend(struct cpufreq_policy *policy) { pre_suspend_rate = clk_get_rate(cpu_clk); /* Set to max freq and voltage */ + /*There should be *1000, but if fix the typo error, found + hard to pass streng test, it means we didn't move cpu freq + to highest freq in suspend, but if we choose bypass, we + have to do this, so use macro to decrease the impact on + released code, the 1Ghz issue should be fixed in the future*/ if (pre_suspend_rate != (imx_freq_table[0].frequency * 1000)) + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + set_cpu_freq(imx_freq_table[0].frequency * 1000); + #else set_cpu_freq(imx_freq_table[0].frequency); + #endif return 0; } @@ -194,7 +394,6 @@ static int mxc_cpufreq_resume(struct cpufreq_policy *policy) { if (clk_get_rate(cpu_clk) != pre_suspend_rate) set_cpu_freq(pre_suspend_rate); - return 0; } @@ -301,8 +500,14 @@ static struct cpufreq_driver mxc_driver = { .name = "imx", }; +extern void mx6_cpu_regulator_init(void); static int __init mxc_cpufreq_driver_init(void) { + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + mx6_cpu_regulator_init(); + i2c_base = MX6_IO_ADDRESS(MX6Q_I2C2_BASE_ADDR); + ccm_base = MX6_IO_ADDRESS(CCM_BASE_ADDR); + #endif return cpufreq_register_driver(&mxc_driver); } diff --git a/arch/arm/plat-mxc/system.c b/arch/arm/plat-mxc/system.c index cadf3bb23275..d3b77abba2b2 100644 --- a/arch/arm/plat-mxc/system.c +++ b/arch/arm/plat-mxc/system.c @@ -1,7 +1,7 @@ /* * Copyright (C) 1999 ARM Limited * Copyright (C) 2000 Deep Blue Solutions Ltd - * Copyright 2006-2011 Freescale Semiconductor, Inc. + * Copyright 2006-2012 Freescale Semiconductor, Inc. * Copyright 2008 Juergen Beisert, kernel@pengutronix.de * Copyright 2009 Ilya Yanok, Emcraft Systems Ltd, yanok@emcraft.com * @@ -41,7 +41,11 @@ void arch_reset(char mode, const char *cmd) #ifdef CONFIG_ARCH_MX6 /* wait for reset to assert... */ + #ifdef CONFIG_MX6_INTER_LDO_BYPASS + wcr_enable = 0x14; /*reset system by extern pmic*/ + #else wcr_enable = (1 << 2); + #endif __raw_writew(wcr_enable, wdog_base); /* errata TKT039676, SRS bit may be missed when SRC sample it, need to write the wdog controller