power: rk8xx: add support for RK806

This adds support for RK806, only the SPI variant has been tested.

The communication "protocol" over SPI is the following:
 - write three bytes:
   - 1 byte: [0:3] length of the payload, [6] Enable CRC, [7] Write
   - 1 byte: LSB register address
   - 1 byte: MSB register address
 - write/read length of payload

The CRC is always disabled for now.

The RK806 technically supports I2C as well, and this should be able to
support it without any change, but it wasn't tested.

The DT node name prefix for the buck converters has changed in the
Device Tree and is now dcdc-reg. The logic for buck converters is
however manageable within the current logic inside the rk8xx regulator
driver. The same cannot be said for the NLDO and PLDO.

Because pmic_bind_children() parses the DT nodes and extracts the LDO
index from the DT node name, NLDO and PLDO will have overlapping
indices. Therefore, we need a separate logic from the already-existing
ldo callbacks. Let's reuse as much as possible though.

Cc: Quentin Schulz <foss+uboot@0leil.net>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>
Signed-off-by: Quentin Schulz <quentin.schulz@theobroma-systems.com>
diff --git a/drivers/power/pmic/rk8xx.c b/drivers/power/pmic/rk8xx.c
index 4e3a173..3a8261d 100644
--- a/drivers/power/pmic/rk8xx.c
+++ b/drivers/power/pmic/rk8xx.c
@@ -9,8 +9,10 @@
 #include <dm/lists.h>
 #include <errno.h>
 #include <log.h>
+#include <linux/bitfield.h>
 #include <power/rk8xx_pmic.h>
 #include <power/pmic.h>
+#include <spi.h>
 #include <sysreset.h>
 
 static int rk8xx_sysreset_request(struct udevice *dev, enum sysreset_t type)
@@ -32,6 +34,10 @@
 		pmic_clrsetbits(dev->parent, RK817_REG_SYS_CFG3, 0,
 				BIT(0));
 		break;
+	case RK806_ID:
+		pmic_clrsetbits(dev->parent, RK806_REG_SYS_CFG3, 0,
+				BIT(0));
+		break;
 	default:
 		printf("Unknown PMIC RK%x: Cannot shutdown\n",
 		       priv->variant);
@@ -83,6 +89,11 @@
 	}
 }
 
+static struct reg_data rk806_init_reg[] = {
+	/* RST_FUN */
+	{ RK806_REG_SYS_CFG3, GENMASK(7, 6), BIT(7)},
+};
+
 static struct reg_data rk817_init_reg[] = {
 /* enable the under-voltage protection,
  * the under-voltage protection will shutdown the LDO3 and reset the PMIC
@@ -92,7 +103,10 @@
 
 static const struct pmic_child_info pmic_children_info[] = {
 	{ .prefix = "DCDC_REG", .driver = "rk8xx_buck"},
+	{ .prefix = "dcdc-reg", .driver = "rk8xx_buck"},
 	{ .prefix = "LDO_REG", .driver = "rk8xx_ldo"},
+	{ .prefix = "nldo-reg", .driver = "rk8xx_nldo"},
+	{ .prefix = "pldo-reg", .driver = "rk8xx_pldo"},
 	{ .prefix = "SWITCH_REG", .driver = "rk8xx_switch"},
 	{ },
 };
@@ -102,11 +116,51 @@
 	return RK808_NUM_OF_REGS;
 }
 
+#if CONFIG_IS_ENABLED(SPI) && CONFIG_IS_ENABLED(DM_SPI)
+struct rk806_cmd {
+	uint8_t	len: 4; /* Payload size in bytes - 1 */
+	uint8_t	reserved: 2;
+	uint8_t	crc_en: 1;
+	uint8_t	op: 1; /* READ=0; WRITE=1; */
+	uint8_t	reg_l;
+#define REG_L_MASK	GENMASK(7, 0)
+	uint8_t	reg_h;
+#define REG_H_MASK	GENMASK(15, 8)
+};
+#endif
+
 static int rk8xx_write(struct udevice *dev, uint reg, const uint8_t *buff,
 			  int len)
 {
 	int ret;
 
+#if CONFIG_IS_ENABLED(SPI) && CONFIG_IS_ENABLED(DM_SPI)
+	if (device_get_uclass_id(dev->parent) == UCLASS_SPI) {
+		struct spi_slave *spi = dev_get_parent_priv(dev);
+		struct rk806_cmd cmd = {
+			.op = 1,
+			.len = len - 1,
+			.reg_l = FIELD_GET(REG_L_MASK, reg),
+			.reg_h = FIELD_GET(REG_H_MASK, reg),
+		};
+
+		ret = dm_spi_claim_bus(dev);
+		if (ret) {
+			debug("Couldn't claim bus for device: %p!\n", dev);
+			return ret;
+		}
+
+		ret = spi_write_then_read(spi, (u8 *)&cmd, sizeof(cmd), buff, NULL, len);
+		if (ret)
+			debug("write error to device: %p register: %#x!\n",
+			      dev, reg);
+
+		dm_spi_release_bus(dev);
+
+		return ret;
+	}
+#endif
+
 	ret = dm_i2c_write(dev, reg, buff, len);
 	if (ret) {
 		debug("write error to device: %p register: %#x!\n", dev, reg);
@@ -120,6 +174,33 @@
 {
 	int ret;
 
+#if CONFIG_IS_ENABLED(SPI) && CONFIG_IS_ENABLED(DM_SPI)
+	if (device_get_uclass_id(dev->parent) == UCLASS_SPI) {
+		struct spi_slave *spi = dev_get_parent_priv(dev);
+		struct rk806_cmd cmd = {
+			.op = 0,
+			.len = len - 1,
+			.reg_l = FIELD_GET(REG_L_MASK, reg),
+			.reg_h = FIELD_GET(REG_H_MASK, reg),
+		};
+
+		ret = dm_spi_claim_bus(dev);
+		if (ret) {
+			debug("Couldn't claim bus for device: %p!\n", dev);
+			return ret;
+		}
+
+		ret = spi_write_then_read(spi, (u8 *)&cmd, sizeof(cmd), NULL, buff, len);
+		if (ret)
+			debug("read error to device: %p register: %#x!\n",
+			      dev, reg);
+
+		dm_spi_release_bus(dev);
+
+		return ret;
+	}
+#endif
+
 	ret = dm_i2c_read(dev, reg, buff, len);
 	if (ret) {
 		debug("read error from device: %p register: %#x!\n", dev, reg);
@@ -181,6 +262,9 @@
 	    device_is_compatible(dev, "rockchip,rk809")) {
 		id_msb = RK817_ID_MSB;
 		id_lsb = RK817_ID_LSB;
+	} else if (device_is_compatible(dev, "rockchip,rk806")) {
+		id_msb = RK806_ID_MSB;
+		id_lsb = RK806_ID_LSB;
 	} else {
 		id_msb = ID_MSB;
 		id_lsb = ID_LSB;
@@ -221,6 +305,12 @@
 		value = (power_en2 & 0x0f) | ((power_en3 & 0x0f) << 4);
 		pmic_reg_write(dev, RK817_POWER_EN_SAVE1, value);
 		break;
+	case RK806_ID:
+		on_source = RK806_ON_SOURCE;
+		off_source = RK806_OFF_SOURCE;
+		init_data = rk806_init_reg;
+		init_data_num = ARRAY_SIZE(rk806_init_reg);
+		break;
 	default:
 		printf("Unknown PMIC: RK%x!!\n", priv->variant);
 		return -EINVAL;
@@ -263,6 +353,7 @@
 
 static const struct udevice_id rk8xx_ids[] = {
 	{ .compatible = "rockchip,rk805" },
+	{ .compatible = "rockchip,rk806" },
 	{ .compatible = "rockchip,rk808" },
 	{ .compatible = "rockchip,rk809" },
 	{ .compatible = "rockchip,rk816" },