pandora: mark problematic supplies as always-on
[pandora-kernel.git] / arch / arm / mach-omap2 / board-omap3pandora.c
index 6d22101..d82abbf 100644 (file)
@@ -407,6 +407,9 @@ static struct twl4030_gpio_platform_data omap3pandora_gpio_data = {
        .irq_base       = TWL4030_GPIO_IRQ_BASE,
        .irq_end        = TWL4030_GPIO_IRQ_END,
        .setup          = omap3pandora_twl_gpio_setup,
+       /* note: never set PU/PD for gpio2, it's connected to vaux2 */
+       .pulldowns      = BIT(8) | BIT(15) | BIT(16) | BIT(17),
+       .debounce       = BIT(0) | BIT(1),
 };
 
 static struct regulator_consumer_supply pandora_vmmc1_supply[] = {
@@ -429,12 +432,18 @@ static struct regulator_consumer_supply pandora_vdds_supplies[] = {
 
 static struct regulator_consumer_supply pandora_vcc_lcd_supply[] = {
        REGULATOR_SUPPLY("vcc", "display0"),
+       /* on non-CC units only, must be last */
+       REGULATOR_SUPPLY("vdd", "display0"),
 };
 
 static struct regulator_consumer_supply pandora_usb_phy_supply[] = {
        REGULATOR_SUPPLY("hsusb1", "ehci-omap.0"),
 };
 
+static struct regulator_consumer_supply pandora_exp_supply[] = {
+       REGULATOR_SUPPLY("exp", NULL),
+};
+
 /* ads7846 on SPI and 2 nub controllers on I2C */
 static struct regulator_consumer_supply pandora_vaux4_supplies[] = {
        REGULATOR_SUPPLY("vcc", "spi1.0"),
@@ -447,6 +456,13 @@ static struct regulator_consumer_supply pandora_adac_supply[] = {
        REGULATOR_SUPPLY("lidsw", NULL),
 };
 
+static struct regulator_consumer_supply pandora_5v_supplies[] = {
+       REGULATOR_SUPPLY("v5v_force", NULL),
+       REGULATOR_SUPPLY("vdd_amp", "soc-audio"),
+       REGULATOR_SUPPLY("hsusb1_vbus", "ehci-omap.0"),
+       REGULATOR_SUPPLY("vdd", "display0"), /* on CC units, must be last */
+};
+
 /* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */
 static struct regulator_init_data pandora_vmmc1 = {
        .constraints = {
@@ -502,11 +518,28 @@ static struct regulator_init_data pandora_vaux2 = {
                                        | REGULATOR_MODE_STANDBY,
                .valid_ops_mask         = REGULATOR_CHANGE_MODE
                                        | REGULATOR_CHANGE_STATUS,
+               /* turning it off wastes power for unknown reason */
+               .always_on              = true,
        },
        .num_consumer_supplies  = ARRAY_SIZE(pandora_usb_phy_supply),
        .consumer_supplies      = pandora_usb_phy_supply,
 };
 
+/* VAUX3 goes to the expansion port */
+static struct regulator_init_data pandora_vaux3 = {
+       .constraints = {
+               .min_uV                 = 1500000,
+               .max_uV                 = 3000000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_VOLTAGE
+                                       | REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(pandora_exp_supply),
+       .consumer_supplies      = pandora_exp_supply,
+};
+
 /* VAUX4 for ads7846 and nubs */
 static struct regulator_init_data pandora_vaux4 = {
        .constraints = {
@@ -517,6 +550,8 @@ static struct regulator_init_data pandora_vaux4 = {
                                        | REGULATOR_MODE_STANDBY,
                .valid_ops_mask         = REGULATOR_CHANGE_MODE
                                        | REGULATOR_CHANGE_STATUS,
+               /* turning it off wastes power for unknown reason */
+               .always_on              = true,
        },
        .num_consumer_supplies  = ARRAY_SIZE(pandora_vaux4_supplies),
        .consumer_supplies      = pandora_vaux4_supplies,
@@ -537,6 +572,40 @@ static struct regulator_init_data pandora_vsim = {
        .consumer_supplies      = pandora_adac_supply,
 };
 
+/* REGEN enables the 5V supply */
+static struct regulator_init_data pandora_regen = {
+       .constraints = {
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+};
+
+/* 5V supply that feeds EHCI VBUS, audio amp, and (on CC only) the LCD */
+static struct regulator_init_data pandora_v5v_data = {
+       .supply_regulator       = "REGEN",
+       .num_consumer_supplies  = ARRAY_SIZE(pandora_5v_supplies),
+       .consumer_supplies      = pandora_5v_supplies,
+};
+
+static struct fixed_voltage_config pandora_v5v = {
+       .supply_name            = "v5v",
+       .microvolts             = 5000000,
+       .gpio                   = -EINVAL,
+       .startup_delay          = 5000, /* just a guess */
+       .enabled_at_boot        = 1,
+       .init_data              = &pandora_v5v_data,
+};
+
+static struct platform_device pandora_v5v_device = {
+       .name           = "reg-fixed-voltage",
+       .id             = 0,
+       .dev = {
+               .platform_data = &pandora_v5v,
+       },
+};
+
 /* Fixed regulator internal to Wifi module */
 static struct regulator_init_data pandora_vmmc3 = {
        .constraints = {
@@ -624,11 +693,15 @@ static struct twl4030_platform_data omap3pandora_twldata = {
        .vmmc2          = &pandora_vmmc2,
        .vaux1          = &pandora_vaux1,
        .vaux2          = &pandora_vaux2,
+       .vaux3          = &pandora_vaux3,
        .vaux4          = &pandora_vaux4,
        .vsim           = &pandora_vsim,
+       .regen          = &pandora_regen,
        .keypad         = &pandora_kp_data,
        .bci            = &pandora_bci_data,
        .power          = &pandora_power_data,
+
+       .dep_device     = &pandora_v5v_device,
 };
 
 static struct vsense_platform_data omap3pandora_nub1_data = {
@@ -669,6 +742,12 @@ static int __init omap3pandora_i2c_init(void)
                                        ARRAY_SIZE(pandora_vdds_supplies);
        omap3pandora_twldata.vpll2->consumer_supplies = pandora_vdds_supplies;
 
+       /* LCD vdd is connected differently on CC */
+       if (cpu_is_omap3630() || omap_rev() >= OMAP3430_REV_ES3_0)
+               pandora_v5v_data.num_consumer_supplies--;
+       else
+               pandora_vaux1.num_consumer_supplies--;
+
        omap3_pmic_init("tps65950", &omap3pandora_twldata);
        /* i2c2 pins are not connected */
        omap_register_i2c_bus(3, 100, omap3pandora_i2c3_boardinfo,
@@ -867,6 +946,11 @@ static struct omap_board_mux board_mux[] __initdata = {
 #endif
 
 static struct regulator *lid_switch_power;
+static struct regulator *v5v_power;
+static struct regulator *exp_power;
+static bool v5v_power_disable_needed;
+static bool exp_power_disable_needed;
+static struct delayed_work disable_regulators_work;
 
 #ifdef CONFIG_PM_SLEEP
 static int pandora_pm_suspend(struct device *dev)
@@ -888,11 +972,54 @@ static int pandora_pm_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(pandora_pm, pandora_pm_suspend, pandora_pm_resume);
 
+static void pandora_disable_regulators(struct work_struct *work)
+{
+       if (v5v_power_disable_needed)
+               regulator_disable(v5v_power);
+
+       if (exp_power_disable_needed)
+               regulator_disable(exp_power);
+}
+
 static int __devinit pandora_pm_probe(struct platform_device *pdev)
 {
        lid_switch_power = regulator_get(NULL, "lidsw");
        if (!IS_ERR(lid_switch_power))
                regulator_enable(lid_switch_power);
+       else
+               dev_err(&pdev->dev, "regulator_get lidsw: %ld\n",
+                       PTR_ERR(lid_switch_power));
+
+       /* if regulators are enabled by the bootloaders, take a reference and
+        * disable them later, after the boot scripts have a chance to
+        * reconfigure things if they want to, otherwise leave them off */
+       v5v_power = regulator_get(NULL, "v5v_force");
+       if (!IS_ERR(v5v_power)) {
+               if (regulator_is_enabled(v5v_power) > 0) {
+                       v5v_power_disable_needed = true;
+                       regulator_enable(v5v_power);
+               }
+       }
+       else {
+               dev_err(&pdev->dev, "regulator_get v5v_force: %ld\n",
+                       PTR_ERR(v5v_power));
+       }
+
+       exp_power = regulator_get(NULL, "exp");
+       if (!IS_ERR(exp_power)) {
+               if (regulator_is_enabled(exp_power) > 0) {
+                       exp_power_disable_needed = true;
+                       regulator_enable(exp_power);
+               }
+       }
+       else {
+               dev_err(&pdev->dev, "regulator_get exp: %ld\n",
+                       PTR_ERR(exp_power));
+       }
+
+       INIT_DELAYED_WORK(&disable_regulators_work,
+               pandora_disable_regulators);
+       schedule_delayed_work(&disable_regulators_work, 30 * HZ);
 
        return 0;
 }
@@ -947,17 +1074,134 @@ static void __init omap3pandora_init(void)
        omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT);
 }
 
-/* HACK: create it here, so that others don't need to bother */
 #ifdef CONFIG_PROC_FS
 #include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+
+static bool v5v_power_proc_enabled;
+static bool exp_power_proc_enabled;
+
+static int v5v_enable_read(char *page, char **start, off_t off,
+               int count, int *eof, void *data)
+{
+       if (IS_ERR_OR_NULL(v5v_power))
+               return -ENODEV;
+
+       *eof = 1;
+       return sprintf(page, "%d\n", v5v_power_proc_enabled);
+}
+
+static int v5v_enable_write(struct file *file, const char __user *buffer,
+               unsigned long count, void *data)
+{
+       char buff[32];
+       long val;
+       int ret;
+
+       if (IS_ERR_OR_NULL(v5v_power))
+               return -ENODEV;
+
+       count = strncpy_from_user(buff, buffer,
+                       count < sizeof(buff) ? count : sizeof(buff) - 1);
+       buff[count] = 0;
+
+       ret = strict_strtol(buff, 0, &val);
+       if (ret < 0)
+               return ret;
+
+       val = !!val;
+       if (val == v5v_power_proc_enabled)
+               return count;
+       if (val)
+               ret = regulator_enable(v5v_power);
+       else
+               ret = regulator_disable(v5v_power);
+       if (ret < 0)
+               return ret;
+
+       v5v_power_proc_enabled = val;
+       return count;
+}
+
+static int exp_power_read(char *page, char **start, off_t off,
+               int count, int *eof, void *data)
+{
+       int ret = 0;
+
+       if (IS_ERR_OR_NULL(exp_power))
+               return -ENODEV;
+
+       if (regulator_is_enabled(exp_power) > 0) {
+               ret = regulator_get_voltage(exp_power);
+               if (ret < 0)
+                       return ret;
+               ret /= 1000;
+       }
+
+       *eof = 1;
+       return sprintf(page, "%d\n", ret);
+}
+
+static int exp_power_write(struct file *file, const char __user *buffer,
+               unsigned long count, void *data)
+{
+       char buff[32];
+       bool enable;
+       long val;
+       int ret;
+
+       if (IS_ERR_OR_NULL(exp_power))
+               return -ENODEV;
+
+       count = strncpy_from_user(buff, buffer,
+                       count < sizeof(buff) ? count : sizeof(buff) - 1);
+       buff[count] = 0;
+
+       ret = strict_strtol(buff, 0, &val);
+       if (ret < 0)
+               return ret;
+
+       if (val != 0) {
+               val *= 1000;
+               ret = regulator_set_voltage(exp_power, val, val);
+               if (ret < 0)
+                       return ret;
+       }
+       enable = !!val;
+       if (enable == exp_power_proc_enabled)
+               return count;
+       if (enable)
+               ret = regulator_enable(exp_power);
+       else
+               ret = regulator_disable(exp_power);
+       if (ret < 0)
+               return ret;
+
+       exp_power_proc_enabled = enable;
+       return count;
+}
 
 static int __init proc_pandora_init(void)
 {
-       struct proc_dir_entry *ret;
+       struct proc_dir_entry *root, *ret;
 
-       ret = proc_mkdir("pandora", NULL);
-       if (!ret)
+       /* HACK: create it here, so that others don't need to bother */
+       root = proc_mkdir("pandora", NULL);
+       if (root == NULL)
                return -ENOMEM;
+
+       ret = create_proc_entry("always_enable_5v", S_IWUSR | S_IRUGO, root);
+       if (ret != NULL) {
+               ret->read_proc = v5v_enable_read;
+               ret->write_proc = v5v_enable_write;
+       }
+
+       ret = create_proc_entry("exp_power_mv", S_IWUSR | S_IRUGO, root);
+       if (ret != NULL) {
+               ret->read_proc = exp_power_read;
+               ret->write_proc = exp_power_write;
+       }
+
        return 0;
 }
 fs_initcall(proc_pandora_init);