summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2019-10-16 07:37:06 -0700
committerTony Lindgren <tony@atomide.com>2019-10-16 07:51:15 -0700
commitd44fa156dcb29dd0215c1fe63e7a7031a106557e (patch)
tree992eb4e7c74956db688fe2600bc0405f00bf25a6
parent32236a84906f9df6aeccf6cc735c8e911ef26da6 (diff)
ARM: OMAP2+: Configure voltage controller for cpcap
We can configure voltage controller for cpcap with the data available in Motorola Mapphone Android Linux kernel. Let's add it so we can have droid4 behave the same way for voltage controller as other omap4 devices and save some power when idle. Note that we're now using high-speed i2c mode, looks like the Motorola kernel had a typo using 0x200 instead of 200 for the timings which may caused it to not work properly. Also note that in the long run, this should just become dts data for a voltage controller device driver. But let's get things working first to make it possible to test further changes easily. Cc: Merlijn Wajer <merlijn@wizzup.org> Cc: Pavel Machek <pavel@ucw.cz> Cc: Sebastian Reichel <sre@kernel.org> Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Tony Lindgren <tony@atomide.com>
-rw-r--r--arch/arm/mach-omap2/Makefile5
-rw-r--r--arch/arm/mach-omap2/omap_twl.c5
-rw-r--r--arch/arm/mach-omap2/pm.c1
-rw-r--r--arch/arm/mach-omap2/pm.h14
-rw-r--r--arch/arm/mach-omap2/pmic-cpcap.c265
5 files changed, 285 insertions, 5 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 8f208197988f..9ef2f222f1bd 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -29,6 +29,11 @@ obj-y += mcbsp.o
endif
obj-$(CONFIG_TWL4030_CORE) += omap_twl.o
+
+ifneq ($(CONFIG_MFD_CPCAP),)
+obj-y += pmic-cpcap.o
+endif
+
obj-$(CONFIG_SOC_HAS_OMAP2_SDRC) += sdrc.o
# SMP support ONLY available for OMAP4
diff --git a/arch/arm/mach-omap2/omap_twl.c b/arch/arm/mach-omap2/omap_twl.c
index cb1e8451c7ad..a642d3b39e50 100644
--- a/arch/arm/mach-omap2/omap_twl.c
+++ b/arch/arm/mach-omap2/omap_twl.c
@@ -36,11 +36,6 @@
#define OMAP4_VDD_CORE_SR_VOLT_REG 0x61
#define OMAP4_VDD_CORE_SR_CMD_REG 0x62
-#define OMAP4_VP_CONFIG_ERROROFFSET 0x00
-#define OMAP4_VP_VSTEPMIN_VSTEPMIN 0x01
-#define OMAP4_VP_VSTEPMAX_VSTEPMAX 0x04
-#define OMAP4_VP_VLIMITTO_TIMEOUT_US 200
-
static bool is_offset_valid;
static u8 smps_offset;
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 7ac9af56762d..01ec1ba4878b 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -148,6 +148,7 @@ int __init omap2_common_pm_late_init(void)
/* Init the voltage layer */
omap3_twl_init();
omap4_twl_init();
+ omap4_cpcap_init();
omap_voltage_late_init();
/* Smartreflex device init */
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index 8a55b69bca63..2a883a0c1fcd 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -107,6 +107,11 @@ extern u16 pm44xx_errata;
#define IS_PM44XX_ERRATUM(id) 0
#endif
+#define OMAP4_VP_CONFIG_ERROROFFSET 0x00
+#define OMAP4_VP_VSTEPMIN_VSTEPMIN 0x01
+#define OMAP4_VP_VSTEPMAX_VSTEPMAX 0x04
+#define OMAP4_VP_VLIMITTO_TIMEOUT_US 200
+
#ifdef CONFIG_POWER_AVS_OMAP
extern int omap_devinit_smartreflex(void);
extern void omap_enable_smartreflex_on_init(void);
@@ -134,6 +139,15 @@ static inline int omap4_twl_init(void)
}
#endif
+#if IS_ENABLED(CONFIG_MFD_CPCAP)
+extern int omap4_cpcap_init(void);
+#else
+static inline int omap4_cpcap_init(void)
+{
+ return -EINVAL;
+}
+#endif
+
#ifdef CONFIG_PM
extern void omap_pm_setup_oscillator(u32 tstart, u32 tshut);
extern void omap_pm_get_oscillator(u32 *tstart, u32 *tshut);
diff --git a/arch/arm/mach-omap2/pmic-cpcap.c b/arch/arm/mach-omap2/pmic-cpcap.c
new file mode 100644
index 000000000000..2c2a178d988d
--- /dev/null
+++ b/arch/arm/mach-omap2/pmic-cpcap.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * pmic-cpcap.c - CPCAP-specific functions for the OPP code
+ *
+ * Adapted from Motorola Mapphone Android Linux kernel
+ * Copyright (C) 2011 Motorola, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include "soc.h"
+#include "pm.h"
+#include "voltage.h"
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include "pm.h"
+#include "vc.h"
+
+/**
+ * omap_cpcap_vsel_to_vdc - convert CPCAP VSEL value to microvolts DC
+ * @vsel: CPCAP VSEL value to convert
+ *
+ * Returns the microvolts DC that the CPCAP PMIC should generate when
+ * programmed with @vsel.
+ */
+unsigned long omap_cpcap_vsel_to_uv(unsigned char vsel)
+{
+ if (vsel > 0x44)
+ vsel = 0x44;
+ return (((vsel * 125) + 6000)) * 100;
+}
+
+/**
+ * omap_cpcap_uv_to_vsel - convert microvolts DC to CPCAP VSEL value
+ * @uv: microvolts DC to convert
+ *
+ * Returns the VSEL value necessary for the CPCAP PMIC to
+ * generate an output voltage equal to or greater than @uv microvolts DC.
+ */
+unsigned char omap_cpcap_uv_to_vsel(unsigned long uv)
+{
+ if (uv < 600000)
+ uv = 600000;
+ else if (uv > 1450000)
+ uv = 1450000;
+ return DIV_ROUND_UP(uv - 600000, 12500);
+}
+
+static struct omap_voltdm_pmic omap_cpcap_core = {
+ .slew_rate = 4000,
+ .step_size = 12500,
+ .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
+ .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
+ .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX,
+ .vddmin = 900000,
+ .vddmax = 1350000,
+ .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
+ .i2c_slave_addr = 0x02,
+ .volt_reg_addr = 0x00,
+ .cmd_reg_addr = 0x01,
+ .i2c_high_speed = true,
+ .vsel_to_uv = omap_cpcap_vsel_to_uv,
+ .uv_to_vsel = omap_cpcap_uv_to_vsel,
+};
+
+static struct omap_voltdm_pmic omap_cpcap_iva = {
+ .slew_rate = 4000,
+ .step_size = 12500,
+ .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
+ .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
+ .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX,
+ .vddmin = 900000,
+ .vddmax = 1350000,
+ .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
+ .i2c_slave_addr = 0x44,
+ .volt_reg_addr = 0x0,
+ .cmd_reg_addr = 0x01,
+ .i2c_high_speed = true,
+ .vsel_to_uv = omap_cpcap_vsel_to_uv,
+ .uv_to_vsel = omap_cpcap_uv_to_vsel,
+};
+
+/**
+ * omap_max8952_vsel_to_vdc - convert MAX8952 VSEL value to microvolts DC
+ * @vsel: MAX8952 VSEL value to convert
+ *
+ * Returns the microvolts DC that the MAX8952 Regulator should generate when
+ * programmed with @vsel.
+ */
+unsigned long omap_max8952_vsel_to_uv(unsigned char vsel)
+{
+ if (vsel > 0x3F)
+ vsel = 0x3F;
+ return (((vsel * 100) + 7700)) * 100;
+}
+
+/**
+ * omap_max8952_uv_to_vsel - convert microvolts DC to MAX8952 VSEL value
+ * @uv: microvolts DC to convert
+ *
+ * Returns the VSEL value necessary for the MAX8952 Regulator to
+ * generate an output voltage equal to or greater than @uv microvolts DC.
+ */
+unsigned char omap_max8952_uv_to_vsel(unsigned long uv)
+{
+ if (uv < 770000)
+ uv = 770000;
+ else if (uv > 1400000)
+ uv = 1400000;
+ return DIV_ROUND_UP(uv - 770000, 10000);
+}
+
+static struct omap_voltdm_pmic omap443x_max8952_mpu = {
+ .slew_rate = 16000,
+ .step_size = 10000,
+ .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
+ .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
+ .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX,
+ .vddmin = 900000,
+ .vddmax = 1400000,
+ .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
+ .i2c_slave_addr = 0x60,
+ .volt_reg_addr = 0x03,
+ .cmd_reg_addr = 0x03,
+ .i2c_high_speed = true,
+ .vsel_to_uv = omap_max8952_vsel_to_uv,
+ .uv_to_vsel = omap_max8952_uv_to_vsel,
+};
+
+/**
+ * omap_fan5355_vsel_to_vdc - convert FAN535503 VSEL value to microvolts DC
+ * @vsel: FAN535503 VSEL value to convert
+ *
+ * Returns the microvolts DC that the FAN535503 Regulator should generate when
+ * programmed with @vsel.
+ */
+unsigned long omap_fan535503_vsel_to_uv(unsigned char vsel)
+{
+ /* Extract bits[5:0] */
+ vsel &= 0x3F;
+
+ return (((vsel * 125) + 7500)) * 100;
+}
+
+/**
+ * omap_fan535508_vsel_to_vdc - convert FAN535508 VSEL value to microvolts DC
+ * @vsel: FAN535508 VSEL value to convert
+ *
+ * Returns the microvolts DC that the FAN535508 Regulator should generate when
+ * programmed with @vsel.
+ */
+unsigned long omap_fan535508_vsel_to_uv(unsigned char vsel)
+{
+ /* Extract bits[5:0] */
+ vsel &= 0x3F;
+
+ if (vsel > 0x37)
+ vsel = 0x37;
+ return (((vsel * 125) + 7500)) * 100;
+}
+
+
+/**
+ * omap_fan535503_uv_to_vsel - convert microvolts DC to FAN535503 VSEL value
+ * @uv: microvolts DC to convert
+ *
+ * Returns the VSEL value necessary for the MAX8952 Regulator to
+ * generate an output voltage equal to or greater than @uv microvolts DC.
+ */
+unsigned char omap_fan535503_uv_to_vsel(unsigned long uv)
+{
+ unsigned char vsel;
+ if (uv < 750000)
+ uv = 750000;
+ else if (uv > 1537500)
+ uv = 1537500;
+
+ vsel = DIV_ROUND_UP(uv - 750000, 12500);
+ return vsel | 0xC0;
+}
+
+/**
+ * omap_fan535508_uv_to_vsel - convert microvolts DC to FAN535508 VSEL value
+ * @uv: microvolts DC to convert
+ *
+ * Returns the VSEL value necessary for the MAX8952 Regulator to
+ * generate an output voltage equal to or greater than @uv microvolts DC.
+ */
+unsigned char omap_fan535508_uv_to_vsel(unsigned long uv)
+{
+ unsigned char vsel;
+ if (uv < 750000)
+ uv = 750000;
+ else if (uv > 1437500)
+ uv = 1437500;
+
+ vsel = DIV_ROUND_UP(uv - 750000, 12500);
+ return vsel | 0xC0;
+}
+
+/* fan5335-core */
+static struct omap_voltdm_pmic omap4_fan_core = {
+ .slew_rate = 4000,
+ .step_size = 12500,
+ .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
+ .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
+ .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX,
+ .vddmin = 850000,
+ .vddmax = 1375000,
+ .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
+ .i2c_slave_addr = 0x4A,
+ .i2c_high_speed = true,
+ .volt_reg_addr = 0x01,
+ .cmd_reg_addr = 0x01,
+ .vsel_to_uv = omap_fan535508_vsel_to_uv,
+ .uv_to_vsel = omap_fan535508_uv_to_vsel,
+};
+
+/* fan5335 iva */
+static struct omap_voltdm_pmic omap4_fan_iva = {
+ .slew_rate = 4000,
+ .step_size = 12500,
+ .vp_erroroffset = OMAP4_VP_CONFIG_ERROROFFSET,
+ .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
+ .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX,
+ .vddmin = 850000,
+ .vddmax = 1375000,
+ .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
+ .i2c_slave_addr = 0x48,
+ .volt_reg_addr = 0x01,
+ .cmd_reg_addr = 0x01,
+ .i2c_high_speed = true,
+ .vsel_to_uv = omap_fan535503_vsel_to_uv,
+ .uv_to_vsel = omap_fan535503_uv_to_vsel,
+};
+
+int __init omap4_cpcap_init(void)
+{
+ struct voltagedomain *voltdm;
+
+ if (!of_find_compatible_node(NULL, NULL, "motorola,cpcap"))
+ return -ENODEV;
+
+ voltdm = voltdm_lookup("mpu");
+ omap_voltage_register_pmic(voltdm, &omap443x_max8952_mpu);
+
+ if (of_machine_is_compatible("motorola,droid-bionic")) {
+ voltdm = voltdm_lookup("mpu");
+ omap_voltage_register_pmic(voltdm, &omap_cpcap_core);
+
+ voltdm = voltdm_lookup("mpu");
+ omap_voltage_register_pmic(voltdm, &omap_cpcap_iva);
+ } else {
+ voltdm = voltdm_lookup("core");
+ omap_voltage_register_pmic(voltdm, &omap4_fan_core);
+
+ voltdm = voltdm_lookup("iva");
+ omap_voltage_register_pmic(voltdm, &omap4_fan_iva);
+ }
+
+ return 0;
+}