Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf407955 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "regulator: qcom_pm8008-regulator: add support for PMIC PM8010"

parents 9995b020 4338d310
Loading
Loading
Loading
Loading
+130 −31
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */

#define pr_fmt(fmt) "PM8008: %s: " fmt, __func__

@@ -60,6 +60,8 @@

#define LDO_STEPPER_CTL_REG(base)	(base + 0x3b)
#define STEP_RATE_MASK			GENMASK(1, 0)
/* Step rate in uV/us */
#define PM8010_STEP_RATE		4800

#define LDO_PD_CTL_REG(base)		(base + 0xA0)
#define STRONG_PD_EN_BIT		BIT(7)
@@ -67,6 +69,11 @@
#define MAX_REG_NAME			20
#define PM8008_MAX_LDO			7

enum pmic_subtype {
	PM8008_SUBTYPE,
	PM8010_SUBTYPE,
};

struct pm8008_chip {
	struct device		*dev;
	struct regmap		*regmap;
@@ -75,11 +82,20 @@ struct pm8008_chip {
	int			ocp_irq;
};

struct reg_init_data {
	u8			offset;
	u8			data;
};

struct regulator_data {
	char				*name;
	char				*supply_name;
	int				min_uv;
	int				max_uv;
	int				hpm_min_load_ua;
	int				min_dropout_uv;
	const struct reg_init_data	*reg_init;
	unsigned int			reg_init_size;
};

struct pm8008_regulator {
@@ -96,17 +112,52 @@ struct pm8008_regulator {
	int			min_dropout_uv;
	int			step_rate;
	bool			enable_ocp_broadcast;
	enum pmic_subtype       pmic_subtype;
};

static const struct regulator_data pm8008_reg_data[PM8008_MAX_LDO] = {
	/* name  parent      min_uv  max_uv  hpm_load  headroom_uv */
	{"l1", "vdd_l1_l2",  528000, 1504000, 30000, 225000},
	{"l2", "vdd_l1_l2",  528000, 1504000, 30000, 225000},
	{"l3", "vdd_l3_l4", 1504000, 3400000, 10000, 200000},
	{"l4", "vdd_l3_l4", 1504000, 3400000, 10000, 200000},
	{"l5", "vdd_l5",    1504000, 3400000, 10000, 300000},
	{"l6", "vdd_l6",    1504000, 3400000, 10000, 300000},
	{"l7", "vdd_l7",    1504000, 3400000, 10000, 300000},
};

static struct regulator_data reg_data[] = {
			/* name,        parent,  min load, headroom */
			{"pm8008_l1", "vdd_l1_l2", 10000, 225000},
			{"pm8008_l2", "vdd_l1_l2", 10000, 225000},
			{"pm8008_l3", "vdd_l3_l4", 10000, 200000},
			{"pm8008_l4", "vdd_l3_l4", 10000, 200000},
			{"pm8008_l5", "vdd_l5", 10000, 300000},
			{"pm8008_l6", "vdd_l6", 10000, 300000},
			{"pm8008_l7", "vdd_l7", 10000, 300000},
static const struct reg_init_data pm8010_p300_reg_init_data[] = {
	{0x55, 0x8A},
	{0x77, 0x03},
};

static const struct reg_init_data pm8010_p600_reg_init_data[] = {
	{0x76, 0x07},
	{0x77, 0x03},
};

/*
 * PM8010 LDOs 3, 4, and 6 can physically output a minimum of 1808 mV.  However,
 * 1504 mV is specified here to match PM8008 and to avoid the parent supply of
 * these regulators being stuck at an unnecessarily high voltage as a result of
 * the framework maintaining a minimum vote of 1808 mV + headroom at all times
 * (even when the LDOs are OFF).  This would waste power.  The LDO hardware
 * automatically rounds up programmed voltages to supported set points.
 */
static const struct regulator_data pm8010_reg_data[PM8008_MAX_LDO] = {
	/* name  parent      min_uv  max_uv  hpm_load  headroom_uv */
	{"l1", "vdd_l1_l2",  528000, 1544000, 30000, 100000},
	{"l2", "vdd_l1_l2",  528000, 1544000, 30000, 100000},
	{"l3", "vdd_l3_l4", 1504000, 3312000, 10000, 300000,
	 pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)},
	{"l4", "vdd_l3_l4", 1504000, 3312000, 10000, 300000,
	 pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)},
	{"l5", "vdd_l5",    1504000, 3544000, 10000, 300000,
	 pm8010_p600_reg_init_data, ARRAY_SIZE(pm8010_p600_reg_init_data)},
	{"l6", "vdd_l6",    1504000, 3312000, 10000, 300000,
	 pm8010_p300_reg_init_data, ARRAY_SIZE(pm8010_p300_reg_init_data)},
	{"l7", "vdd_l7",    1504000, 3544000, 10000, 300000,
	 pm8010_p600_reg_init_data, ARRAY_SIZE(pm8010_p600_reg_init_data)},
};

/* common functions */
@@ -121,7 +172,8 @@ static int pm8008_read(struct regmap *regmap, u16 reg, u8 *val, int count)
	return rc;
}

static int pm8008_write(struct regmap *regmap, u16 reg, u8 *val, int count)
static int pm8008_write(struct regmap *regmap, u16 reg, const u8 *val,
			int count)
{
	int rc;

@@ -559,6 +611,25 @@ static int pm8008_ldo_cb(struct notifier_block *nb, ulong event, void *data)
	return NOTIFY_OK;
}

static int pm8008_regulator_register_init(struct pm8008_regulator *pm8008_reg,
					  const struct regulator_data *reg_data)
{
	int i, rc;

	if (!reg_data->reg_init)
		return 0;

	for (i = 0; i < reg_data->reg_init_size; i++) {
		rc = pm8008_write(pm8008_reg->regmap,
			pm8008_reg->base + reg_data->reg_init[i].offset,
			&reg_data->reg_init[i].data, 1);
		if (rc < 0)
			return rc;
	}

	return 0;
}

static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
						const char *name)
{
@@ -567,13 +638,17 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
	struct device *dev = pm8008_reg->dev;
	struct device_node *reg_node = pm8008_reg->of_node;
	char buff[MAX_REG_NAME];
	const struct regulator_data *reg_data;
	int rc, i, init_voltage;
	u32 base = 0;
	u8 reg;

	reg_data = pm8008_reg->pmic_subtype == PM8008_SUBTYPE ? pm8008_reg_data
							      : pm8010_reg_data;

	/* get regulator data */
	for (i = 0; i < PM8008_MAX_LDO; i++)
		if (!strcmp(reg_data[i].name, name))
		if (strstr(name, reg_data[i].name))
			break;

	if (i == PM8008_MAX_LDO) {
@@ -588,6 +663,10 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
	}
	pm8008_reg->base = base;

	rc = pm8008_regulator_register_init(pm8008_reg, &reg_data[i]);
	if (rc)
		return rc;

	pm8008_reg->min_dropout_uv = reg_data[i].min_dropout_uv;
	of_property_read_u32(reg_node, "qcom,min-dropout-voltage",
					&pm8008_reg->min_dropout_uv);
@@ -622,6 +701,7 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
	}

	/* get slew rate */
	if (pm8008_reg->pmic_subtype == PM8008_SUBTYPE) {
		rc = pm8008_read(pm8008_reg->regmap,
				LDO_STEPPER_CTL_REG(pm8008_reg->base), &reg, 1);
		if (rc < 0) {
@@ -630,6 +710,9 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
			return rc;
		}
		pm8008_reg->step_rate = 38400 >> (reg & STEP_RATE_MASK);
	} else {
		pm8008_reg->step_rate = PM8010_STEP_RATE;
	}

	scnprintf(buff, MAX_REG_NAME, "%s-supply", reg_data[i].supply_name);
	if (of_find_property(dev->of_node, buff, NULL)) {
@@ -713,6 +796,18 @@ static int pm8008_register_ldo(struct pm8008_regulator *pm8008_reg,
	return 0;
}

static const struct of_device_id pm8008_regulator_match_table[] = {
	{
		.compatible	= "qcom,pm8008-regulator",
		.data		= (void *)(uintptr_t)PM8008_SUBTYPE,
	},
	{
		.compatible	= "qcom,pm8010-regulator",
		.data		= (void *)(uintptr_t)PM8010_SUBTYPE,
	},
	{ },
};

/* PMIC probe and helper function */
static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev)
{
@@ -720,8 +815,18 @@ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev)
	const char *name;
	struct device_node *child;
	struct pm8008_regulator *pm8008_reg;
	const struct of_device_id *match;
	enum pmic_subtype pmic_subtype;
	bool ocp;

	match = of_match_node(pm8008_regulator_match_table, dev->of_node);
	if (match) {
		pmic_subtype = (uintptr_t)match->data;
	} else {
		dev_err(dev, "could not find compatible string match\n");
		return -ENODEV;
	}

	ocp = of_property_read_bool(dev->of_node, "qcom,enable-ocp-broadcast");

	/* parse each subnode and register regulator for regulator child */
@@ -734,6 +839,7 @@ static int pm8008_parse_regulator(struct regmap *regmap, struct device *dev)
		pm8008_reg->of_node = child;
		pm8008_reg->dev = dev;
		pm8008_reg->enable_ocp_broadcast = ocp;
		pm8008_reg->pmic_subtype = pmic_subtype;

		rc = of_property_read_string(child, "regulator-name", &name);
		if (rc)
@@ -923,13 +1029,6 @@ static int pm8008_chip_remove(struct platform_device *pdev)
	return 0;
}

static const struct of_device_id pm8008_regulator_match_table[] = {
	{
		.compatible	= "qcom,pm8008-regulator",
	},
	{ },
};

static struct platform_driver pm8008_regulator_driver = {
	.driver	= {
		.name		= "qcom,pm8008-regulator",