[PATCH v4 00/16] rockchip: add support for SARADCv2 and RK806 PMIC and regulators

The RK3588 has a new IP for SARADC compared to older SoCs of the same vendor but most of the boilerplate is shared, so rockchip-saradc.c driver is adapted instead of creating a new one.
Because the SARADC requires a vref-supply which is commonly coming from a regulator of the RK806 PMIC, support for the RK806 PMIC and regulators are also added in this patch series. Finally, RK806 PMIC is the first one to actually support SPI as communication layer between the SoC and the PMIC (but supports I2C as well according to the datasheet). This adds support for RK806 over SPI though there should be no reason for RK806 over I2C to not work.
Note that the DT now uses a different prefix for the buck and ldo node names and the RK806 is the first one to use nldo and pldo and while they both are LDOs, they have overlapping indices which makes it impossible to simply reuse the rk8xx_ldo driver and need to define new ones (though some callbacks can be reused, as is done in this patch series).
The SARADC was tested by toggling the BIOS button connected over channel 0 on RK3588 Jaguar and using the adc or button commands to watch over the value. The PMIC was tested with the pmic command for reading the CHIP_VERS registers and writing to the WDT_REG (0x73). The regulators were tested by using a modified DT for RK3588 Jaguar and playing with the regulator command to enable/disable some buck/nldo/pldo and modify their output voltage.
This has a light dependency on https://lore.kernel.org/u-boot/20240221-jaguar-v3-0-1f256a82201b@theobroma-s... because of: - remove of asm/io.h include in favor of asm/arch-rockchip/hardware.h (both should be included if applied before the dependency) - Jaguar defconfig modified in this patch series is added in the aforementioned patch series,
Signed-off-by: Quentin Schulz quentin.schulz@theobroma-systems.com --- Changes in v4: - use uint8_t instead of char to fix clang's -Wsingle-bit-bitfield-constant-conversion for the members of rk806_cmd data structure, they all are storing unsigned numbers anyway, - Link to v3: https://lore.kernel.org/r/20240304-rk3588-saradc-v3-0-7424e2ed5d3b@theobroma...
Changes in v3: - added ifdef safeguards for the spi sections in the rk8xx PMIC driver, - removed SPL_PMIC_RK8XXX from Jaguar defconfig since it's doing nothing at the moment (issue around missing Kconfig symbol dependency, will be fixed in another patch series), - Link to v2: https://lore.kernel.org/r/20240223-rk3588-saradc-v2-0-11c046b7e94a@theobroma...
Changes in v2: - fixed incorrect bitmasking when selecting channel - added optional controller reset to match downstream U-Boot and both downstream and upstream Linux kernel logic, - Link to v1: https://lore.kernel.org/r/20240221-rk3588-saradc-v1-0-c39a5601d1cc@theobroma...
--- Quentin Schulz (15): rockchip: spi: rk_spi: do not write bytes when in read-only mode regulator: rk8xx: remove unused functions regulator: rk8xx: add indirection level for some ldo callbacks power: rk8xx: add support for RK806 pmic: reword help text rockchip: adc: rockchip-saradc: use union for preparing for v2 rockchip: adc: rockchip-saradc: factor out channel_data callback rockchip: adc: rockchip-saradc: factor out start_channel callback rockchip: adc: rockchip-saradc: factor out stop callback rockchip: adc: rockchip-saradc: add support for RK3588 power: pmic: rk8xx: fix duplicate prompt rockchip: jaguar-rk3588: enable SARADC and derivatives adc: add missing depends on ADC for controller drivers button: add missing ADC dependency for BUTTON_ADC rockchip: boot_mode: fix rockchip_dnl_key_pressed requiring ADC support
William Wu (1): regulator: rk8xx: fix SWITCH enable on RK809
arch/arm/mach-rockchip/boot_mode.c | 4 + cmd/pmic.c | 4 +- configs/jaguar-rk3588_defconfig | 10 +- drivers/adc/Kconfig | 4 + drivers/adc/rockchip-saradc.c | 202 +++++++++++-- drivers/button/Kconfig | 1 + drivers/power/pmic/Kconfig | 2 +- drivers/power/pmic/rk8xx.c | 91 ++++++ drivers/power/regulator/rk8xx.c | 595 ++++++++++++++++++++++++++++++++++--- drivers/spi/rk_spi.c | 20 +- include/power/rk8xx_pmic.h | 21 +- 11 files changed, 879 insertions(+), 75 deletions(-) --- base-commit: 40e06db4c0a9893d613adbe002e794115397a150 change-id: 20240212-rk3588-saradc-ab0a9bda9306
Best regards,

From: Quentin Schulz quentin.schulz@theobroma-systems.com
The read-only mode is currently supported but only for 16b-aligned buffers. For unaligned buffers, the last byte will be read in RW mode right now, which isn't what is desired. Instead, let's put the controller back into RO mode for that last byte and skip any write in the xfer loop.
This is required for 3-wire SPI mode where PICO/POCI lanes are shorted on HW level. This incidentally the recommended design for RK806 PMIC for RK3588 products.
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 --- drivers/spi/rk_spi.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c index 7de943356ad..c8694fdff95 100644 --- a/drivers/spi/rk_spi.c +++ b/drivers/spi/rk_spi.c @@ -453,8 +453,17 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, * case of read-only transfers by using the full 16bits of each * FIFO element. */ - if (!out) + if (!out) { ret = rockchip_spi_16bit_reader(dev, &in, &len); + /* + * If "in" isn't 16b-aligned, we need to send the last byte + * ourselves. We however need to have the controller in RO mode + * which differs from the default. + */ + clrsetbits_le32(®s->ctrlr0, + TMOD_MASK << TMOD_SHIFT, + TMOD_RO << TMOD_SHIFT); + }
/* This is the original 8bit reader/writer code */ while (len > 0) { @@ -465,12 +474,13 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, rkspi_enable_chip(regs, true);
toread = todo; - towrite = todo; + /* Only write if we have something to write */ + towrite = out ? todo : 0; while (toread || towrite) { u32 status = readl(®s->sr);
if (towrite && !(status & SR_TF_FULL)) { - writel(out ? *out++ : 0, regs->txdr); + writel(*out++, regs->txdr); towrite--; } if (toread && !(status & SR_RF_EMPT)) { @@ -501,6 +511,10 @@ static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, spi_cs_deactivate(dev, slave_plat->cs);
rkspi_enable_chip(regs, false); + if (!out) + clrsetbits_le32(®s->ctrlr0, + TMOD_MASK << TMOD_SHIFT, + TMOD_TR << TMOD_SHIFT);
return ret; }

From: Quentin Schulz quentin.schulz@theobroma-systems.com
Those two functions had their last user removed in commit f9c68a566c4d ("rockchip: phycore_rk3288: remove phycore_init() function") part of v2023.01 release, so let's do some cleanup here.
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 --- drivers/power/regulator/rk8xx.c | 31 ------------------------------- include/power/rk8xx_pmic.h | 2 -- 2 files changed, 33 deletions(-)
diff --git a/drivers/power/regulator/rk8xx.c b/drivers/power/regulator/rk8xx.c index e80bd6c3723..97d73ac95e0 100644 --- a/drivers/power/regulator/rk8xx.c +++ b/drivers/power/regulator/rk8xx.c @@ -210,14 +210,6 @@ static const struct rk8xx_reg_info rk818_ldo[] = { }; #endif
-static const u16 rk818_chrg_cur_input_array[] = { - 450, 800, 850, 1000, 1250, 1500, 1750, 2000, 2250, 2500, 2750, 3000 -}; - -static const uint rk818_chrg_shutdown_vsel_array[] = { - 2780000, 2850000, 2920000, 2990000, 3060000, 3130000, 3190000, 3260000 -}; - static const struct rk8xx_reg_info *get_buck_reg(struct udevice *pmic, int num, int uvolt) { @@ -1160,26 +1152,3 @@ int rk8xx_spl_configure_buck(struct udevice *pmic, int buck, int uvolt)
return _buck_set_enable(pmic, buck, true); } - -int rk818_spl_configure_usb_input_current(struct udevice *pmic, int current_ma) -{ - uint i; - - for (i = 0; i < ARRAY_SIZE(rk818_chrg_cur_input_array); i++) - if (current_ma <= rk818_chrg_cur_input_array[i]) - break; - - return pmic_clrsetbits(pmic, REG_USB_CTRL, RK818_USB_ILIM_SEL_MASK, i); -} - -int rk818_spl_configure_usb_chrg_shutdown(struct udevice *pmic, int uvolt) -{ - uint i; - - for (i = 0; i < ARRAY_SIZE(rk818_chrg_shutdown_vsel_array); i++) - if (uvolt <= rk818_chrg_shutdown_vsel_array[i]) - break; - - return pmic_clrsetbits(pmic, REG_USB_CTRL, RK818_USB_CHG_SD_VSEL_MASK, - i); -} diff --git a/include/power/rk8xx_pmic.h b/include/power/rk8xx_pmic.h index 3cbfc021956..0db82419d4f 100644 --- a/include/power/rk8xx_pmic.h +++ b/include/power/rk8xx_pmic.h @@ -233,7 +233,5 @@ struct rk8xx_priv { };
int rk8xx_spl_configure_buck(struct udevice *pmic, int buck, int uvolt); -int rk818_spl_configure_usb_input_current(struct udevice *pmic, int current_ma); -int rk818_spl_configure_usb_chrg_shutdown(struct udevice *pmic, int uvolt);
#endif

From: William Wu william.wu@rock-chips.com
On RK809 in PMIC_POWER_ENX registers, in order to set or clear a bit N, the bit at offset N + 4 needs to be set otherwise nothing is done.
This fixes the inability to modify the SWITCH state on RK809.
Cc: Quentin Schulz foss+uboot@0leil.net Signed-off-by: William Wu william.wu@rock-chips.com [reworded commit log] Reviewed-by: Kever Yang kever.yang@rock-chips.com Signed-off-by: Quentin Schulz quentin.schulz@theobroma-systems.com --- drivers/power/regulator/rk8xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/regulator/rk8xx.c b/drivers/power/regulator/rk8xx.c index 97d73ac95e0..e905df3a800 100644 --- a/drivers/power/regulator/rk8xx.c +++ b/drivers/power/regulator/rk8xx.c @@ -901,7 +901,7 @@ static int switch_set_enable(struct udevice *dev, bool enable) case RK809_ID: mask = (1 << (sw + 2)) | (1 << (sw + 6)); ret = pmic_clrsetbits(dev->parent, RK817_POWER_EN(3), mask, - enable ? mask : 0); + enable ? mask : (1 << (sw + 6))); break; case RK818_ID: mask = 1 << 6;

From: Quentin Schulz quentin.schulz@theobroma-systems.com
By passing a rk8xx_reg_info directly to the internal get_value, it'd be possible to call this same function with a logic for getting the rk8xx_reg_info different from the current get_ldo_reg, e.g. for NLDO and PLDO support for RK806.
No logic change is expected.
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 --- drivers/power/regulator/rk8xx.c | 48 ++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-)
diff --git a/drivers/power/regulator/rk8xx.c b/drivers/power/regulator/rk8xx.c index e905df3a800..212bb752a76 100644 --- a/drivers/power/regulator/rk8xx.c +++ b/drivers/power/regulator/rk8xx.c @@ -780,10 +780,8 @@ static int buck_get_enable(struct udevice *dev) return _buck_get_enable(dev->parent, buck); }
-static int ldo_get_value(struct udevice *dev) +static int _ldo_get_value(struct udevice *dev, const struct rk8xx_reg_info *info) { - int ldo = dev->driver_data - 1; - const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, 0); int mask = info->vsel_mask; int ret, val;
@@ -797,10 +795,16 @@ static int ldo_get_value(struct udevice *dev) return info->min_uv + val * info->step_uv; }
-static int ldo_set_value(struct udevice *dev, int uvolt) +static int ldo_get_value(struct udevice *dev) { int ldo = dev->driver_data - 1; - const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, uvolt); + const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, 0); + + return _ldo_get_value(dev, info); +} + +static int _ldo_set_value(struct udevice *dev, const struct rk8xx_reg_info *info, int uvolt) +{ int mask = info->vsel_mask; int val;
@@ -812,16 +816,22 @@ static int ldo_set_value(struct udevice *dev, int uvolt) else val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel;
- debug("%s: volt=%d, ldo=%d, reg=0x%x, mask=0x%x, val=0x%x\n", - __func__, uvolt, ldo + 1, info->vsel_reg, mask, val); + debug("%s: volt=%d, reg=0x%x, mask=0x%x, val=0x%x\n", + __func__, uvolt, info->vsel_reg, mask, val);
return pmic_clrsetbits(dev->parent, info->vsel_reg, mask, val); }
-static int ldo_set_suspend_value(struct udevice *dev, int uvolt) +static int ldo_set_value(struct udevice *dev, int uvolt) { int ldo = dev->driver_data - 1; const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, uvolt); + + return _ldo_set_value(dev, info, uvolt); +} + +static int _ldo_set_suspend_value(struct udevice *dev, const struct rk8xx_reg_info *info, int uvolt) +{ int mask = info->vsel_mask; int val;
@@ -833,16 +843,22 @@ static int ldo_set_suspend_value(struct udevice *dev, int uvolt) else val = ((uvolt - info->min_uv) / info->step_uv) + info->min_sel;
- debug("%s: volt=%d, ldo=%d, reg=0x%x, mask=0x%x, val=0x%x\n", - __func__, uvolt, ldo + 1, info->vsel_sleep_reg, mask, val); + debug("%s: volt=%d, reg=0x%x, mask=0x%x, val=0x%x\n", + __func__, uvolt, info->vsel_sleep_reg, mask, val);
return pmic_clrsetbits(dev->parent, info->vsel_sleep_reg, mask, val); }
-static int ldo_get_suspend_value(struct udevice *dev) +static int ldo_set_suspend_value(struct udevice *dev, int uvolt) { int ldo = dev->driver_data - 1; - const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, 0); + const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, uvolt); + + return _ldo_set_suspend_value(dev->parent, info, uvolt); +} + +static int _ldo_get_suspend_value(struct udevice *dev, const struct rk8xx_reg_info *info) +{ int mask = info->vsel_mask; int val, ret;
@@ -858,6 +874,14 @@ static int ldo_get_suspend_value(struct udevice *dev) return info->min_uv + val * info->step_uv; }
+static int ldo_get_suspend_value(struct udevice *dev) +{ + int ldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_ldo_reg(dev->parent, ldo, 0); + + return _ldo_get_suspend_value(dev->parent, info); +} + static int ldo_set_enable(struct udevice *dev, bool enable) { int ldo = dev->driver_data - 1;

From: Quentin Schulz quentin.schulz@theobroma-systems.com
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 --- drivers/power/pmic/rk8xx.c | 91 +++++++ drivers/power/regulator/rk8xx.c | 514 +++++++++++++++++++++++++++++++++++++++- include/power/rk8xx_pmic.h | 19 ++ 3 files changed, 622 insertions(+), 2 deletions(-)
diff --git a/drivers/power/pmic/rk8xx.c b/drivers/power/pmic/rk8xx.c index 4e3a17337ee..3a8261d1749 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 @@ static int rk8xx_sysreset_request(struct udevice *dev, enum sysreset_t type) 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 @@ void rk8xx_off_for_plugin(struct udevice *dev) } }
+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 struct reg_data rk817_init_reg[] = {
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 @@ static int rk8xx_reg_count(struct udevice *dev) 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 @@ static int rk8xx_read(struct udevice *dev, uint reg, 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 = 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 @@ static int rk8xx_probe(struct udevice *dev) 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 @@ static int rk8xx_probe(struct udevice *dev) 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 struct dm_pmic_ops rk8xx_ops = {
static const struct udevice_id rk8xx_ids[] = { { .compatible = "rockchip,rk805" }, + { .compatible = "rockchip,rk806" }, { .compatible = "rockchip,rk808" }, { .compatible = "rockchip,rk809" }, { .compatible = "rockchip,rk816" }, diff --git a/drivers/power/regulator/rk8xx.c b/drivers/power/regulator/rk8xx.c index 212bb752a76..1bd4605d43a 100644 --- a/drivers/power/regulator/rk8xx.c +++ b/drivers/power/regulator/rk8xx.c @@ -25,6 +25,19 @@ #define NA 0xff
/* Field Definitions */ +#define RK806_BUCK_CONFIG(n) (0x10 + (n) - 1) +#define RK806_BUCK_ON_VSEL(n) (0x1a + (n) - 1) +#define RK806_BUCK_SLP_VSEL(n) (0x24 + (n) - 1) +#define RK806_BUCK_VSEL_MASK 0xff + +#define RK806_NLDO_ON_VSEL(n) (0x43 + (n) - 1) +#define RK806_NLDO_SLP_VSEL(n) (0x48 + (n) - 1) +#define RK806_NLDO_VSEL_MASK 0xff + +#define RK806_PLDO_ON_VSEL(n) (0x4e + (n) - 1) +#define RK806_PLDO_SLP_VSEL(n) (0x54 + (n) - 1) +#define RK806_PLDO_VSEL_MASK 0xff + #define RK808_BUCK_VSEL_MASK 0x3f #define RK808_BUCK4_VSEL_MASK 0xf #define RK808_LDO_VSEL_MASK 0x1f @@ -91,6 +104,49 @@ struct rk8xx_reg_info { u8 max_sel; };
+static const struct rk8xx_reg_info rk806_buck[] = { + /* buck 1 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(1), RK806_BUCK_SLP_VSEL(1), RK806_BUCK_CONFIG(1), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(1), RK806_BUCK_SLP_VSEL(1), RK806_BUCK_CONFIG(1), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(1), RK806_BUCK_SLP_VSEL(1), RK806_BUCK_CONFIG(1), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 2 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(2), RK806_BUCK_SLP_VSEL(2), RK806_BUCK_CONFIG(2), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(2), RK806_BUCK_SLP_VSEL(2), RK806_BUCK_CONFIG(2), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(2), RK806_BUCK_SLP_VSEL(2), RK806_BUCK_CONFIG(2), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 3 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(3), RK806_BUCK_SLP_VSEL(3), RK806_BUCK_CONFIG(3), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(3), RK806_BUCK_SLP_VSEL(3), RK806_BUCK_CONFIG(3), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(3), RK806_BUCK_SLP_VSEL(3), RK806_BUCK_CONFIG(3), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 4 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(4), RK806_BUCK_SLP_VSEL(4), RK806_BUCK_CONFIG(4), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(4), RK806_BUCK_SLP_VSEL(4), RK806_BUCK_CONFIG(4), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(4), RK806_BUCK_SLP_VSEL(4), RK806_BUCK_CONFIG(4), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 5 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(5), RK806_BUCK_SLP_VSEL(5), RK806_BUCK_CONFIG(5), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(5), RK806_BUCK_SLP_VSEL(5), RK806_BUCK_CONFIG(5), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(5), RK806_BUCK_SLP_VSEL(5), RK806_BUCK_CONFIG(5), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 6 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(6), RK806_BUCK_SLP_VSEL(6), RK806_BUCK_CONFIG(6), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(6), RK806_BUCK_SLP_VSEL(6), RK806_BUCK_CONFIG(6), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(6), RK806_BUCK_SLP_VSEL(6), RK806_BUCK_CONFIG(6), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 7 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(7), RK806_BUCK_SLP_VSEL(7), RK806_BUCK_CONFIG(7), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(7), RK806_BUCK_SLP_VSEL(7), RK806_BUCK_CONFIG(7), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(7), RK806_BUCK_SLP_VSEL(7), RK806_BUCK_CONFIG(7), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 8 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(8), RK806_BUCK_SLP_VSEL(8), RK806_BUCK_CONFIG(8), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(8), RK806_BUCK_SLP_VSEL(8), RK806_BUCK_CONFIG(8), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(8), RK806_BUCK_SLP_VSEL(8), RK806_BUCK_CONFIG(8), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 9 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(9), RK806_BUCK_SLP_VSEL(9), RK806_BUCK_CONFIG(9), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(9), RK806_BUCK_SLP_VSEL(9), RK806_BUCK_CONFIG(9), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(9), RK806_BUCK_SLP_VSEL(9), RK806_BUCK_CONFIG(9), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, + /* buck 10 */ + { 500000, 6250, RK806_BUCK_ON_VSEL(10), RK806_BUCK_SLP_VSEL(10), RK806_BUCK_CONFIG(10), RK806_BUCK_VSEL_MASK, 0x00, 0x9f }, + { 1500000, 25000, RK806_BUCK_ON_VSEL(10), RK806_BUCK_SLP_VSEL(10), RK806_BUCK_CONFIG(10), RK806_BUCK_VSEL_MASK, 0xa0, 0xeb }, + { 3400000, 0, RK806_BUCK_ON_VSEL(10), RK806_BUCK_SLP_VSEL(10), RK806_BUCK_CONFIG(10), RK806_BUCK_VSEL_MASK, 0xec, 0xff }, +}; + static const struct rk8xx_reg_info rk808_buck[] = { { 712500, 12500, REG_BUCK1_ON_VSEL, REG_BUCK1_SLP_VSEL, REG_BUCK1_CONFIG, RK808_BUCK_VSEL_MASK, 0x00, 0x3f }, { 712500, 12500, REG_BUCK2_ON_VSEL, REG_BUCK2_SLP_VSEL, REG_BUCK2_CONFIG, RK808_BUCK_VSEL_MASK, 0x00, 0x3f }, @@ -148,6 +204,45 @@ static const struct rk8xx_reg_info rk818_buck[] = { };
#ifdef ENABLE_DRIVER +static const struct rk8xx_reg_info rk806_nldo[] = { + /* nldo 1 */ + { 500000, 12500, RK806_NLDO_ON_VSEL(1), RK806_NLDO_SLP_VSEL(1), NA, RK806_NLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_NLDO_ON_VSEL(1), RK806_NLDO_SLP_VSEL(1), NA, RK806_NLDO_VSEL_MASK, 0xe8, 0xff}, + /* nldo 2 */ + { 500000, 12500, RK806_NLDO_ON_VSEL(2), RK806_NLDO_SLP_VSEL(2), NA, RK806_NLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_NLDO_ON_VSEL(2), RK806_NLDO_SLP_VSEL(2), NA, RK806_NLDO_VSEL_MASK, 0xe8, 0xff}, + /* nldo 3 */ + { 500000, 12500, RK806_NLDO_ON_VSEL(3), RK806_NLDO_SLP_VSEL(3), NA, RK806_NLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_NLDO_ON_VSEL(3), RK806_NLDO_SLP_VSEL(3), NA, RK806_NLDO_VSEL_MASK, 0xe8, 0xff}, + /* nldo 4 */ + { 500000, 12500, RK806_NLDO_ON_VSEL(4), RK806_NLDO_SLP_VSEL(4), NA, RK806_NLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_NLDO_ON_VSEL(4), RK806_NLDO_SLP_VSEL(4), NA, RK806_NLDO_VSEL_MASK, 0xe8, 0xff}, + /* nldo 5 */ + { 500000, 12500, RK806_NLDO_ON_VSEL(5), RK806_NLDO_SLP_VSEL(5), NA, RK806_NLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_NLDO_ON_VSEL(5), RK806_NLDO_SLP_VSEL(5), NA, RK806_NLDO_VSEL_MASK, 0xe8, 0xff}, +}; + +static const struct rk8xx_reg_info rk806_pldo[] = { + /* pldo 1 */ + { 500000, 12500, RK806_PLDO_ON_VSEL(1), RK806_PLDO_SLP_VSEL(1), NA, RK806_PLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_PLDO_ON_VSEL(1), RK806_PLDO_SLP_VSEL(1), NA, RK806_PLDO_VSEL_MASK, 0xe8, 0xff}, + /* pldo 2 */ + { 500000, 12500, RK806_PLDO_ON_VSEL(2), RK806_PLDO_SLP_VSEL(2), NA, RK806_PLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_PLDO_ON_VSEL(2), RK806_PLDO_SLP_VSEL(2), NA, RK806_PLDO_VSEL_MASK, 0xe8, 0xff}, + /* pldo 3 */ + { 500000, 12500, RK806_PLDO_ON_VSEL(3), RK806_PLDO_SLP_VSEL(3), NA, RK806_PLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_PLDO_ON_VSEL(3), RK806_PLDO_SLP_VSEL(3), NA, RK806_PLDO_VSEL_MASK, 0xe8, 0xff}, + /* pldo 4 */ + { 500000, 12500, RK806_PLDO_ON_VSEL(4), RK806_PLDO_SLP_VSEL(4), NA, RK806_PLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_PLDO_ON_VSEL(4), RK806_PLDO_SLP_VSEL(4), NA, RK806_PLDO_VSEL_MASK, 0xe8, 0xff}, + /* pldo 5 */ + { 500000, 12500, RK806_PLDO_ON_VSEL(5), RK806_PLDO_SLP_VSEL(5), NA, RK806_PLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_PLDO_ON_VSEL(5), RK806_PLDO_SLP_VSEL(5), NA, RK806_PLDO_VSEL_MASK, 0xe8, 0xff}, + /* pldo 6 */ + { 500000, 12500, RK806_PLDO_ON_VSEL(6), RK806_PLDO_SLP_VSEL(6), NA, RK806_PLDO_VSEL_MASK, 0x00, 0xe7}, + { 3400000, 0, RK806_PLDO_ON_VSEL(6), RK806_PLDO_SLP_VSEL(6), NA, RK806_PLDO_VSEL_MASK, 0xe8, 0xff}, +}; + static const struct rk8xx_reg_info rk808_ldo[] = { { 1800000, 100000, REG_LDO1_ON_VSEL, REG_LDO1_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, { 1800000, 100000, REG_LDO2_ON_VSEL, REG_LDO2_SLP_VSEL, NA, RK808_LDO_VSEL_MASK, }, @@ -230,7 +325,12 @@ static const struct rk8xx_reg_info *get_buck_reg(struct udevice *pmic, default: return &rk816_buck[num + 4]; } - + case RK806_ID: + if (uvolt < 1500000) + return &rk806_buck[num * 3 + 0]; + else if (uvolt < 3400000) + return &rk806_buck[num * 3 + 1]; + return &rk806_buck[num * 3 + 2]; case RK809_ID: case RK817_ID: switch (num) { @@ -314,7 +414,11 @@ static int _buck_set_enable(struct udevice *pmic, int buck, bool enable) value = ((0 << buck) | (1 << (buck + 4))); ret = pmic_reg_write(pmic, en_reg, value); break; - + case RK806_ID: + value = RK806_POWER_EN_CLRSETBITS(buck % 4, enable); + en_reg = RK806_POWER_EN((buck + 1) / 4); + ret = pmic_reg_write(pmic, en_reg, value); + break; case RK808_ID: case RK818_ID: mask = 1 << buck; @@ -389,6 +493,10 @@ static int _buck_get_enable(struct udevice *pmic, int buck) ret = pmic_reg_read(pmic, RK816_REG_DCDC_EN1); } break; + case RK806_ID: + mask = BIT(buck % 4); + ret = pmic_reg_read(pmic, RK806_POWER_EN((buck + 1) / 4)); + break; case RK808_ID: case RK818_ID: mask = 1 << buck; @@ -428,6 +536,20 @@ static int _buck_set_suspend_enable(struct udevice *pmic, int buck, bool enable) ret = pmic_clrsetbits(pmic, RK816_REG_DCDC_SLP_EN, mask, enable ? mask : 0); break; + case RK806_ID: + { + u8 reg; + + if (buck + 1 >= 9) { + reg = RK806_POWER_SLP_EN1; + mask = BIT(buck + 1 - 3); + } else { + reg = RK806_POWER_SLP_EN0; + mask = BIT(buck + 1); + } + ret = pmic_clrsetbits(pmic, reg, mask, enable ? mask : 0); + } + break; case RK808_ID: case RK818_ID: mask = 1 << buck; @@ -465,6 +587,21 @@ static int _buck_get_suspend_enable(struct udevice *pmic, int buck) return val; ret = val & mask ? 1 : 0; break; + case RK806_ID: + { + u8 reg; + + if (buck + 1 >= 9) { + reg = RK806_POWER_SLP_EN1; + mask = BIT(buck + 1 - 3); + } else { + reg = RK806_POWER_SLP_EN0; + mask = BIT(buck + 1); + } + val = pmic_reg_read(pmic, reg); + } + ret = (val & mask) ? 1 : 0; + break; case RK808_ID: case RK818_ID: mask = 1 << buck; @@ -514,6 +651,34 @@ static const struct rk8xx_reg_info *get_ldo_reg(struct udevice *pmic, } }
+static const struct rk8xx_reg_info *get_nldo_reg(struct udevice *pmic, + int num, int uvolt) +{ + const struct rk8xx_priv *priv = dev_get_priv(pmic); + + switch (priv->variant) { + case RK806_ID: + default: + if (uvolt < 3400000) + return &rk806_nldo[num * 2 + 0]; + return &rk806_nldo[num * 2 + 1]; + } +} + +static const struct rk8xx_reg_info *get_pldo_reg(struct udevice *pmic, + int num, int uvolt) +{ + const struct rk8xx_priv *priv = dev_get_priv(pmic); + + switch (priv->variant) { + case RK806_ID: + default: + if (uvolt < 3400000) + return &rk806_pldo[num * 2 + 0]; + return &rk806_pldo[num * 2 + 1]; + } +} + static int _ldo_get_enable(struct udevice *pmic, int ldo) { struct rk8xx_priv *priv = dev_get_priv(pmic); @@ -561,6 +726,63 @@ static int _ldo_get_enable(struct udevice *pmic, int ldo) return ret & mask ? true : false; }
+static int _nldo_get_enable(struct udevice *pmic, int nldo) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask = 0; + int ret = 0; + u8 en_reg = 0; + + switch (priv->variant) { + case RK806_ID: + default: + if (nldo + 1 >= 5) { + mask = BIT(2); + en_reg = RK806_POWER_EN(5); + } else { + mask = BIT(nldo); + en_reg = RK806_POWER_EN(3); + } + ret = pmic_reg_read(pmic, en_reg); + break; + } + + if (ret < 0) + return ret; + + return (ret & mask) ? 1 : 0; +} + +static int _pldo_get_enable(struct udevice *pmic, int pldo) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask = 0; + int ret = 0; + u8 en_reg = 0; + + switch (priv->variant) { + case RK806_ID: + default: + if (pldo + 1 <= 3) { + mask = BIT(pldo + 1); + en_reg = RK806_POWER_EN(4); + } else if (pldo + 1 == 6) { + mask = BIT(0); + en_reg = RK806_POWER_EN(4); + } else { + mask = BIT((pldo + 1) % 4); + en_reg = RK806_POWER_EN(5); + } + ret = pmic_reg_read(pmic, en_reg); + break; + } + + if (ret < 0) + return ret; + + return (ret & mask) ? 1 : 0; +} + static int _ldo_set_enable(struct udevice *pmic, int ldo, bool enable) { struct rk8xx_priv *priv = dev_get_priv(pmic); @@ -616,6 +838,62 @@ static int _ldo_set_enable(struct udevice *pmic, int ldo, bool enable) return ret; }
+static int _nldo_set_enable(struct udevice *pmic, int nldo, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint value, en_reg; + int ret = 0; + + switch (priv->variant) { + case RK806_ID: + default: + if (nldo + 1 >= 5) { + value = RK806_POWER_EN_CLRSETBITS(2, enable); + en_reg = RK806_POWER_EN(5); + } else { + value = RK806_POWER_EN_CLRSETBITS(nldo, enable); + en_reg = RK806_POWER_EN(3); + } + ret = pmic_reg_write(pmic, en_reg, value); + break; + } + + if (enable) + udelay(500); + + return ret; +} + +static int _pldo_set_enable(struct udevice *pmic, int pldo, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint value, en_reg; + int ret = 0; + + switch (priv->variant) { + case RK806_ID: + default: + /* PLDO */ + if (pldo + 1 <= 3) { + value = RK806_POWER_EN_CLRSETBITS(pldo + 1, enable); + en_reg = RK806_POWER_EN(4); + } else if (pldo + 1 == 6) { + value = RK806_POWER_EN_CLRSETBITS(0, enable); + en_reg = RK806_POWER_EN(4); + } else { + value = RK806_POWER_EN_CLRSETBITS((pldo + 1) % 4, enable); + en_reg = RK806_POWER_EN(5); + } + ret = pmic_reg_write(pmic, en_reg, value); + break; + } + + if (enable) + udelay(500); + + return ret; +} + static int _ldo_set_suspend_enable(struct udevice *pmic, int ldo, bool enable) { struct rk8xx_priv *priv = dev_get_priv(pmic); @@ -652,6 +930,43 @@ static int _ldo_set_suspend_enable(struct udevice *pmic, int ldo, bool enable) return ret; }
+static int _nldo_set_suspend_enable(struct udevice *pmic, int nldo, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask; + int ret = 0; + + switch (priv->variant) { + case RK806_ID: + default: + mask = BIT(nldo); + ret = pmic_clrsetbits(pmic, RK806_POWER_SLP_EN1, mask, enable ? mask : 0); + break; + } + + return ret; +} + +static int _pldo_set_suspend_enable(struct udevice *pmic, int pldo, bool enable) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + uint mask; + int ret = 0; + + switch (priv->variant) { + case RK806_ID: + default: + if (pldo + 1 >= 6) + mask = BIT(0); + else + mask = BIT(pldo + 1); + ret = pmic_clrsetbits(pmic, RK806_POWER_SLP_EN2, mask, enable ? mask : 0); + break; + } + + return ret; +} + static int _ldo_get_suspend_enable(struct udevice *pmic, int ldo) { struct rk8xx_priv *priv = dev_get_priv(pmic); @@ -696,6 +1011,45 @@ static int _ldo_get_suspend_enable(struct udevice *pmic, int ldo) return ret; }
+static int _nldo_get_suspend_enable(struct udevice *pmic, int nldo) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + int val, ret = 0; + uint mask; + + switch (priv->variant) { + case RK806_ID: + default: + mask = BIT(nldo); + val = pmic_reg_read(pmic, RK806_POWER_SLP_EN1); + ret = (val & mask) ? 1 : 0; + break; + } + + return ret; +} + +static int _pldo_get_suspend_enable(struct udevice *pmic, int pldo) +{ + struct rk8xx_priv *priv = dev_get_priv(pmic); + int val, ret = 0; + uint mask; + + switch (priv->variant) { + case RK806_ID: + default: + if (pldo + 1 >= 6) + mask = BIT(0); + else + mask = BIT(pldo + 1); + val = pmic_reg_read(pmic, RK806_POWER_SLP_EN2); + ret = (val & mask) ? 1 : 0; + break; + } + + return ret; +} + static int buck_get_value(struct udevice *dev) { int buck = dev->driver_data - 1; @@ -803,6 +1157,22 @@ static int ldo_get_value(struct udevice *dev) return _ldo_get_value(dev, info); }
+static int nldo_get_value(struct udevice *dev) +{ + int nldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_nldo_reg(dev->parent, nldo, 0); + + return _ldo_get_value(dev, info); +} + +static int pldo_get_value(struct udevice *dev) +{ + int pldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_pldo_reg(dev->parent, pldo, 0); + + return _ldo_get_value(dev, info); +} + static int _ldo_set_value(struct udevice *dev, const struct rk8xx_reg_info *info, int uvolt) { int mask = info->vsel_mask; @@ -830,6 +1200,22 @@ static int ldo_set_value(struct udevice *dev, int uvolt) return _ldo_set_value(dev, info, uvolt); }
+static int nldo_set_value(struct udevice *dev, int uvolt) +{ + int nldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_nldo_reg(dev->parent, nldo, uvolt); + + return _ldo_set_value(dev, info, uvolt); +} + +static int pldo_set_value(struct udevice *dev, int uvolt) +{ + int pldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_pldo_reg(dev->parent, pldo, uvolt); + + return _ldo_set_value(dev, info, uvolt); +} + static int _ldo_set_suspend_value(struct udevice *dev, const struct rk8xx_reg_info *info, int uvolt) { int mask = info->vsel_mask; @@ -857,6 +1243,22 @@ static int ldo_set_suspend_value(struct udevice *dev, int uvolt) return _ldo_set_suspend_value(dev->parent, info, uvolt); }
+static int nldo_set_suspend_value(struct udevice *dev, int uvolt) +{ + int nldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_nldo_reg(dev->parent, nldo, uvolt); + + return _ldo_set_suspend_value(dev->parent, info, uvolt); +} + +static int pldo_set_suspend_value(struct udevice *dev, int uvolt) +{ + int pldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_pldo_reg(dev->parent, pldo, uvolt); + + return _ldo_set_suspend_value(dev->parent, info, uvolt); +} + static int _ldo_get_suspend_value(struct udevice *dev, const struct rk8xx_reg_info *info) { int mask = info->vsel_mask; @@ -882,6 +1284,22 @@ static int ldo_get_suspend_value(struct udevice *dev) return _ldo_get_suspend_value(dev->parent, info); }
+static int nldo_get_suspend_value(struct udevice *dev) +{ + int nldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_nldo_reg(dev->parent, nldo, 0); + + return _ldo_get_suspend_value(dev->parent, info); +} + +static int pldo_get_suspend_value(struct udevice *dev) +{ + int pldo = dev->driver_data - 1; + const struct rk8xx_reg_info *info = get_pldo_reg(dev->parent, pldo, 0); + + return _ldo_get_suspend_value(dev->parent, info); +} + static int ldo_set_enable(struct udevice *dev, bool enable) { int ldo = dev->driver_data - 1; @@ -889,6 +1307,20 @@ static int ldo_set_enable(struct udevice *dev, bool enable) return _ldo_set_enable(dev->parent, ldo, enable); }
+static int nldo_set_enable(struct udevice *dev, bool enable) +{ + int nldo = dev->driver_data - 1; + + return _nldo_set_enable(dev->parent, nldo, enable); +} + +static int pldo_set_enable(struct udevice *dev, bool enable) +{ + int pldo = dev->driver_data - 1; + + return _pldo_set_enable(dev->parent, pldo, enable); +} + static int ldo_set_suspend_enable(struct udevice *dev, bool enable) { int ldo = dev->driver_data - 1; @@ -896,6 +1328,20 @@ static int ldo_set_suspend_enable(struct udevice *dev, bool enable) return _ldo_set_suspend_enable(dev->parent, ldo, enable); }
+static int nldo_set_suspend_enable(struct udevice *dev, bool enable) +{ + int nldo = dev->driver_data - 1; + + return _nldo_set_suspend_enable(dev->parent, nldo, enable); +} + +static int pldo_set_suspend_enable(struct udevice *dev, bool enable) +{ + int pldo = dev->driver_data - 1; + + return _pldo_set_suspend_enable(dev->parent, pldo, enable); +} + static int ldo_get_suspend_enable(struct udevice *dev) { int ldo = dev->driver_data - 1; @@ -903,6 +1349,20 @@ static int ldo_get_suspend_enable(struct udevice *dev) return _ldo_get_suspend_enable(dev->parent, ldo); }
+static int nldo_get_suspend_enable(struct udevice *dev) +{ + int nldo = dev->driver_data - 1; + + return _nldo_get_suspend_enable(dev->parent, nldo); +} + +static int pldo_get_suspend_enable(struct udevice *dev) +{ + int pldo = dev->driver_data - 1; + + return _pldo_get_suspend_enable(dev->parent, pldo); +} + static int ldo_get_enable(struct udevice *dev) { int ldo = dev->driver_data - 1; @@ -910,6 +1370,20 @@ static int ldo_get_enable(struct udevice *dev) return _ldo_get_enable(dev->parent, ldo); }
+static int nldo_get_enable(struct udevice *dev) +{ + int nldo = dev->driver_data - 1; + + return _nldo_get_enable(dev->parent, nldo); +} + +static int pldo_get_enable(struct udevice *dev) +{ + int pldo = dev->driver_data - 1; + + return _pldo_get_enable(dev->parent, pldo); +} + static int switch_set_enable(struct udevice *dev, bool enable) { struct rk8xx_priv *priv = dev_get_priv(dev->parent); @@ -1133,6 +1607,28 @@ static const struct dm_regulator_ops rk8xx_ldo_ops = { .get_suspend_enable = ldo_get_suspend_enable, };
+static const struct dm_regulator_ops rk8xx_nldo_ops = { + .get_value = nldo_get_value, + .set_value = nldo_set_value, + .set_suspend_value = nldo_set_suspend_value, + .get_suspend_value = nldo_get_suspend_value, + .get_enable = nldo_get_enable, + .set_enable = nldo_set_enable, + .set_suspend_enable = nldo_set_suspend_enable, + .get_suspend_enable = nldo_get_suspend_enable, +}; + +static const struct dm_regulator_ops rk8xx_pldo_ops = { + .get_value = pldo_get_value, + .set_value = pldo_set_value, + .set_suspend_value = pldo_set_suspend_value, + .get_suspend_value = pldo_get_suspend_value, + .get_enable = pldo_get_enable, + .set_enable = pldo_set_enable, + .set_suspend_enable = pldo_set_suspend_enable, + .get_suspend_enable = pldo_get_suspend_enable, +}; + static const struct dm_regulator_ops rk8xx_switch_ops = { .get_value = switch_get_value, .set_value = switch_set_value, @@ -1158,6 +1654,20 @@ U_BOOT_DRIVER(rk8xx_ldo) = { .probe = rk8xx_ldo_probe, };
+U_BOOT_DRIVER(rk8xx_nldo) = { + .name = "rk8xx_nldo", + .id = UCLASS_REGULATOR, + .ops = &rk8xx_nldo_ops, + .probe = rk8xx_ldo_probe, +}; + +U_BOOT_DRIVER(rk8xx_pldo) = { + .name = "rk8xx_pldo", + .id = UCLASS_REGULATOR, + .ops = &rk8xx_pldo_ops, + .probe = rk8xx_ldo_probe, +}; + U_BOOT_DRIVER(rk8xx_switch) = { .name = "rk8xx_switch", .id = UCLASS_REGULATOR, diff --git a/include/power/rk8xx_pmic.h b/include/power/rk8xx_pmic.h index 0db82419d4f..31221aa46b6 100644 --- a/include/power/rk8xx_pmic.h +++ b/include/power/rk8xx_pmic.h @@ -182,8 +182,19 @@ enum { RK816_REG_LDO_EN2, };
+enum { + RK806_POWER_SLP_EN0 = 0x06, + RK806_POWER_SLP_EN1, + RK806_POWER_SLP_EN2, + RK806_REG_SYS_CFG3 = 0x72, + RK806_WDT_REG, + RK806_ON_SOURCE, + RK806_OFF_SOURCE +}; + enum { RK805_ID = 0x8050, + RK806_ID = 0x8060, RK808_ID = 0x0000, RK809_ID = 0x8090, RK816_ID = 0x8160, @@ -201,6 +212,14 @@ enum { #define RK817_POWER_EN_SAVE0 0x99 #define RK817_POWER_EN_SAVE1 0xa4
+#define RK806_POWER_EN(x) (0x00 + (x)) +/* POWER_ENx register lower 4 bits are write-protected unless the associated top bit is set */ +#define RK806_POWER_EN_CLRSETBITS(bit, val) (((val) << (bit)) | (1 << ((bit) + 4))) + +#define RK806_POWER_SLP_EN(x) (0x06 + (x)) + +#define RK806_ID_MSB 0x5a +#define RK806_ID_LSB 0x5b #define RK817_ID_MSB 0xed #define RK817_ID_LSB 0xee #define RK8XX_ID_MSK 0xfff0

From: Quentin Schulz quentin.schulz@theobroma-systems.com
Reword the help text for the pmic read and pmic write commands to better match what's expected from the user.
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 --- cmd/pmic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cmd/pmic.c b/cmd/pmic.c index 49a405fa297..c9e9730adf9 100644 --- a/cmd/pmic.c +++ b/cmd/pmic.c @@ -225,6 +225,6 @@ U_BOOT_CMD(pmic, CONFIG_SYS_MAXARGS, 1, do_pmic, "list - list pmic devices\n" "pmic dev [name] - show or [set] operating PMIC device\n" "pmic dump - dump registers\n" - "pmic read address - read byte of register at address\n" - "pmic write address - write byte to register at address\n" + "pmic read <reg> - read byte of 'reg' register\n" + "pmic write <reg> <byte> - write 'byte' byte to 'reg' register\n" );

From: Quentin Schulz quentin.schulz@theobroma-systems.com
The registers are entirely different between SARADC v1 and SARADC v2, so let's prepare to add another struct for accessing v2 registers by adding a union.
Cc: Quentin Schulz quentin.schulz@theobroma-systems.com Reviewed-by: Kever Yang kever.yang@rock-chips.com Signed-off-by: Quentin Schulz quentin.schulz@theobroma-systems.com --- drivers/adc/rockchip-saradc.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index 03caca78b5f..4a842556a60 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -23,13 +23,16 @@
#define SARADC_TIMEOUT (100 * 1000)
-struct rockchip_saradc_regs { +struct rockchip_saradc_regs_v1 { unsigned int data; unsigned int stas; unsigned int ctrl; unsigned int dly_pu_soc; };
+union rockchip_saradc_regs { + struct rockchip_saradc_regs_v1 *v1; +}; struct rockchip_saradc_data { int num_bits; int num_channels; @@ -37,7 +40,7 @@ struct rockchip_saradc_data { };
struct rockchip_saradc_priv { - struct rockchip_saradc_regs *regs; + union rockchip_saradc_regs regs; int active_channel; const struct rockchip_saradc_data *data; }; @@ -53,16 +56,16 @@ int rockchip_saradc_channel_data(struct udevice *dev, int channel, return -EINVAL; }
- if ((readl(&priv->regs->ctrl) & SARADC_CTRL_IRQ_STATUS) != + if ((readl(&priv->regs.v1->ctrl) & SARADC_CTRL_IRQ_STATUS) != SARADC_CTRL_IRQ_STATUS) return -EBUSY;
/* Read value */ - *data = readl(&priv->regs->data); + *data = readl(&priv->regs.v1->data); *data &= uc_pdata->data_mask;
/* Power down adc */ - writel(0, &priv->regs->ctrl); + writel(0, &priv->regs.v1->ctrl);
return 0; } @@ -77,11 +80,11 @@ int rockchip_saradc_start_channel(struct udevice *dev, int channel) }
/* 8 clock periods as delay between power up and start cmd */ - writel(8, &priv->regs->dly_pu_soc); + writel(8, &priv->regs.v1->dly_pu_soc);
/* Select the channel to be used and trigger conversion */ writel(SARADC_CTRL_POWER_CTRL | (channel & SARADC_CTRL_CHN_MASK) | - SARADC_CTRL_IRQ_ENABLE, &priv->regs->ctrl); + SARADC_CTRL_IRQ_ENABLE, &priv->regs.v1->ctrl);
priv->active_channel = channel;
@@ -93,7 +96,7 @@ int rockchip_saradc_stop(struct udevice *dev) struct rockchip_saradc_priv *priv = dev_get_priv(dev);
/* Power down adc */ - writel(0, &priv->regs->ctrl); + writel(0, &priv->regs.v1->ctrl);
priv->active_channel = -1;
@@ -146,8 +149,8 @@ int rockchip_saradc_of_to_plat(struct udevice *dev) struct rockchip_saradc_data *data;
data = (struct rockchip_saradc_data *)dev_get_driver_data(dev); - priv->regs = dev_read_addr_ptr(dev); - if (!priv->regs) { + priv->regs.v1 = dev_read_addr_ptr(dev); + if (!priv->regs.v1) { pr_err("Dev: %s - can't get address!", dev->name); return -EINVAL; }

From: Quentin Schulz quentin.schulz@theobroma-systems.com
SARADC v1 and v2 have a different way of reading data, therefore let's abstract this function so that it can be provided from the udevice.data pointer.
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 --- drivers/adc/rockchip-saradc.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index 4a842556a60..1cc57beecc2 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -37,6 +37,7 @@ struct rockchip_saradc_data { int num_bits; int num_channels; unsigned long clk_rate; + int (*channel_data)(struct udevice *dev, int channel, unsigned int *data); };
struct rockchip_saradc_priv { @@ -45,28 +46,45 @@ struct rockchip_saradc_priv { const struct rockchip_saradc_data *data; };
+int rockchip_saradc_channel_data_v1(struct udevice *dev, int channel, + unsigned int *data) +{ + struct rockchip_saradc_priv *priv = dev_get_priv(dev); + + if ((readl(&priv->regs.v1->ctrl) & SARADC_CTRL_IRQ_STATUS) != + SARADC_CTRL_IRQ_STATUS) + return -EBUSY; + + /* Read value */ + *data = readl(&priv->regs.v1->data); + + /* Power down adc */ + writel(0, &priv->regs.v1->ctrl); + + return 0; +} + int rockchip_saradc_channel_data(struct udevice *dev, int channel, unsigned int *data) { struct rockchip_saradc_priv *priv = dev_get_priv(dev); struct adc_uclass_plat *uc_pdata = dev_get_uclass_plat(dev); + int ret;
if (channel != priv->active_channel) { pr_err("Requested channel is not active!"); return -EINVAL; }
- if ((readl(&priv->regs.v1->ctrl) & SARADC_CTRL_IRQ_STATUS) != - SARADC_CTRL_IRQ_STATUS) - return -EBUSY; + ret = priv->data->channel_data(dev, channel, data); + if (ret) { + if (ret != -EBUSY) + pr_err("Error reading channel data, %d!", ret); + return ret; + }
- /* Read value */ - *data = readl(&priv->regs.v1->data); *data &= uc_pdata->data_mask;
- /* Power down adc */ - writel(0, &priv->regs.v1->ctrl); - return 0; }
@@ -174,18 +192,21 @@ static const struct rockchip_saradc_data saradc_data = { .num_bits = 10, .num_channels = 3, .clk_rate = 1000000, + .channel_data = rockchip_saradc_channel_data_v1, };
static const struct rockchip_saradc_data rk3066_tsadc_data = { .num_bits = 12, .num_channels = 2, .clk_rate = 50000, + .channel_data = rockchip_saradc_channel_data_v1, };
static const struct rockchip_saradc_data rk3399_saradc_data = { .num_bits = 10, .num_channels = 6, .clk_rate = 1000000, + .channel_data = rockchip_saradc_channel_data_v1, };
static const struct udevice_id rockchip_saradc_ids[] = {

From: Quentin Schulz quentin.schulz@theobroma-systems.com
SARADC v1 and v2 have a different way of starting a channel, therefore let's abstract this function so that it can be provided from the udevice.data pointer.
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 --- drivers/adc/rockchip-saradc.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-)
diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index 1cc57beecc2..607d10b5b70 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -38,6 +38,7 @@ struct rockchip_saradc_data { int num_channels; unsigned long clk_rate; int (*channel_data)(struct udevice *dev, int channel, unsigned int *data); + int (*start_channel)(struct udevice *dev, int channel); };
struct rockchip_saradc_priv { @@ -88,15 +89,10 @@ int rockchip_saradc_channel_data(struct udevice *dev, int channel, return 0; }
-int rockchip_saradc_start_channel(struct udevice *dev, int channel) +int rockchip_saradc_start_channel_v1(struct udevice *dev, int channel) { struct rockchip_saradc_priv *priv = dev_get_priv(dev);
- if (channel < 0 || channel >= priv->data->num_channels) { - pr_err("Requested channel is invalid!"); - return -EINVAL; - } - /* 8 clock periods as delay between power up and start cmd */ writel(8, &priv->regs.v1->dly_pu_soc);
@@ -104,6 +100,25 @@ int rockchip_saradc_start_channel(struct udevice *dev, int channel) writel(SARADC_CTRL_POWER_CTRL | (channel & SARADC_CTRL_CHN_MASK) | SARADC_CTRL_IRQ_ENABLE, &priv->regs.v1->ctrl);
+ return 0; +} + +int rockchip_saradc_start_channel(struct udevice *dev, int channel) +{ + struct rockchip_saradc_priv *priv = dev_get_priv(dev); + int ret; + + if (channel < 0 || channel >= priv->data->num_channels) { + pr_err("Requested channel is invalid!"); + return -EINVAL; + } + + ret = priv->data->start_channel(dev, channel); + if (ret) { + pr_err("Error starting channel, %d!", ret); + return ret; + } + priv->active_channel = channel;
return 0; @@ -193,6 +208,7 @@ static const struct rockchip_saradc_data saradc_data = { .num_channels = 3, .clk_rate = 1000000, .channel_data = rockchip_saradc_channel_data_v1, + .start_channel = rockchip_saradc_start_channel_v1, };
static const struct rockchip_saradc_data rk3066_tsadc_data = { @@ -200,6 +216,7 @@ static const struct rockchip_saradc_data rk3066_tsadc_data = { .num_channels = 2, .clk_rate = 50000, .channel_data = rockchip_saradc_channel_data_v1, + .start_channel = rockchip_saradc_start_channel_v1, };
static const struct rockchip_saradc_data rk3399_saradc_data = { @@ -207,6 +224,7 @@ static const struct rockchip_saradc_data rk3399_saradc_data = { .num_channels = 6, .clk_rate = 1000000, .channel_data = rockchip_saradc_channel_data_v1, + .start_channel = rockchip_saradc_start_channel_v1, };
static const struct udevice_id rockchip_saradc_ids[] = {

From: Quentin Schulz quentin.schulz@theobroma-systems.com
SARADC v2 doesn't have a stop mechanism once in single mode. In series conversion, the logic is different anyway. Therefore, let's abstract this function so that it can be provided from the udevice.data pointer.
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 --- drivers/adc/rockchip-saradc.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index 607d10b5b70..b5df58fe3eb 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -39,6 +39,7 @@ struct rockchip_saradc_data { unsigned long clk_rate; int (*channel_data)(struct udevice *dev, int channel, unsigned int *data); int (*start_channel)(struct udevice *dev, int channel); + int (*stop)(struct udevice *dev); };
struct rockchip_saradc_priv { @@ -124,13 +125,29 @@ int rockchip_saradc_start_channel(struct udevice *dev, int channel) return 0; }
-int rockchip_saradc_stop(struct udevice *dev) +int rockchip_saradc_stop_v1(struct udevice *dev) { struct rockchip_saradc_priv *priv = dev_get_priv(dev);
/* Power down adc */ writel(0, &priv->regs.v1->ctrl);
+ return 0; +} + +int rockchip_saradc_stop(struct udevice *dev) +{ + struct rockchip_saradc_priv *priv = dev_get_priv(dev); + + if (priv->data->stop) { + int ret = priv->data->stop(dev); + + if (ret) { + pr_err("Error stopping channel, %d!", ret); + return ret; + } + } + priv->active_channel = -1;
return 0; @@ -209,6 +226,7 @@ static const struct rockchip_saradc_data saradc_data = { .clk_rate = 1000000, .channel_data = rockchip_saradc_channel_data_v1, .start_channel = rockchip_saradc_start_channel_v1, + .stop = rockchip_saradc_stop_v1, };
static const struct rockchip_saradc_data rk3066_tsadc_data = { @@ -217,6 +235,7 @@ static const struct rockchip_saradc_data rk3066_tsadc_data = { .clk_rate = 50000, .channel_data = rockchip_saradc_channel_data_v1, .start_channel = rockchip_saradc_start_channel_v1, + .stop = rockchip_saradc_stop_v1, };
static const struct rockchip_saradc_data rk3399_saradc_data = { @@ -225,6 +244,7 @@ static const struct rockchip_saradc_data rk3399_saradc_data = { .clk_rate = 1000000, .channel_data = rockchip_saradc_channel_data_v1, .start_channel = rockchip_saradc_start_channel_v1, + .stop = rockchip_saradc_stop_v1, };
static const struct udevice_id rockchip_saradc_ids[] = {

From: Quentin Schulz quentin.schulz@theobroma-systems.com
This adds support for the SARADCv2 found on RK3588.
There is no stop callback as it is currently configured in single conversion mode, where the ADC is powered down after a single conversion has been made.
Due to what seems to be a silicon bug, a controller reset needs to be issued before starting a channel conversion otherwise Rockchip says that channel 1 will error whatever that means. This is aligned with upstream and downstream Linux kernel as well as downstream U-Boot.
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 --- drivers/adc/rockchip-saradc.c | 102 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-)
diff --git a/drivers/adc/rockchip-saradc.c b/drivers/adc/rockchip-saradc.c index b5df58fe3eb..10ded1b088f 100644 --- a/drivers/adc/rockchip-saradc.c +++ b/drivers/adc/rockchip-saradc.c @@ -10,12 +10,17 @@ #include <clk.h> #include <dm.h> #include <errno.h> -#include <asm/io.h> +#include <reset.h> +#include <asm/arch-rockchip/hardware.h> +#include <linux/bitfield.h> #include <linux/bitops.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/printk.h> #include <power/regulator.h>
+#define usleep_range(a, b) udelay((b)) + #define SARADC_CTRL_CHN_MASK GENMASK(2, 0) #define SARADC_CTRL_POWER_CTRL BIT(3) #define SARADC_CTRL_IRQ_ENABLE BIT(5) @@ -30,8 +35,37 @@ struct rockchip_saradc_regs_v1 { unsigned int dly_pu_soc; };
+struct rockchip_saradc_regs_v2 { + unsigned int conv_con; +#define SARADC2_SINGLE_MODE BIT(5) +#define SARADC2_START BIT(4) +#define SARADC2_CONV_CHANNELS GENMASK(3, 0) + unsigned int t_pd_soc; + unsigned int t_as_soc; + unsigned int t_das_soc; + unsigned int t_sel_soc; + unsigned int high_comp[16]; + unsigned int low_comp[16]; + unsigned int debounce; + unsigned int ht_int_en; + unsigned int lt_int_en; + unsigned int reserved[24]; + unsigned int mt_int_en; + unsigned int end_int_en; +#define SARADC2_EN_END_INT BIT(0) + unsigned int st_con; + unsigned int status; + unsigned int end_int_st; + unsigned int ht_int_st; + unsigned int lt_int_st; + unsigned int mt_int_st; + unsigned int data[16]; + unsigned int auto_ch_en; +}; + union rockchip_saradc_regs { struct rockchip_saradc_regs_v1 *v1; + struct rockchip_saradc_regs_v2 *v2; }; struct rockchip_saradc_data { int num_bits; @@ -46,6 +80,7 @@ struct rockchip_saradc_priv { union rockchip_saradc_regs regs; int active_channel; const struct rockchip_saradc_data *data; + struct reset_ctl *reset; };
int rockchip_saradc_channel_data_v1(struct udevice *dev, int channel, @@ -66,6 +101,22 @@ int rockchip_saradc_channel_data_v1(struct udevice *dev, int channel, return 0; }
+int rockchip_saradc_channel_data_v2(struct udevice *dev, int channel, + unsigned int *data) +{ + struct rockchip_saradc_priv *priv = dev_get_priv(dev); + + if (!(readl(&priv->regs.v2->end_int_st) & SARADC2_EN_END_INT)) + return -EBUSY; + + /* Read value */ + *data = readl(&priv->regs.v2->data[channel]); + + /* Acknowledge the interrupt */ + writel(SARADC2_EN_END_INT, &priv->regs.v2->end_int_st); + + return 0; +} int rockchip_saradc_channel_data(struct udevice *dev, int channel, unsigned int *data) { @@ -104,6 +155,40 @@ int rockchip_saradc_start_channel_v1(struct udevice *dev, int channel) return 0; }
+static void rockchip_saradc_reset_controller(struct reset_ctl *reset) +{ + reset_assert(reset); + usleep_range(10, 20); + reset_deassert(reset); +} + +int rockchip_saradc_start_channel_v2(struct udevice *dev, int channel) +{ + struct rockchip_saradc_priv *priv = dev_get_priv(dev); + + /* + * Downstream says + * """If read other chn at anytime, then chn1 will error, assert + * controller as a workaround.""" + */ + if (priv->reset) + rockchip_saradc_reset_controller(priv->reset); + + writel(0xc, &priv->regs.v2->t_das_soc); + writel(0x20, &priv->regs.v2->t_pd_soc); + + /* Acknowledge any previous interrupt */ + writel(SARADC2_EN_END_INT, &priv->regs.v2->end_int_st); + + rk_clrsetreg(&priv->regs.v2->conv_con, + SARADC2_CONV_CHANNELS | SARADC2_START | SARADC2_SINGLE_MODE, + FIELD_PREP(SARADC2_CONV_CHANNELS, channel) | + FIELD_PREP(SARADC2_START, 1) | + FIELD_PREP(SARADC2_SINGLE_MODE, 1)); + + return 0; +} + int rockchip_saradc_start_channel(struct udevice *dev, int channel) { struct rockchip_saradc_priv *priv = dev_get_priv(dev); @@ -162,6 +247,8 @@ int rockchip_saradc_probe(struct udevice *dev) int vref_uv; int ret;
+ priv->reset = devm_reset_control_get_optional(dev, "saradc-apb"); + ret = clk_get_by_index(dev, 0, &clk); if (ret) return ret; @@ -178,6 +265,9 @@ int rockchip_saradc_probe(struct udevice *dev) return ret; }
+ if (priv->reset) + rockchip_saradc_reset_controller(priv->reset); + vref_uv = regulator_get_value(vref); if (vref_uv < 0) { printf("can't get vref-supply value: %d\n", vref_uv); @@ -247,6 +337,14 @@ static const struct rockchip_saradc_data rk3399_saradc_data = { .stop = rockchip_saradc_stop_v1, };
+static const struct rockchip_saradc_data rk3588_saradc_data = { + .num_bits = 12, + .num_channels = 8, + .clk_rate = 1000000, + .channel_data = rockchip_saradc_channel_data_v2, + .start_channel = rockchip_saradc_start_channel_v2, +}; + static const struct udevice_id rockchip_saradc_ids[] = { { .compatible = "rockchip,saradc", .data = (ulong)&saradc_data }, @@ -254,6 +352,8 @@ static const struct udevice_id rockchip_saradc_ids[] = { .data = (ulong)&rk3066_tsadc_data }, { .compatible = "rockchip,rk3399-saradc", .data = (ulong)&rk3399_saradc_data }, + { .compatible = "rockchip,rk3588-saradc", + .data = (ulong)&rk3588_saradc_data }, { } };

From: Quentin Schulz quentin.schulz@theobroma-systems.com
SPL_PMIC_RK8XX and PMIC_RK8XX both share the same prompt making it difficult to know at first glance in menuconfig what's for what, let's fix this by adding "in SPL" at the end of the prompt for the SPL symbol.
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 --- drivers/power/pmic/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 9b61b18e11f..562c1a3b122 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -250,7 +250,7 @@ config PMIC_RK8XX This driver implements register read/write operations.
config SPL_PMIC_RK8XX - bool "Enable support for Rockchip PMIC RK8XX" + bool "Enable support for Rockchip PMIC RK8XX in SPL" depends on SPL_DM_PMIC ---help--- The Rockchip RK808 PMIC provides four buck DC-DC convertors, 8 LDOs,

From: Quentin Schulz quentin.schulz@theobroma-systems.com
The SARADC is used on Jaguar for multiple things: - channel 0 is used (at runtime) as a BIOS button, - channel 2 is exposed on the Mezzanine connector for customer specific logic, - channel 5 and 6 are used for identification,
Since the SARADC requires a vref-supply provided by the RK806 PMIC, its support and the support for its regulators are also enabled.
The button, adc, pmic and regulator commands are also enabled for CLI use in U-Boot for debugging and scripting purposes.
The RK806 PMIC on Jaguar being routed on the SPI bus, let's enable Rockchip SPI controller driver.
Finally, the SARADC channel 1 on Jaguar is hardwired so will never change in the lifetime of a unit, for that reason, disable the Rockchip Download Mode check by setting ROCKCHIP_BOOT_MODE_REG symbol to 0.
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 --- configs/jaguar-rk3588_defconfig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/configs/jaguar-rk3588_defconfig b/configs/jaguar-rk3588_defconfig index f55bfb1c82b..275d70ae008 100644 --- a/configs/jaguar-rk3588_defconfig +++ b/configs/jaguar-rk3588_defconfig @@ -15,6 +15,7 @@ CONFIG_ENV_SIZE=0x1f000 CONFIG_DEFAULT_DEVICE_TREE="rk3588-jaguar" CONFIG_ROCKCHIP_RK3588=y CONFIG_SPL_ROCKCHIP_COMMON_BOARD=y +CONFIG_ROCKCHIP_BOOT_MODE_REG=0x0 CONFIG_SPL_SERIAL=y CONFIG_SPL_STACK_R_ADDR=0x600000 CONFIG_TARGET_JAGUAR_RK3588=y @@ -47,6 +48,7 @@ CONFIG_SPL_ATF=y # CONFIG_BOOTM_RTEMS is not set # CONFIG_BOOTM_VXWORKS is not set # CONFIG_CMD_ELF is not set +CONFIG_CMD_ADC=y CONFIG_CMD_GPIO=y CONFIG_CMD_GPT=y CONFIG_CMD_I2C=y @@ -59,6 +61,7 @@ CONFIG_CMD_USB=y # CONFIG_CMD_MII is not set # CONFIG_CMD_BLOCK_CACHE is not set # CONFIG_CMD_EFICONFIG is not set +CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y CONFIG_CMD_EROFS=y CONFIG_CMD_SQUASHFS=y @@ -73,7 +76,8 @@ CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_SPL_REGMAP=y CONFIG_SPL_SYSCON=y -# CONFIG_SARADC_ROCKCHIP is not set +CONFIG_BUTTON=y +CONFIG_BUTTON_ADC=y CONFIG_SPL_CLK=y CONFIG_CLK_GPIO=y CONFIG_ROCKCHIP_GPIO=y @@ -101,10 +105,14 @@ CONFIG_DWC_ETH_QOS=y CONFIG_DWC_ETH_QOS_ROCKCHIP=y CONFIG_PHY_ROCKCHIP_INNO_USB2=y CONFIG_SPL_PINCTRL=y +CONFIG_DM_PMIC=y +CONFIG_PMIC_RK8XX=y +CONFIG_REGULATOR_RK8XX=y CONFIG_SPL_RAM=y CONFIG_SCSI=y CONFIG_DEBUG_UART_SHIFT=2 CONFIG_SYS_NS16550_MEM32=y +CONFIG_ROCKCHIP_SPI=y CONFIG_SYSRESET=y CONFIG_USB=y CONFIG_USB_EHCI_HCD=y

From: Quentin Schulz quentin.schulz@theobroma-systems.com
The ADC controller drivers are obviously all depending on ADC symbol being selected.
While they don't seem to fail to build without, they won't be useful without that symbol selected, so let's make sure the options aren't shown in menuconfig when ADC isn't selected.
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 --- drivers/adc/Kconfig | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/drivers/adc/Kconfig b/drivers/adc/Kconfig index a01d73846b7..c9cdbe6942d 100644 --- a/drivers/adc/Kconfig +++ b/drivers/adc/Kconfig @@ -13,6 +13,7 @@ config ADC
config ADC_EXYNOS bool "Enable Exynos 54xx ADC driver" + depends on ADC help This enables basic driver for Exynos ADC compatible with Exynos54xx. It provides: @@ -22,6 +23,7 @@ config ADC_EXYNOS
config ADC_SANDBOX bool "Enable Sandbox ADC test driver" + depends on ADC help This enables driver for Sandbox ADC device emulation. It provides: @@ -31,6 +33,7 @@ config ADC_SANDBOX
config SARADC_MESON bool "Enable Amlogic Meson SARADC driver" + depends on ADC imply REGMAP help This enables driver for Amlogic Meson SARADC. @@ -41,6 +44,7 @@ config SARADC_MESON
config SARADC_ROCKCHIP bool "Enable Rockchip SARADC driver" + depends on ADC help This enables driver for Rockchip SARADC. It provides:

From: Quentin Schulz quentin.schulz@theobroma-systems.com
The BUTTON_ADC symbol guards the compilation of button-adc driver whose name very well makes it explicit that it requires ADC support to be enabled.
Fix build issue of button-adc driver when ADC support isn't enabled by making sure it cannot be built without ADC support.
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 --- drivers/button/Kconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/button/Kconfig b/drivers/button/Kconfig index 097b05f822e..3918b05ae03 100644 --- a/drivers/button/Kconfig +++ b/drivers/button/Kconfig @@ -12,6 +12,7 @@ config BUTTON config BUTTON_ADC bool "Button adc" depends on BUTTON + depends on ADC help Enable support for buttons which are connected to Analog to Digital Converter device. The ADC driver must use driver model. Buttons are

From: Quentin Schulz quentin.schulz@theobroma-systems.com
ADC support is implied by the Rockchip arch Kconfig but that means it should be possible to disable ADC support and still be able to build.
However the weak implementation of rockchip_dnl_key_pressed() currently blindly use functions from the ADC subsystem which do not exist when ADC is not enabled, failing the build.
Therefore, let's encapsulate this logic with a check on the ADC symbol being selected.
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 --- arch/arm/mach-rockchip/boot_mode.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/arch/arm/mach-rockchip/boot_mode.c b/arch/arm/mach-rockchip/boot_mode.c index eb8f65ae4e9..f9be396aa55 100644 --- a/arch/arm/mach-rockchip/boot_mode.c +++ b/arch/arm/mach-rockchip/boot_mode.c @@ -40,6 +40,7 @@ void set_back_to_bootrom_dnl_flag(void)
__weak int rockchip_dnl_key_pressed(void) { +#if CONFIG_IS_ENABLED(ADC) unsigned int val; struct udevice *dev; struct uclass *uc; @@ -69,6 +70,9 @@ __weak int rockchip_dnl_key_pressed(void) return true; else return false; +#else + return false; +#endif }
void rockchip_dnl_mode_check(void)
participants (1)
-
Quentin Schulz