]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00181348-1 :sabresd pfuze support cpu internal LDO bypass
authorRobin Gong <B38343@freescale.com>
Thu, 3 May 2012 10:11:06 +0000 (18:11 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:34:34 +0000 (08:34 +0200)
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 <B38343@freescale.com>
arch/arm/mach-mx6/Kconfig
arch/arm/mach-mx6/board-mx6q_sabresd.c
arch/arm/mach-mx6/mx6q_sabresd_pmic_pfuze100.c
arch/arm/plat-mxc/cpufreq.c
arch/arm/plat-mxc/system.c

index 1db924a0975e09507be64af422c1813c5351854b..c202f0c56612703401821d8494c036a0d6dbf9cb 100644 (file)
@@ -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
index 37e4e284e3e8b080e0e83200055811cc79a086ff..2192abbd28142712e98cec791bc89312691c515d 100644 (file)
@@ -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 */
index 2d3863beba31b27e678c1c8122701a26ff289b5c..ed6bfb115d3129b8998a342559af0fccb77660a0 100644 (file)
 #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);
index 9dfdd39be28ca53f16b199fa9f89904140599ec5..3a0b818f5b225b74961d7f98d54bdc1a94f2599d 100755 (executable)
@@ -22,7 +22,7 @@
 #include <linux/slab.h>
 #include <linux/regulator/consumer.h>
 #include <linux/delay.h>
-
+#include <linux/io.h>
 #include <asm/smp_plat.h>
 #include <asm/cpu.h>
 
 #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);
 }
 
index cadf3bb232753db1a9a3b995ecdb438513f0df08..d3b77abba2b25abf92c57b24eba0e9775584cc80 100644 (file)
@@ -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