summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-07-30 08:50:13 -0600
committerTom Rini <[email protected]>2026-07-30 08:50:13 -0600
commit100e12ea78c73071b9710f08b32fd4590019266f (patch)
tree2cb18246d53827c2391fb40a4abb21214dfd0e87
parent94b349bd902d9e38e1846c157fadd9054c34680a (diff)
parent602726cb300992cd98ceea72e9ea08aa37a92a0c (diff)
Merge tag 'mmc-for-2026.10-rc2' of https://git.u-boot-project.org/u-boot/custodians/u-boot-mmc
CI: https://git.u-boot-project.org/u-boot/custodians/u-boot-mmc/-/pipelines/781 - Enable MMC CMD23 for multi-block transfers - Various fixes to mmc core - Add PF9453 pmic/regulator driver - Support SCP 4.36 map for renesas-r8a78000
-rw-r--r--drivers/mmc/mmc.c45
-rw-r--r--drivers/mmc/mmc_private.h4
-rw-r--r--drivers/mmc/mmc_write.c25
-rw-r--r--drivers/mmc/rpmb.c2
-rw-r--r--drivers/mmc/sdhci.c3
-rw-r--r--drivers/power/domain/renesas-r8a78000-power-domain.c4
-rw-r--r--drivers/power/pmic/Kconfig15
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/pf9453.c170
-rw-r--r--drivers/power/regulator/Kconfig15
-rw-r--r--drivers/power/regulator/Makefile1
-rw-r--r--drivers/power/regulator/pf9453.c256
-rw-r--r--include/mmc.h3
-rw-r--r--include/mvebu_mmc.h2
-rw-r--r--include/power/pf9453.h71
15 files changed, 593 insertions, 24 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 2e565560656..670e92ee12b 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -325,7 +325,7 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms)
if ((status & MMC_STATUS_RDY_FOR_DATA) &&
(status & MMC_STATUS_CURR_STATE) !=
MMC_STATE_PRG)
- break;
+ return 0;
if (status & MMC_STATUS_MASK) {
#if !defined(CONFIG_XPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
@@ -340,14 +340,10 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout_ms)
udelay(1000);
}
- if (timeout_ms <= 0) {
#if !defined(CONFIG_XPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- log_err("Timeout waiting card ready\n");
+ log_err("Timeout waiting card ready\n");
#endif
- return -ETIMEDOUT;
- }
-
- return 0;
+ return -ETIMEDOUT;
}
int mmc_set_blocklen(struct mmc *mmc, int len)
@@ -461,6 +457,14 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
struct mmc_cmd cmd;
struct mmc_data data;
+ if (blkcnt > 1 && (mmc->host_caps & MMC_CAP_CMD23)) {
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.cmdarg = blkcnt & 0x0000ffff;
+ cmd.resp_type = MMC_RSP_R1;
+ if (mmc_send_cmd(mmc, &cmd, NULL))
+ return 0;
+ }
+
if (blkcnt > 1)
cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
else
@@ -481,7 +485,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
if (mmc_send_cmd(mmc, &cmd, &data))
return 0;
- if (blkcnt > 1) {
+ if (blkcnt > 1 && !(mmc->host_caps & MMC_CAP_CMD23)) {
if (mmc_send_stop_transmission(mmc, false)) {
#if !defined(CONFIG_XPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
log_err("mmc fail to send stop cmd\n");
@@ -494,7 +498,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
}
#if !CONFIG_IS_ENABLED(DM_MMC)
-static int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt)
+int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt)
{
if (mmc->cfg->ops->get_b_max)
return mmc->cfg->ops->get_b_max(mmc, dst, blkcnt);
@@ -1037,6 +1041,10 @@ static int mmc_get_capabilities(struct mmc *mmc)
mmc->card_caps = MMC_MODE_1BIT | MMC_CAP(MMC_LEGACY);
+ /* CMD23(SET_BLOCK_COUNT) requires eMMC spec v3.1 or above */
+ if (mmc->version < MMC_VERSION_3)
+ mmc->host_caps &= ~MMC_CAP_CMD23;
+
if (mmc_host_is_spi(mmc))
return 0;
@@ -1436,6 +1444,8 @@ static int sd_get_capabilities(struct mmc *mmc)
if (mmc->scr[0] & SD_DATA_4BIT)
mmc->card_caps |= MMC_MODE_4BIT;
+ if (!(mmc->scr[0] & SD_SCR_CMD23_SUPPORT))
+ mmc->host_caps &= ~MMC_CAP_CMD23;
/* Version 1.0 doesn't support switching */
if (mmc->version == SD_VERSION_1_0)
@@ -3177,10 +3187,19 @@ int mmc_deinit(struct mmc *mmc)
return 0;
if (IS_SD(mmc)) {
- caps_filtered = mmc->card_caps &
- ~(MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) |
- MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_DDR50) |
- MMC_CAP(UHS_SDR104));
+ u32 uhs_mask = UHS_CAPS;
+
+#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
+ /*
+ * Per SD spec, once a card enters 1.8V signaling it
+ * cannot revert to 3.3V without a power cycle.
+ * If the card is operating at 1.8V, keep UHS_SDR12
+ * as the minimum fallback mode.
+ */
+ if (mmc_sd_card_using_v18(mmc))
+ uhs_mask &= ~MMC_CAP(UHS_SDR12);
+#endif
+ caps_filtered = mmc->card_caps & ~uhs_mask;
return sd_select_mode_and_width(mmc, caps_filtered);
} else {
diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h
index fc45f017e5d..24e68ceb5e6 100644
--- a/drivers/mmc/mmc_private.h
+++ b/drivers/mmc/mmc_private.h
@@ -17,6 +17,10 @@ int mmc_poll_for_busy(struct mmc *mmc, int timeout);
int mmc_set_blocklen(struct mmc *mmc, int len);
+#if !CONFIG_IS_ENABLED(DM_MMC)
+int mmc_get_b_max(struct mmc *mmc, void *dst, lbaint_t blkcnt);
+#endif
+
#if CONFIG_IS_ENABLED(BLK)
ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
void *dst);
diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c
index 928c05872ca..e739720b77c 100644
--- a/drivers/mmc/mmc_write.c
+++ b/drivers/mmc/mmc_write.c
@@ -165,7 +165,15 @@ static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
if (blkcnt == 0)
return 0;
- else if (blkcnt == 1)
+ if (blkcnt > 1 && mmc->host_caps & MMC_CAP_CMD23) {
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.cmdarg = blkcnt & 0x0000ffff;
+ cmd.resp_type = MMC_RSP_R1;
+ if (mmc_send_cmd(mmc, &cmd, NULL))
+ return 0;
+ }
+
+ if (blkcnt == 1)
cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
else
cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
@@ -191,10 +199,13 @@ static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
*/
}
- /* SPI multiblock writes terminate using a special
- * token, not a STOP_TRANSMISSION request.
+ /*
+ * SPI multiblock writes terminate using a special token, not CMD12.
+ * When CMD23 was issued the card auto-terminates, so CMD12 is also
+ * skipped in that case.
*/
- if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
+ if (!mmc_host_is_spi(mmc) && blkcnt > 1 &&
+ !(mmc->host_caps & MMC_CAP_CMD23)) {
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
@@ -228,6 +239,7 @@ ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
int dev_num = block_dev->devnum;
lbaint_t cur, blocks_todo = blkcnt;
int err;
+ uint b_max;
struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
@@ -240,9 +252,10 @@ ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
if (mmc_set_blocklen(mmc, mmc->write_bl_len))
return 0;
+ b_max = mmc_get_b_max(mmc, (void *)src, blkcnt);
+
do {
- cur = (blocks_todo > mmc->cfg->b_max) ?
- mmc->cfg->b_max : blocks_todo;
+ cur = (blocks_todo > b_max) ? b_max : blocks_todo;
if (mmc_write_blocks(mmc, start, cur, src) != cur)
return 0;
blocks_todo -= cur;
diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c
index 8bfdffd56f5..26d2262ef45 100644
--- a/drivers/mmc/rpmb.c
+++ b/drivers/mmc/rpmb.c
@@ -452,7 +452,7 @@ static int rpmb_route_frames(struct mmc *mmc, struct s_rpmb *req,
return rpmb_route_read_req(mmc, req, req_cnt, rsp, rsp_cnt);
case RPMB_REQ_READ_DATA:
- if (req_cnt != 1 || !req_cnt)
+ if (req_cnt != 1 || !rsp_cnt)
return -EINVAL;
return rpmb_route_read_req(mmc, req, req_cnt, rsp, rsp_cnt);
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 71a6d8ea2f5..962a99cd05a 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -733,7 +733,8 @@ static int sdhci_init(struct mmc *mmc)
*/
host->force_align_buffer = true;
#else
- if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) {
+ if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
+ !host->align_buffer) {
host->align_buffer = memalign(8, 512 * 1024);
if (!host->align_buffer) {
log_err("Aligned buffer alloc failed\n");
diff --git a/drivers/power/domain/renesas-r8a78000-power-domain.c b/drivers/power/domain/renesas-r8a78000-power-domain.c
index 57b3b56b2d9..62b56fd8ae0 100644
--- a/drivers/power/domain/renesas-r8a78000-power-domain.c
+++ b/drivers/power/domain/renesas-r8a78000-power-domain.c
@@ -166,6 +166,7 @@ struct rst_map_in {
#define GEN5_SCMI_SDK_4_30 0x010c0000
#define GEN5_SCMI_SDK_4_31 0x010d0000
#define GEN5_SCMI_SDK_4_32 0x010e0000
+#define GEN5_SCMI_SDK_4_36 0x01100000
static const struct rst_map_in gen5_rst_map_dt_sdk_4_28[] = {
{ SCP_RESET_DOMAIN_ID_UFS0, 202 },
@@ -220,7 +221,8 @@ static int gen5_reset_of_xlate(struct reset_ctl *reset_ctl,
map = gen5_rst_map_dt_sdk_4_28;
map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_28);
} else if (priv->basever == GEN5_SCMI_SDK_4_31 ||
- priv->basever == GEN5_SCMI_SDK_4_32) {
+ priv->basever == GEN5_SCMI_SDK_4_32 ||
+ priv->basever == GEN5_SCMI_SDK_4_36) {
map = gen5_rst_map_dt_sdk_4_31;
map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_31);
} else {
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index d45869cb30f..95687d0bdfd 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -171,6 +171,21 @@ config SPL_DM_PMIC_PCA9450
This config enables implementation of driver-model pmic uclass features
for PMIC PCA9450 in SPL. The driver implements read/write operations.
+config DM_PMIC_PF9453
+ bool "Enable Driver Model for PMIC PF9453"
+ depends on DM_I2C
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC PF9453. The driver implements read/write operations.
+
+config SPL_DM_PMIC_PF9453
+ bool "Enable Driver Model for PMIC PF9453 in SPL"
+ depends on SPL_DM_PMIC
+ depends on SPL_DM_I2C
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC PF9453 in SPL. The driver implements read/write operations.
+
config DM_PMIC_PFUZE100
bool "Enable Driver Model for PMIC PFUZE100"
help
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 0ac92084221..da9b810de90 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_$(PHASE_)DM_PMIC_BD71837) += bd71837.o
obj-$(CONFIG_$(PHASE_)DM_PMIC_MP5416) += mp5416.o
obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o
obj-$(CONFIG_$(PHASE_)DM_PMIC_PCA9450) += pca9450.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_PF9453) += pf9453.o
obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o
obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
obj-$(CONFIG_PMIC_AB8500) += ab8500.o
diff --git a/drivers/power/pmic/pf9453.c b/drivers/power/pmic/pf9453.c
new file mode 100644
index 00000000000..71d766bce5b
--- /dev/null
+++ b/drivers/power/pmic/pf9453.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 NXP
+ */
+
+#include <asm/global_data.h>
+#include <asm-generic/gpio.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <i2c.h>
+#include <linux/err.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/pf9453.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct pmic_child_info pmic_children_info[] = {
+ /* buck */
+ { .prefix = "b", .driver = PF9453_REGULATOR_DRIVER},
+ { .prefix = "B", .driver = PF9453_REGULATOR_DRIVER},
+ /* ldo */
+ { .prefix = "l", .driver = PF9453_REGULATOR_DRIVER},
+ { .prefix = "L", .driver = PF9453_REGULATOR_DRIVER},
+ { },
+};
+
+struct pf9453_priv {
+ struct gpio_desc *sd_vsel_gpio;
+};
+
+static int pf9453_reg_count(struct udevice *dev)
+{
+ return PF9453_REG_NUM;
+}
+
+static bool is_reg_protect(uint reg)
+{
+ switch (reg) {
+ case PF9453_BUCK1OUT:
+ case PF9453_BUCK2OUT:
+ case PF9453_BUCK3OUT:
+ case PF9453_BUCK4OUT:
+ case PF9453_LDO1OUT_L:
+ case PF9453_LDO1OUT_H:
+ case PF9453_LDO2OUT:
+ case PF9453_LDOSNVS_CFG1:
+ case PF9453_BUCK2OUT_MAX_LIMIT:
+ case PF9453_BUCK2OUT_MIN_LIMIT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int pf9453_write(struct udevice *dev, uint reg, const uint8_t *buff, int len)
+{
+ bool is_pro;
+ u32 val = PF9453_UNLOCK_KEY;
+
+ is_pro = is_reg_protect(reg);
+ if (is_pro) {
+ if (dm_i2c_write(dev, PF9453_REG_LOCK, (uint8_t *)&val, 1)) {
+ pr_err("write error to device: %p register: %#x!\n", dev, reg);
+ return -EIO;
+ }
+
+ if (dm_i2c_write(dev, reg, buff, len)) {
+ pr_err("write error to device: %p register: %#x!\n", dev, reg);
+ return -EIO;
+ }
+
+ val = PF9453_LOCK_KEY;
+ if (dm_i2c_write(dev, PF9453_REG_LOCK, (uint8_t *)&val, 1)) {
+ pr_err("write error to device: %p register: %#x!\n", dev, reg);
+ return -EIO;
+ }
+ } else {
+ if (dm_i2c_write(dev, reg, buff, len)) {
+ pr_err("write error to device: %p register: %#x!\n", dev, reg);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int pf9453_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ if (dm_i2c_read(dev, reg, buff, len)) {
+ pr_err("read error from device: %p register: %#x!\n", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int pf9453_bind(struct udevice *dev)
+{
+ int children;
+ ofnode regulators_node;
+
+ regulators_node = dev_read_subnode(dev, "regulators");
+ if (!ofnode_valid(regulators_node)) {
+ debug("%s: %s regulators subnode not found!", __func__,
+ dev->name);
+ return -ENXIO;
+ }
+
+ debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+ children = pmic_bind_children(dev, regulators_node,
+ pmic_children_info);
+ if (!children)
+ debug("%s: %s - no child found\n", __func__, dev->name);
+
+ /* Always return success for this device */
+ return 0;
+}
+
+static int pf9453_probe(struct udevice *dev)
+{
+ struct pf9453_priv *priv = dev_get_priv(dev);
+ unsigned int reset_ctrl;
+ int ret = 0;
+
+ if (CONFIG_IS_ENABLED(DM_GPIO) && CONFIG_IS_ENABLED(DM_REGULATOR_PF9453)) {
+ priv->sd_vsel_gpio = devm_gpiod_get_optional(dev, "sd-vsel",
+ GPIOD_IS_OUT |
+ GPIOD_IS_OUT_ACTIVE);
+ if (IS_ERR(priv->sd_vsel_gpio)) {
+ ret = PTR_ERR(priv->sd_vsel_gpio);
+ dev_err(dev, "Failed to request SD_VSEL GPIO: %d\n", ret);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (ofnode_read_bool(dev_ofnode(dev), "nxp,wdog_b-warm-reset"))
+ reset_ctrl = PF9453_PMIC_RESET_WDOG_B_CFG_WARM;
+ else
+ reset_ctrl = PF9453_PMIC_RESET_WDOG_B_CFG_COLD;
+
+ return pmic_clrsetbits(dev, PF9453_RESET_CTRL,
+ PF9453_PMIC_RESET_WDOG_B_CFG_MASK, reset_ctrl);
+}
+
+static struct dm_pmic_ops pf9453_ops = {
+ .reg_count = pf9453_reg_count,
+ .read = pf9453_read,
+ .write = pf9453_write,
+};
+
+static const struct udevice_id pf9453_ids[] = {
+ { .compatible = "nxp,pf9453", .data = NXP_CHIP_TYPE_PF9453, },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_pf9453) = {
+ .name = "pf9453 pmic",
+ .id = UCLASS_PMIC,
+ .of_match = pf9453_ids,
+ .bind = pf9453_bind,
+ .probe = pf9453_probe,
+ .ops = &pf9453_ops,
+ .priv_auto = sizeof(struct pf9453_priv),
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index c3dc5d6f97b..00a25acfdf9 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -103,6 +103,21 @@ config SPL_DM_REGULATOR_PCA9450
This config enables implementation of driver-model regulator uclass
features for regulators on ROHM PCA9450 in SPL.
+config DM_REGULATOR_PF9453
+ bool "Enable Driver Model for NXP PF9453 regulators"
+ depends on DM_REGULATOR && DM_PMIC_PF9453
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on NXP PF9453 PMICs. PF9453 contains 6 bucks
+ and 5 LDOS. The driver implements get/set api for value and enable.
+
+config SPL_DM_REGULATOR_PF9453
+ bool "Enable Driver Model for NXP PF9453 regulators in SPL"
+ depends on DM_REGULATOR_PF9453 && SPL
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on ROHM PF9453 in SPL.
+
config DM_REGULATOR_DA9063
bool "Enable Driver Model for REGULATOR DA9063"
depends on DM_REGULATOR && DM_PMIC_DA9063
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 3a148545387..a40c9d340d9 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PFUZE100) += pfuze100.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_BD71837) += bd71837.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PCA9450) += pca9450.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PF9453) += pf9453.o
obj-$(CONFIG_$(PHASE_)REGULATOR_PWM) += pwm_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_COMMON) += regulator_common.o
diff --git a/drivers/power/regulator/pf9453.c b/drivers/power/regulator/pf9453.c
new file mode 100644
index 00000000000..6ecde09bafa
--- /dev/null
+++ b/drivers/power/regulator/pf9453.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NXP PF9453 regulator driver
+ * Copyright 2024 NXP
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <linux/bitops.h>
+#include <power/pf9453.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+/**
+ * struct pf9453_vrange - describe linear range of voltages
+ *
+ * @min_volt: smallest voltage in range
+ * @step: how much voltage changes at each selector step
+ * @min_sel: smallest selector in the range
+ * @max_sel: maximum selector in the range
+ */
+struct pf9453_vrange {
+ unsigned int min_volt;
+ unsigned int step;
+ u8 min_sel;
+ u8 max_sel;
+};
+
+/**
+ * struct pf9453_plat - describe regulator control registers
+ *
+ * @name: name of the regulator. Used for matching the dt-entry
+ * @enable_reg: register address used to enable/disable regulator
+ * @enablemask: register mask used to enable/disable regulator
+ * @volt_reg: register address used to configure regulator voltage
+ * @volt_mask: register mask used to configure regulator voltage
+ * @ranges: pointer to ranges of regulator voltages and matching register
+ * values
+ * @numranges: number of voltage ranges pointed by ranges
+ */
+struct pf9453_plat {
+ const char *name;
+ u8 enable_reg;
+ u8 enablemask;
+ u8 volt_reg;
+ u8 volt_mask;
+ struct pf9453_vrange *ranges;
+ unsigned int numranges;
+};
+
+#define PCA_RANGE(_min, _vstep, _sel_low, _sel_hi) \
+{ \
+ .min_volt = (_min), .step = (_vstep), \
+ .min_sel = (_sel_low), .max_sel = (_sel_hi), \
+}
+
+#define PCA_DATA(_name, enreg, enmask, vreg, vmask, _range, _numranges) \
+{ \
+ .name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \
+ .volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \
+ .numranges = _numranges \
+}
+
+static struct pf9453_vrange pf9453_buck134_vranges[] = {
+ PCA_RANGE(600000, 25000, 0, 0x7f),
+};
+
+static struct pf9453_vrange pf9453_buck2_vranges[] = {
+ PCA_RANGE(600000, 12500, 0, 0x7f),
+};
+
+static struct pf9453_vrange pf9453_ldo1_vranges[] = {
+ PCA_RANGE(800000, 25000, 0x0, 0x64),
+};
+
+static struct pf9453_vrange pf9453_ldo2_vranges[] = {
+ PCA_RANGE(500000, 25000, 0x0, 0x3a),
+};
+
+static struct pf9453_vrange pf9453_ldosnvs_vranges[] = {
+ PCA_RANGE(800000, 25000, 0x0, 0x58),
+};
+
+static struct pf9453_plat pf9453_reg_data[] = {
+ PCA_DATA("BUCK1", PF9453_BUCK1CTRL, PF9453_EN_MODE_MASK,
+ PF9453_BUCK1OUT, PF9453_BUCK_RUN_MASK,
+ pf9453_buck134_vranges, ARRAY_SIZE(pf9453_buck134_vranges)),
+ PCA_DATA("BUCK2", PF9453_BUCK2CTRL, PF9453_EN_MODE_MASK,
+ PF9453_BUCK2OUT, PF9453_BUCK_RUN_MASK,
+ pf9453_buck2_vranges, ARRAY_SIZE(pf9453_buck2_vranges)),
+ PCA_DATA("BUCK3", PF9453_BUCK3CTRL, PF9453_EN_MODE_MASK,
+ PF9453_BUCK3OUT, PF9453_BUCK_RUN_MASK,
+ pf9453_buck134_vranges, ARRAY_SIZE(pf9453_buck134_vranges)),
+ PCA_DATA("BUCK4", PF9453_BUCK4CTRL, PF9453_EN_MODE_MASK,
+ PF9453_BUCK4OUT, PF9453_BUCK_RUN_MASK,
+ pf9453_buck134_vranges, ARRAY_SIZE(pf9453_buck134_vranges)),
+ /* LDOs */
+ PCA_DATA("LDO1", PF9453_LDO1CFG, PF9453_EN_MODE_MASK,
+ PF9453_LDO1OUT_H, PF9453_LDO1_MASK,
+ pf9453_ldo1_vranges, ARRAY_SIZE(pf9453_ldo1_vranges)),
+ PCA_DATA("LDO2", PF9453_LDO2CFG, PF9453_EN_MODE_MASK,
+ PF9453_LDO2OUT, PF9453_LDO2_MASK,
+ pf9453_ldo2_vranges, ARRAY_SIZE(pf9453_ldo2_vranges)),
+ PCA_DATA("LDO-SNVS", PF9453_LDOSNVS_CFG2, PF9453_EN_MODE_MASK,
+ PF9453_LDOSNVS_CFG1, PF9453_LDOSNVS_MASK,
+ pf9453_ldosnvs_vranges, ARRAY_SIZE(pf9453_ldosnvs_vranges)),
+};
+
+static int vrange_find_value(struct pf9453_vrange *r, unsigned int sel,
+ unsigned int *val)
+{
+ if (!val || sel < r->min_sel || sel > r->max_sel)
+ return -EINVAL;
+
+ *val = r->min_volt + r->step * (sel - r->min_sel);
+ return 0;
+}
+
+static int vrange_find_selector(struct pf9453_vrange *r, int val,
+ unsigned int *sel)
+{
+ int ret = -EINVAL;
+ int num_vals = r->max_sel - r->min_sel + 1;
+
+ if (val >= r->min_volt &&
+ val <= r->min_volt + r->step * (num_vals - 1)) {
+ if (r->step) {
+ *sel = r->min_sel + ((val - r->min_volt) / r->step);
+ ret = 0;
+ } else {
+ *sel = r->min_sel;
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int pf9453_get_enable(struct udevice *dev)
+{
+ struct pf9453_plat *plat = dev_get_plat(dev);
+ int val;
+
+ val = pmic_reg_read(dev->parent, plat->enable_reg);
+ if (val < 0)
+ return val;
+
+ return (val & plat->enablemask);
+}
+
+static int pf9453_set_enable(struct udevice *dev, bool enable)
+{
+ int val = 0;
+ struct pf9453_plat *plat = dev_get_plat(dev);
+
+ if (enable)
+ val = plat->enablemask;
+
+ return pmic_clrsetbits(dev->parent, plat->enable_reg, plat->enablemask,
+ val);
+}
+
+static int pf9453_get_value(struct udevice *dev)
+{
+ struct pf9453_plat *plat = dev_get_plat(dev);
+ unsigned int reg, tmp;
+ int i, ret;
+
+ ret = pmic_reg_read(dev->parent, plat->volt_reg);
+ if (ret < 0)
+ return ret;
+
+ reg = ret;
+ reg &= plat->volt_mask;
+
+ for (i = 0; i < plat->numranges; i++) {
+ struct pf9453_vrange *r = &plat->ranges[i];
+
+ if (!vrange_find_value(r, reg, &tmp))
+ return tmp;
+ }
+
+ pr_err("Unknown voltage value read from pmic\n");
+
+ return -EINVAL;
+}
+
+static int pf9453_set_value(struct udevice *dev, int uvolt)
+{
+ struct pf9453_plat *plat = dev_get_plat(dev);
+ unsigned int sel;
+ int i, found = 0;
+
+ for (i = 0; i < plat->numranges; i++) {
+ struct pf9453_vrange *r = &plat->ranges[i];
+
+ found = !vrange_find_selector(r, uvolt, &sel);
+ if (found) {
+ unsigned int tmp;
+
+ /*
+ * We require exactly the requested value to be
+ * supported - this can be changed later if needed
+ */
+ found = !vrange_find_value(r, sel, &tmp);
+ if (found && tmp == uvolt)
+ break;
+ found = 0;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ return pmic_clrsetbits(dev->parent, plat->volt_reg,
+ plat->volt_mask, sel);
+}
+
+static int pf9453_regulator_probe(struct udevice *dev)
+{
+ struct pf9453_plat *plat = dev_get_plat(dev);
+ int i, type;
+
+ type = dev_get_driver_data(dev_get_parent(dev));
+
+ if (type != NXP_CHIP_TYPE_PF9453) {
+ debug("Unknown PMIC type\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pf9453_reg_data); i++) {
+ if (strcmp(dev->name, pf9453_reg_data[i].name))
+ continue;
+ *plat = pf9453_reg_data[i];
+ return 0;
+ }
+
+ pr_err("Unknown regulator '%s'\n", dev->name);
+
+ return -ENOENT;
+}
+
+static const struct dm_regulator_ops pf9453_regulator_ops = {
+ .get_value = pf9453_get_value,
+ .set_value = pf9453_set_value,
+ .get_enable = pf9453_get_enable,
+ .set_enable = pf9453_set_enable,
+};
+
+U_BOOT_DRIVER(pf9453_regulator) = {
+ .name = PF9453_REGULATOR_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &pf9453_regulator_ops,
+ .probe = pf9453_regulator_probe,
+ .plat_auto = sizeof(struct pf9453_plat),
+};
diff --git a/include/mmc.h b/include/mmc.h
index 9509c9e9543..4bda44ea220 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -67,6 +67,7 @@ struct bd_info;
#define MMC_CAP_NONREMOVABLE BIT(14)
#define MMC_CAP_NEEDS_POLL BIT(15)
#define MMC_CAP_CD_ACTIVE_HIGH BIT(16)
+#define MMC_CAP_CMD23 BIT(17)
#define MMC_MODE_8BIT BIT(30)
#define MMC_MODE_4BIT BIT(29)
@@ -141,6 +142,8 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
#define SD_HIGHSPEED_BUSY 0x00020000
#define SD_HIGHSPEED_SUPPORTED 0x00020000
+#define SD_SCR_CMD23_SUPPORT BIT(1)
+
#define UHS_SDR12_BUS_SPEED 0
#define HIGH_SPEED_BUS_SPEED 1
#define UHS_SDR25_BUS_SPEED 1
diff --git a/include/mvebu_mmc.h b/include/mvebu_mmc.h
index 0f6f5c23dee..258623d5c67 100644
--- a/include/mvebu_mmc.h
+++ b/include/mvebu_mmc.h
@@ -253,8 +253,6 @@
#define MMC_CAP_DRIVER_TYPE_C (1 << 24)
/* Host supports Driver Type D */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25)
-/* CMD23 supported. */
-#define MMC_CAP_CMD23 (1 << 30)
/* Hardware reset */
#define MMC_CAP_HW_RESET (1 << 31)
diff --git a/include/power/pf9453.h b/include/power/pf9453.h
new file mode 100644
index 00000000000..f33b839cde0
--- /dev/null
+++ b/include/power/pf9453.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2024 NXP
+ */
+
+#ifndef PF9453_H_
+#define PF9453_H_
+
+#define PF9453_REGULATOR_DRIVER "pf9453_regulator"
+
+enum {
+ PF9453_REG_DEV_ID = 0x00,
+ PF9453_OTP_VER = 0x01,
+ PF9453_INT1 = 0x02,
+ PF9453_INT1_MSK = 0x03,
+ PF9453_INT1_STATUS = 0x04,
+ PF9453_VRFLT1_INT = 0x05,
+ PF9453_VRFLT1_MASK = 0x06,
+ PF9453_PWRON_STAT = 0x07,
+ PF9453_RESET_CTRL = 0x08,
+ PF9453_SW_RST = 0x09,
+ PF9453_PWR_CTRL = 0x0a,
+ PF9453_CONFIG1 = 0x0b,
+ PF9453_CONFIG2 = 0x0c,
+ PF9453_32K_CONFIG = 0x0d,
+ PF9453_BUCK1CTRL = 0x10,
+ PF9453_BUCK1OUT = 0x11,
+ PF9453_BUCK2CTRL = 0x14,
+ PF9453_BUCK2OUT = 0x15,
+ PF9453_BUCK2OUT_STBY = 0x1D,
+ PF9453_BUCK2OUT_MAX_LIMIT = 0x1F,
+ PF9453_BUCK2OUT_MIN_LIMIT = 0x20,
+ PF9453_BUCK3CTRL = 0x21,
+ PF9453_BUCK3OUT = 0x22,
+ PF9453_BUCK4CTRL = 0x2e,
+ PF9453_BUCK4OUT = 0x2f,
+ PF9453_LDO1OUT_L = 0x36,
+ PF9453_LDO1CFG = 0x37,
+ PF9453_LDO1OUT_H = 0x38,
+ PF9453_LDOSNVS_CFG1 = 0x39,
+ PF9453_LDOSNVS_CFG2 = 0x3a,
+ PF9453_LDO2CFG = 0x3b,
+ PF9453_LDO2OUT = 0x3c,
+ PF9453_BUCK_POK = 0x3d,
+ PF9453_LSW_CTRL1 = 0x40,
+ PF9453_LSW_CTRL2 = 0x41,
+ PF9453_REG_LOCK = 0x4e,
+ PF9453_REG_NUM,
+};
+
+int power_pf9453_init(unsigned char bus, unsigned char addr);
+
+enum {
+ NXP_CHIP_TYPE_PF9453 = 0,
+ NXP_CHIP_TYPE_AMOUNT
+};
+
+#define PF9453_UNLOCK_KEY 0x5c
+#define PF9453_LOCK_KEY 0x0
+
+#define PF9453_EN_MODE_MASK 0x3
+#define PF9453_BUCK_RUN_MASK 0x7f
+#define PF9453_LDO1_MASK 0x7f
+#define PF9453_LDO2_MASK 0x3f
+#define PF9453_LDOSNVS_MASK 0x7f
+
+#define PF9453_PMIC_RESET_WDOG_B_CFG_MASK 0xc0
+#define PF9453_PMIC_RESET_WDOG_B_CFG_WARM 0x40
+#define PF9453_PMIC_RESET_WDOG_B_CFG_COLD 0x80
+
+#endif