diff options
| author | Tom Rini <[email protected]> | 2026-07-30 08:50:13 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-07-30 08:50:13 -0600 |
| commit | 100e12ea78c73071b9710f08b32fd4590019266f (patch) | |
| tree | 2cb18246d53827c2391fb40a4abb21214dfd0e87 | |
| parent | 94b349bd902d9e38e1846c157fadd9054c34680a (diff) | |
| parent | 602726cb300992cd98ceea72e9ea08aa37a92a0c (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.c | 45 | ||||
| -rw-r--r-- | drivers/mmc/mmc_private.h | 4 | ||||
| -rw-r--r-- | drivers/mmc/mmc_write.c | 25 | ||||
| -rw-r--r-- | drivers/mmc/rpmb.c | 2 | ||||
| -rw-r--r-- | drivers/mmc/sdhci.c | 3 | ||||
| -rw-r--r-- | drivers/power/domain/renesas-r8a78000-power-domain.c | 4 | ||||
| -rw-r--r-- | drivers/power/pmic/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/power/pmic/Makefile | 1 | ||||
| -rw-r--r-- | drivers/power/pmic/pf9453.c | 170 | ||||
| -rw-r--r-- | drivers/power/regulator/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/power/regulator/Makefile | 1 | ||||
| -rw-r--r-- | drivers/power/regulator/pf9453.c | 256 | ||||
| -rw-r--r-- | include/mmc.h | 3 | ||||
| -rw-r--r-- | include/mvebu_mmc.h | 2 | ||||
| -rw-r--r-- | include/power/pf9453.h | 71 |
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 |
