pmic: 8x41: Add support ldo set voltage API.

Change-Id: Ia35d2f64842ddc1f5bf82d93c81305e099e84151
diff --git a/dev/pmic/pm8x41/include/pm8x41.h b/dev/pmic/pm8x41/include/pm8x41.h
index 3816325..94436b1 100644
--- a/dev/pmic/pm8x41/include/pm8x41.h
+++ b/dev/pmic/pm8x41/include/pm8x41.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -9,7 +9,7 @@
  *     copyright notice, this list of conditions and the following
  *     disclaimer in the documentation and/or other materials provided
  *     with the distribution.
- *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *   * Neither the name of The Linux Foundation, Inc. nor the names of its
  *     contributors may be used to endorse or promote products derived
  *     from this software without specific prior written permission.
  *
@@ -29,6 +29,8 @@
 #ifndef _PM8x41_H_
 #define _PM8x41_H_
 
+#include <sys/types.h>
+
 #define PM_GPIO_DIR_OUT         0x01
 #define PM_GPIO_DIR_IN          0x00
 #define PM_GPIO_DIR_BOTH        0x02
@@ -60,5 +62,15 @@
 void pm8x41_set_boot_done();
 int pm8x41_vol_down_key_status();
 void pm8x41_reset_configure(uint8_t);
+int pm8x41_ldo_set_voltage(const char *, uint32_t);
+int pm8x41_ldo_control(const char *, uint8_t);
 
+struct pm8x41_ldo {
+	const char *name;
+	uint8_t type;
+	uint32_t base;
+	uint32_t range_reg;
+	uint32_t step_reg;
+	uint32_t enable_reg;
+};
 #endif
diff --git a/dev/pmic/pm8x41/include/pm8x41_hw.h b/dev/pmic/pm8x41/include/pm8x41_hw.h
index 4886a8e..01fd07f 100644
--- a/dev/pmic/pm8x41/include/pm8x41_hw.h
+++ b/dev/pmic/pm8x41/include/pm8x41_hw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -9,7 +9,7 @@
  *     copyright notice, this list of conditions and the following
  *     disclaimer in the documentation and/or other materials provided
  *     with the distribution.
- *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *   * Neither the name of The Linux Foundation, Inc. nor the names of its
  *     contributors may be used to endorse or promote products derived
  *     from this software without specific prior written permission.
  *
@@ -29,7 +29,6 @@
 #ifndef _PM8x41_HW_H_
 #define _PM8x41_HW_H_
 
-
 /* SMBB Registers */
 #define SMBB_MISC_BOOT_DONE                   0x1642
 
@@ -76,4 +75,44 @@
 #define S2_RESET_TYPE_WARM                    0x1
 #define PON_RESIN_N_RESET_S2_TIMER_MAX_VALUE  0x7
 
+/* LDO voltage ranges */
+#define NLDO_UV_MIN                           375000
+#define NLDO_UV_MAX                           1537500
+#define NLDO_UV_STEP                          12500
+#define NLDO_UV_VMIN_LOW                      750000
+
+#define PLDO_UV_VMIN_LOW                      750000
+#define PLDO_UV_VMIN_MID                      1500000
+#define PLDO_UV_VMIN_HIGH                     1750000
+
+#define PLDO_UV_MIN                           1537500
+#define PDLO_UV_MID                           3075000
+#define PLDO_UV_MAX                           4900000
+#define PLDO_UV_STEP_LOW                      12500
+#define PLDO_UV_STEP_MID                      25000
+#define PLDO_UV_STEP_HIGH                     50000
+
+#define LDO_RANGE_SEL_BIT                     0
+#define LDO_VSET_SEL_BIT                      0
+#define LDO_VREG_ENABLE_BIT                   7
+#define LDO_NORMAL_PWR_BIT                    7
+
+#define LDO_RANGE_CTRL                        0x40
+#define LDO_STEP_CTRL                         0x41
+#define LDO_POWER_MODE                        0x45
+#define LDO_EN_CTL_REG                        0x46
+
+#define PLDO_TYPE                             0
+#define NLDO_TYPE                             1
+
+#define LDO(_name, _type, _base, _range, _step, _enable) \
+{ \
+	.name = _name, \
+	.type = _type, \
+	.base = _base, \
+	.range_reg = _range, \
+	.step_reg = _step, \
+	.enable_reg = _enable, \
+}
+
 #endif
diff --git a/dev/pmic/pm8x41/pm8x41.c b/dev/pmic/pm8x41/pm8x41.c
index f6804ec..31b13cb 100644
--- a/dev/pmic/pm8x41/pm8x41.c
+++ b/dev/pmic/pm8x41/pm8x41.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -9,7 +9,7 @@
  *     copyright notice, this list of conditions and the following
  *     disclaimer in the documentation and/or other materials provided
  *     with the distribution.
- *   * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ *   * Neither the name of The Linux Foundation, Inc. nor the names of its
  *     contributors may be used to endorse or promote products derived
  *     from this software without specific prior written permission.
  *
@@ -30,6 +30,7 @@
 #include <debug.h>
 #include <reg.h>
 #include <spmi.h>
+#include <string.h>
 #include <pm8x41_hw.h>
 #include <pm8x41.h>
 #include <platform/timer.h>
@@ -42,6 +43,11 @@
 #define PERIPH_ID(_addr)    (((_addr) & 0xFF00) >> 8)
 #define SLAVE_ID(_addr)     ((_addr) >> 16)
 
+struct pm8x41_ldo ldo_data[] = {
+	LDO("LDO2",  NLDO_TYPE, 0x14100, LDO_RANGE_CTRL, LDO_STEP_CTRL, LDO_EN_CTL_REG),
+	LDO("LDO12", PLDO_TYPE, 0x14B00, LDO_RANGE_CTRL, LDO_STEP_CTRL, LDO_EN_CTL_REG),
+	LDO("LDO22", PLDO_TYPE, 0x15500, LDO_RANGE_CTRL, LDO_STEP_CTRL, LDO_EN_CTL_REG),
+};
 
 /* Local functions */
 static uint8_t pm8x41_reg_read(uint32_t addr)
@@ -219,3 +225,103 @@
 	val |= BIT(S2_RESET_EN_BIT);
 	REG_WRITE(PON_PS_HOLD_RESET_CTL, val);
 }
+
+static struct pm8x41_ldo *ldo_get(const char *ldo_name)
+{
+	uint8_t i;
+	struct pm8x41_ldo *ldo = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(ldo_data); i++) {
+		ldo = &ldo_data[i];
+		if (!strncmp(ldo->name, ldo_name, strlen(ldo_name)))
+			break;
+	}
+	return ldo;
+}
+
+/*
+ * LDO set voltage, takes ldo name & voltage in UV as input
+ */
+int pm8x41_ldo_set_voltage(const char *name, uint32_t voltage)
+{
+	uint32_t range = 0;
+	uint32_t step = 0;
+	uint32_t mult = 0;
+	uint32_t val = 0;
+	uint32_t vmin = 0;
+	struct pm8x41_ldo *ldo;
+
+	ldo = ldo_get(name);
+	if (!ldo) {
+		dprintf(CRITICAL, "LDO requsted is not supported: %s\n", name);
+		return 1;
+	}
+
+	/* Program Normal power mode */
+	val = 0x0;
+	val = (1 << LDO_NORMAL_PWR_BIT);
+	REG_WRITE((ldo->base + LDO_POWER_MODE), val);
+
+	/*
+	 * Select range, step & vmin based on input voltage & type of LDO
+	 * LDO can operate in low, mid, high power mode
+	 */
+	if (ldo->type == PLDO_TYPE) {
+		if (voltage < PLDO_UV_MIN) {
+			range = 2;
+			step = PLDO_UV_STEP_LOW;
+			vmin = PLDO_UV_VMIN_LOW;
+		} else if (voltage < PDLO_UV_MID) {
+			range = 3;
+			step = PLDO_UV_STEP_MID;
+			vmin = PLDO_UV_VMIN_MID;
+		} else {
+			range = 4;
+			step = PLDO_UV_STEP_HIGH;
+			vmin = PLDO_UV_VMIN_HIGH;
+		}
+	} else {
+		range = 2;
+		step = NLDO_UV_STEP;
+		vmin = NLDO_UV_VMIN_LOW;
+	}
+
+	mult = (voltage - vmin) / step;
+
+	/* Set Range in voltage ctrl register */
+	val = 0x0;
+	val = range << LDO_RANGE_SEL_BIT;
+	REG_WRITE((ldo->base + ldo->range_reg), val);
+
+	/* Set multiplier in voltage ctrl register */
+	val = 0x0;
+	val = mult << LDO_VSET_SEL_BIT;
+	REG_WRITE((ldo->base + ldo->step_reg), val);
+
+	return 0;
+}
+
+/*
+ * Enable or Disable LDO
+ */
+int pm8x41_ldo_control(const char *name, uint8_t enable)
+{
+	uint32_t val = 0;
+	struct pm8x41_ldo *ldo;
+
+	ldo = ldo_get(name);
+	if (!ldo) {
+		dprintf(CRITICAL, "Requested LDO is not supported : %s\n", name);
+		return 1;
+	}
+
+	/* Enable LDO */
+	if (enable)
+		val = (1 << LDO_VREG_ENABLE_BIT);
+	else
+		val = (0 << LDO_VREG_ENABLE_BIT);
+
+	REG_WRITE((ldo->base + ldo->enable_reg), val);
+
+	return 0;
+}