[PATCH 2/3] drivers: regulator: qcom: add PMS405 SPMI regulator

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The PMS405 has 5 HFSMPS and 13 LDO regulators,

This commit adds support for one of the 5 HFSMPS regulators (s3) to
the spmi regulator driver.

The PMIC HFSMPS 430 regulators have 8 mV step size and a voltage
control scheme consisting of two  8-bit registers defining a 16-bit
voltage set point in units of millivolts

S3 controls the cpu voltages (s3 is a buck regulator of type HFS430);
it is therefore required so we can enable voltage scaling for safely
running cpufreq.

Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@xxxxxxxxxx>
---
 drivers/regulator/qcom_spmi-regulator.c | 197 +++++++++++++++++++++++++++++---
 1 file changed, 180 insertions(+), 17 deletions(-)

diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
index 53a61fb..6b8dc9c 100644
--- a/drivers/regulator/qcom_spmi-regulator.c
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -99,6 +99,7 @@ enum spmi_regulator_logical_type {
 	SPMI_REGULATOR_LOGICAL_TYPE_VS,
 	SPMI_REGULATOR_LOGICAL_TYPE_BOOST,
 	SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS,
+	SPMI_REGULATOR_LOGICAL_TYPE_HFS430,
 	SPMI_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
 	SPMI_REGULATOR_LOGICAL_TYPE_LN_LDO,
 	SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
@@ -120,6 +121,7 @@ enum spmi_regulator_type {
 enum spmi_regulator_subtype {
 	SPMI_REGULATOR_SUBTYPE_GP_CTL		= 0x08,
 	SPMI_REGULATOR_SUBTYPE_RF_CTL		= 0x09,
+	SPMI_REGULATOR_SUBTYPE_HFS430		= 0x0a,
 	SPMI_REGULATOR_SUBTYPE_N50		= 0x01,
 	SPMI_REGULATOR_SUBTYPE_N150		= 0x02,
 	SPMI_REGULATOR_SUBTYPE_N300		= 0x03,
@@ -183,6 +185,12 @@ enum spmi_boost_byp_registers {
 	SPMI_BOOST_BYP_REG_CURRENT_LIMIT	= 0x4b,
 };
 
+enum spmi_hfs430_registers {
+	SPMI_HFS430_REG_VOLTAGE_LB		= 0x40,
+	SPMI_HFS430_REG_VOLTAGE_VALID_LB	= 0x42,
+	SPMI_HFS430_REG_MODE			= 0x45,
+};
+
 enum spmi_saw3_registers {
 	SAW3_SECURE				= 0x00,
 	SAW3_ID					= 0x04,
@@ -260,20 +268,61 @@ enum spmi_common_control_register_index {
 #define SPMI_FTSMPS_STEP_CTRL_DELAY_MASK	0x07
 #define SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT	0
 
-/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define SPMI_HFS430_STEP_CTRL_STEP_MASK		0
+#define SPMI_HFS430_STEP_CTRL_STEP_SHIFT	0
+#define SPMI_HFS430_STEP_CTRL_DELAY_MASK	0x3
+#define SPMI_HFS430_STEP_CTRL_DELAY_SHIFT	0
+
+/* Clock rate in kHz of the FTSMPS and HFS430 regulator reference clocks. */
 #define SPMI_FTSMPS_CLOCK_RATE		19200
+#define SPMI_HFS430_CLOCK_RATE		1600
 
 /* Minimum voltage stepper delay for each step. */
 #define SPMI_FTSMPS_STEP_DELAY		8
+#define SPMI_HFS430_STEP_DELAY		2
 #define SPMI_DEFAULT_STEP_DELAY		20
 
 /*
- * The ratio SPMI_FTSMPS_STEP_MARGIN_NUM/SPMI_FTSMPS_STEP_MARGIN_DEN is used to
+ * The ratio SPMI_xxxxx_STEP_MARGIN_NUM/SPMI_xxxxx_STEP_MARGIN_DEN is used to
  * adjust the step rate in order to account for oscillator variance.
  */
 #define SPMI_FTSMPS_STEP_MARGIN_NUM	4
 #define SPMI_FTSMPS_STEP_MARGIN_DEN	5
 
+#define SPMI_HFS430_STEP_MARGIN_NUM	10
+#define SPMI_HFS430_STEP_MARGIN_DEN	11
+
+#define SPMI_STEP_MASK(__x)	SPMI_##__x##_STEP_CTRL_STEP_MASK
+#define SPMI_STEP_SHIFT(__x)	SPMI_##__x##_STEP_CTRL_STEP_SHIFT
+#define SPMI_DELAY_MASK(__x)	SPMI_##__x##_STEP_CTRL_DELAY_MASK
+#define SPMI_DELAY_SHIFT(__x)	SPMI_##__x##_STEP_CTRL_DELAY_SHIFT
+#define SPMI_CLOCK_RATE(__x)	SPMI_##__x##_CLOCK_RATE
+#define SPMI_STEP_DELAY(__x)	SPMI_##__x##_STEP_DELAY
+#define SPMI_MARGIN_NUM(__x)	SPMI_##__x##_STEP_MARGIN_NUM
+#define SPMI_MARGIN_DEN(__x)	SPMI_##__x##_STEP_MARGIN_DEN
+
+struct slew_rate_config {
+	unsigned int delay_mask;
+	unsigned int delay_shift;
+	unsigned int step_mask;
+	unsigned int step_shift;
+	unsigned int margin_num;
+	unsigned int margin_den;
+	unsigned int step_delay;
+	unsigned int clock_rate;
+};
+
+#define SLEW_RATE_CONFIG(x)  {			\
+	.delay_shift = SPMI_DELAY_SHIFT(x),	\
+	.delay_mask = SPMI_DELAY_MASK(x),	\
+	.step_shift = SPMI_STEP_SHIFT(x),	\
+	.step_mask = SPMI_STEP_MASK(x),		\
+	.margin_num = SPMI_MARGIN_NUM(x),	\
+	.margin_den = SPMI_MARGIN_DEN(x),	\
+	.step_delay = SPMI_STEP_DELAY(x),	\
+	.clock_rate = SPMI_CLOCK_RATE(x),	\
+}
+
 /* VSET value to decide the range of ULT SMPS */
 #define ULT_SMPS_RANGE_SPLIT 0x60
 
@@ -472,6 +521,11 @@ static struct spmi_voltage_range ult_pldo_ranges[] = {
 	SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
 };
 
+/* must be only one range */
+static struct spmi_voltage_range hfs430_ranges[] = {
+	SPMI_VOLTAGE_RANGE(0, 320000, 320000, 2040000, 2040000, 8000),
+};
+
 static DEFINE_SPMI_SET_POINTS(pldo);
 static DEFINE_SPMI_SET_POINTS(nldo1);
 static DEFINE_SPMI_SET_POINTS(nldo2);
@@ -486,6 +540,7 @@ static DEFINE_SPMI_SET_POINTS(ult_lo_smps);
 static DEFINE_SPMI_SET_POINTS(ult_ho_smps);
 static DEFINE_SPMI_SET_POINTS(ult_nldo);
 static DEFINE_SPMI_SET_POINTS(ult_pldo);
+static DEFINE_SPMI_SET_POINTS(hfs430);
 
 static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf,
 				 int len)
@@ -653,6 +708,10 @@ spmi_regulator_find_range(struct spmi_regulator *vreg)
 	range = vreg->set_points->range;
 	end = range + vreg->set_points->count;
 
+	/* we know we only have one range for this type */
+	if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_HFS430)
+		return range;
+
 	spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, &range_sel, 1);
 
 	for (; range < end; range++)
@@ -1135,6 +1194,82 @@ spmi_regulator_saw_set_voltage(struct regulator_dev *rdev, unsigned selector)
 					&voltage_sel, true);
 }
 
+#define SPMI_HFS430_MODE_PWM	0x07
+#define SPMI_HFS430_MODE_AUTO	0x06
+
+static unsigned int spmi_regulator_hfs430_get_mode(struct regulator_dev *rdev)
+{
+	struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+	u8 reg;
+	int ret;
+
+	ret = spmi_vreg_read(vreg, SPMI_HFS430_REG_MODE, &reg, 1);
+	if (ret) {
+		dev_err(&rdev->dev, "failed to get mode");
+		return ret;
+	}
+
+	if (reg == SPMI_HFS430_MODE_PWM)
+		return REGULATOR_MODE_NORMAL;
+
+	return REGULATOR_MODE_IDLE;
+}
+
+static int spmi_regulator_hfs430_set_mode(struct regulator_dev *rdev,
+					  unsigned int mode)
+{
+	struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+	u8 reg = mode == REGULATOR_MODE_NORMAL ? SPMI_HFS430_MODE_PWM :
+						 SPMI_HFS430_MODE_AUTO;
+
+	return spmi_vreg_write(vreg, SPMI_HFS430_REG_MODE, &reg, 1);
+}
+
+int spmi_regulator_hfs430_get_voltage(struct regulator_dev *rdev)
+{
+	struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+	u8 val[2];
+	int ret, uV;
+
+	ret = spmi_vreg_read(vreg, SPMI_HFS430_REG_VOLTAGE_VALID_LB, val, 2);
+	if (ret) {
+		dev_err(&rdev->dev, "failed to get voltage");
+		return ret;
+	}
+
+	uV = 1000 * (((unsigned int) val[1] << 8) | val[0]);
+	dev_dbg(&rdev->dev, "read = %d", uV);
+
+	return uV;
+}
+
+static int spmi_regulator_hfs430_set_voltage(struct regulator_dev *rdev,
+					     unsigned selector)
+{
+	struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+	const struct spmi_voltage_range *range;
+	int uV, vlevel;
+	u8 val[2];
+
+	range = spmi_regulator_find_range(vreg);
+	if (!range)
+		return -EINVAL;
+
+	uV = spmi_regulator_common_list_voltage(rdev, selector);
+	if (uV <= 0)
+		return uV;
+
+	vlevel = roundup(uV, range->step_uV) / 1000;
+
+	dev_dbg(&rdev->dev, "write (%d, %d), mode (%d)", uV, vlevel,
+		spmi_regulator_hfs430_get_mode(rdev));
+
+	val[0] = vlevel & 0xFF;
+	val[1] = (vlevel >> 8) & 0xFF;
+
+	return spmi_vreg_write(vreg, SPMI_HFS430_REG_VOLTAGE_LB, val, 2);
+}
+
 static struct regulator_ops spmi_saw_ops = {};
 
 static struct regulator_ops spmi_smps_ops = {
@@ -1264,12 +1399,24 @@ static struct regulator_ops spmi_ult_ldo_ops = {
 	.set_soft_start		= spmi_regulator_common_set_soft_start,
 };
 
+static struct regulator_ops spmi_hfs430_ops = {
+	/* always on regulators */
+	.set_voltage_sel	= spmi_regulator_hfs430_set_voltage,
+	.set_voltage_time_sel	= spmi_regulator_set_voltage_time_sel,
+	.get_voltage		= spmi_regulator_hfs430_get_voltage,
+	.map_voltage		= spmi_regulator_common_map_voltage,
+	.list_voltage		= spmi_regulator_common_list_voltage,
+	.get_mode		= spmi_regulator_hfs430_get_mode,
+	.set_mode		= spmi_regulator_hfs430_set_mode,
+};
+
 /* Maximum possible digital major revision value */
 #define INF 0xFF
 
 static const struct spmi_regulator_mapping supported_regulators[] = {
-	/*           type subtype dig_min dig_max ltype ops setpoints hpm_min */
+	/*        type subtype dig_min dig_max ltype ops setpoints hpm_min */
 	SPMI_VREG(BUCK,  GP_CTL,   0, INF, SMPS,   smps,   smps,   100000),
+	SPMI_VREG(BUCK,  HFS430,   0, INF, HFS430, hfs430, hfs430,  10000),
 	SPMI_VREG(LDO,   N300,     0, INF, LDO,    ldo,    nldo1,   10000),
 	SPMI_VREG(LDO,   N600,     0,   0, LDO,    ldo,    nldo2,   10000),
 	SPMI_VREG(LDO,   N1200,    0,   0, LDO,    ldo,    nldo2,   10000),
@@ -1396,8 +1543,12 @@ static int spmi_regulator_init_slew_rate(struct spmi_regulator *vreg)
 {
 	int ret;
 	u8 reg = 0;
-	int step, delay, slew_rate, step_delay;
+	int step, delay, slew_rate;
 	const struct spmi_voltage_range *range;
+	struct slew_rate_config *config, configs[] = {
+		[0] = SLEW_RATE_CONFIG(HFS430),
+		[1] = SLEW_RATE_CONFIG(FTSMPS),
+	};
 
 	ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_STEP_CTRL, &reg, 1);
 	if (ret) {
@@ -1410,25 +1561,26 @@ static int spmi_regulator_init_slew_rate(struct spmi_regulator *vreg)
 		return -EINVAL;
 
 	switch (vreg->logical_type) {
-	case SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS:
-		step_delay = SPMI_FTSMPS_STEP_DELAY;
+	case SPMI_REGULATOR_LOGICAL_TYPE_HFS430:
+		config = &configs[0];
 		break;
 	default:
-		step_delay = SPMI_DEFAULT_STEP_DELAY;
-		break;
-	}
+		config = &configs[1];
+		if (vreg->logical_type != SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS)
+			config->step_delay =  SPMI_STEP_DELAY(DEFAULT);
+	};
 
-	step = reg & SPMI_FTSMPS_STEP_CTRL_STEP_MASK;
-	step >>= SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT;
+	step = reg & config->step_mask;
+	step >>= config->step_shift;
 
-	delay = reg & SPMI_FTSMPS_STEP_CTRL_DELAY_MASK;
-	delay >>= SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+	delay = reg & config->delay_mask;
+	delay >>= config->delay_shift;
 
 	/* slew_rate has units of uV/us */
-	slew_rate = SPMI_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
-	slew_rate /= 1000 * (step_delay << delay);
-	slew_rate *= SPMI_FTSMPS_STEP_MARGIN_NUM;
-	slew_rate /= SPMI_FTSMPS_STEP_MARGIN_DEN;
+	slew_rate = config->clock_rate * range->step_uV * (1 << step);
+	slew_rate /= 1000 * (config->step_delay << delay);
+	slew_rate *= config->margin_num;
+	slew_rate /= config->margin_den;
 
 	/* Ensure that the slew rate is greater than 0 */
 	vreg->slew_rate = max(slew_rate, 1);
@@ -1445,6 +1597,9 @@ static int spmi_regulator_init_registers(struct spmi_regulator *vreg,
 
 	type = vreg->logical_type;
 
+	if (type == SPMI_REGULATOR_LOGICAL_TYPE_HFS430)
+		return 0;
+
 	ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8);
 	if (ret)
 		return ret;
@@ -1572,9 +1727,11 @@ static int spmi_regulator_of_parse(struct device_node *node,
 	case SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS:
 	case SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS:
 	case SPMI_REGULATOR_LOGICAL_TYPE_SMPS:
+	case SPMI_REGULATOR_LOGICAL_TYPE_HFS430:
 		ret = spmi_regulator_init_slew_rate(vreg);
 		if (ret)
 			return ret;
+
 	default:
 		break;
 	}
@@ -1731,12 +1888,18 @@ static const struct spmi_regulator_data pmi8994_regulators[] = {
 	{ }
 };
 
+static const struct spmi_regulator_data pms405_regulators[] = {
+	{ "s3", 0x1a00, }, /* supply name in the dts only */
+	{ }
+};
+
 static const struct of_device_id qcom_spmi_regulator_match[] = {
 	{ .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators },
 	{ .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators },
 	{ .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators },
 	{ .compatible = "qcom,pm8994-regulators", .data = &pm8994_regulators },
 	{ .compatible = "qcom,pmi8994-regulators", .data = &pmi8994_regulators },
+	{ .compatible = "qcom,pms405-regulators", .data = &pms405_regulators },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, qcom_spmi_regulator_match);
-- 
2.7.4




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux