allwinner: Convert AXP803 regulator setup code into a driver

Previously, the A64/H5 and H6 platforms' PMIC setup code was entirely
independent. However, some H6 boards also need early regulator setup.

Most of the register interface and all of the device tree traversal code
can be reused between the AXP803 and AXP805. The main difference is the
hardware bus interface, so that part is left to the platforms. The
remainder is moved into a driver.

I factored out the bits that were obviously specific to the AXP803;
additional changes for compatibility with other PMICs can be made as
needed.

The only functional change is that rsb_init() now checks the PMIC's chip
ID register against the expected value. This was already being done in
the H6 version of the code.

Signed-off-by: Samuel Holland <samuel@sholland.org>
Change-Id: Icdcf9edd6565f78cccc503922405129ac27e08a2
diff --git a/drivers/allwinner/axp/common.c b/drivers/allwinner/axp/common.c
new file mode 100644
index 0000000..a021e1c
--- /dev/null
+++ b/drivers/allwinner/axp/common.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+
+#include <libfdt.h>
+
+#include <common/debug.h>
+#include <drivers/allwinner/axp.h>
+
+int axp_check_id(void)
+{
+	int ret;
+
+	ret = axp_read(0x03);
+	if (ret < 0)
+		return ret;
+
+	ret &= 0xcf;
+	if (ret != axp_chip_id) {
+		ERROR("PMIC: Found unknown PMIC %02x\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask)
+{
+	uint8_t val;
+	int ret;
+
+	ret = axp_read(reg);
+	if (ret < 0)
+		return ret;
+
+	val = (ret & ~clr_mask) | set_mask;
+
+	return axp_write(reg, val);
+}
+
+void axp_power_off(void)
+{
+	/* Set "power disable control" bit */
+	axp_setbits(0x32, BIT(7));
+}
+
+/*
+ * Retrieve the voltage from a given regulator DTB node.
+ * Both the regulator-{min,max}-microvolt properties must be present and
+ * have the same value. Return that value in millivolts.
+ */
+static int fdt_get_regulator_millivolt(const void *fdt, int node)
+{
+	const fdt32_t *prop;
+	uint32_t min_volt;
+
+	prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL);
+	if (prop == NULL)
+		return -EINVAL;
+	min_volt = fdt32_to_cpu(*prop);
+
+	prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL);
+	if (prop == NULL)
+		return -EINVAL;
+
+	if (fdt32_to_cpu(*prop) != min_volt)
+		return -EINVAL;
+
+	return min_volt / 1000;
+}
+
+static int setup_regulator(const void *fdt, int node,
+			   const struct axp_regulator *reg)
+{
+	uint8_t val;
+	int mvolt;
+
+	mvolt = fdt_get_regulator_millivolt(fdt, node);
+	if (mvolt < reg->min_volt || mvolt > reg->max_volt)
+		return -EINVAL;
+
+	val = (mvolt / reg->step) - (reg->min_volt / reg->step);
+	if (val > reg->split)
+		val = ((val - reg->split) / 2) + reg->split;
+
+	axp_write(reg->volt_reg, val);
+	axp_setbits(reg->switch_reg, BIT(reg->switch_bit));
+
+	INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name,
+	     mvolt / 1000, mvolt % 1000);
+
+	return 0;
+}
+
+static bool should_enable_regulator(const void *fdt, int node)
+{
+	if (fdt_getprop(fdt, node, "phandle", NULL) != NULL)
+		return true;
+	if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL)
+		return true;
+	return false;
+}
+
+void axp_setup_regulators(const void *fdt)
+{
+	int node;
+	bool dc1sw = false;
+
+	if (fdt == NULL)
+		return;
+
+	/* locate the PMIC DT node, bail out if not found */
+	node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible);
+	if (node < 0) {
+		WARN("PMIC: No PMIC DT node, skipping setup\n");
+		return;
+	}
+
+	if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL)) {
+		axp_clrbits(0x8f, BIT(4));
+		axp_setbits(0x30, BIT(2));
+		INFO("PMIC: Enabling DRIVEVBUS\n");
+	}
+
+	/* descend into the "regulators" subnode */
+	node = fdt_subnode_offset(fdt, node, "regulators");
+	if (node < 0) {
+		WARN("PMIC: No regulators DT node, skipping setup\n");
+		return;
+	}
+
+	/* iterate over all regulators to find used ones */
+	fdt_for_each_subnode(node, fdt, node) {
+		const struct axp_regulator *reg;
+		const char *name;
+		int length;
+
+		/* We only care if it's always on or referenced. */
+		if (!should_enable_regulator(fdt, node))
+			continue;
+
+		name = fdt_get_name(fdt, node, &length);
+		for (reg = axp_regulators; reg->dt_name; reg++) {
+			if (!strncmp(name, reg->dt_name, length)) {
+				setup_regulator(fdt, node, reg);
+				break;
+			}
+		}
+
+		if (!strncmp(name, "dc1sw", length)) {
+			/* Delay DC1SW enablement to avoid overheating. */
+			dc1sw = true;
+			continue;
+		}
+	}
+
+	/*
+	 * If DLDO2 is enabled after DC1SW, the PMIC overheats and shuts
+	 * down. So always enable DC1SW as the very last regulator.
+	 */
+	if (dc1sw) {
+		INFO("PMIC: Enabling DC1SW\n");
+		axp_setbits(0x12, BIT(7));
+	}
+}