diff options
author | Tom Rini <trini@konsulko.com> | 2022-05-23 09:25:39 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-05-23 09:25:39 -0400 |
commit | 004d30c786056d443d40428c4b1c11e2f8f0bc32 (patch) | |
tree | a6c7d28590d20c5f88f292804bcb22ebfa36d942 /drivers | |
parent | 6f00b97d7e5760d92566317dde6c4b9224790827 (diff) | |
parent | 4d573d5c98234cad328de77c773c3c3d79258255 (diff) | |
download | u-boot-004d30c786056d443d40428c4b1c11e2f8f0bc32.zip u-boot-004d30c786056d443d40428c4b1c11e2f8f0bc32.tar.gz u-boot-004d30c786056d443d40428c4b1c11e2f8f0bc32.tar.bz2 |
Merge tag 'u-boot-imx-20220523' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
u-boot-imx-20220523
-------------------
CI: https://source.denx.de/u-boot/custodians/u-boot-imx/-/pipelines/12087
Additionally to u-boot-imx20200520:
- DH MX8MP
- i.MX GPIO: reading GPIO when direction is output
- Menlo i.MX53: switch to DM
And from u-boot-imx20200520:
- fix Verdin hang
- add pca9450 regulator
- conversion to DM_SERIAL
- NAND block handling
- fix crypto
- enable cache on some boards
- add ACC board (MX6)
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/crypto/fsl/fsl_hash.c | 8 | ||||
-rw-r--r-- | drivers/crypto/fsl/jr.c | 19 | ||||
-rw-r--r-- | drivers/ddr/imx/imx8m/ddrphy_utils.c | 9 | ||||
-rw-r--r-- | drivers/gpio/imx_rgpio2p.c | 14 | ||||
-rw-r--r-- | drivers/misc/imx8ulp/s400_api.c | 41 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxs_nand.c | 35 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxs_nand_spl.c | 90 | ||||
-rw-r--r-- | drivers/power/pmic/pca9450.c | 8 | ||||
-rw-r--r-- | drivers/power/regulator/Kconfig | 15 | ||||
-rw-r--r-- | drivers/power/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/regulator/pca9450.c | 333 |
11 files changed, 508 insertions, 65 deletions
diff --git a/drivers/crypto/fsl/fsl_hash.c b/drivers/crypto/fsl/fsl_hash.c index a52c4ac..9e6829b 100644 --- a/drivers/crypto/fsl/fsl_hash.c +++ b/drivers/crypto/fsl/fsl_hash.c @@ -149,12 +149,20 @@ static int caam_hash_finish(void *hash_ctx, void *dest_buf, driver_hash[caam_algo].digestsize, 1); + flush_dcache_range((ulong)ctx->sg_tbl, (ulong)(ctx->sg_tbl) + len); + flush_dcache_range((ulong)ctx->sha_desc, + (ulong)(ctx->sha_desc) + (sizeof(uint32_t) * MAX_CAAM_DESCSIZE)); + flush_dcache_range((ulong)ctx->hash, + (ulong)(ctx->hash) + driver_hash[caam_algo].digestsize); + ret = run_descriptor_jr(ctx->sha_desc); if (ret) { debug("Error %x\n", ret); return ret; } else { + invalidate_dcache_range((ulong)ctx->hash, + (ulong)(ctx->hash) + driver_hash[caam_algo].digestsize); memcpy(dest_buf, ctx->hash, sizeof(ctx->hash)); } free(ctx); diff --git a/drivers/crypto/fsl/jr.c b/drivers/crypto/fsl/jr.c index 1d951cf..acd2992 100644 --- a/drivers/crypto/fsl/jr.c +++ b/drivers/crypto/fsl/jr.c @@ -623,7 +623,7 @@ static void kick_trng(int ent_delay, ccsr_sec_t *sec) static int rng_init(uint8_t sec_idx, ccsr_sec_t *sec) { - int ret, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN; + int ret, gen_sk, ent_delay = RTSDCTL_ENT_DLY; struct rng4tst __iomem *rng = (struct rng4tst __iomem *)&sec->rng; u32 inst_handles; @@ -652,6 +652,15 @@ static int rng_init(uint8_t sec_idx, ccsr_sec_t *sec) * the RNG. */ ret = instantiate_rng(sec_idx, sec, gen_sk); + /* + * entropy delay is calculated via self-test method. + * self-test are run across different volatge, temp. + * if worst case value for ent_dly is identified, + * loop can be skipped for that platform. + */ + if (IS_ENABLED(CONFIG_MX6SX)) + break; + } while ((ret == -1) && (ent_delay < RTSDCTL_ENT_DLY_MAX)); if (ret) { printf("SEC%u: Failed to instantiate RNG\n", sec_idx); @@ -758,8 +767,14 @@ init: return -1; } #if CONFIG_IS_ENABLED(OF_CONTROL) - if (ofnode_valid(scu_node)) + if (ofnode_valid(scu_node)) { + if (IS_ENABLED(CONFIG_DM_RNG)) { + ret = device_bind_driver(NULL, "caam-rng", "caam-rng", NULL); + if (ret) + printf("Couldn't bind rng driver (%d)\n", ret); + } return ret; + } #endif #ifdef CONFIG_FSL_CORENET diff --git a/drivers/ddr/imx/imx8m/ddrphy_utils.c b/drivers/ddr/imx/imx8m/ddrphy_utils.c index a54449e..975d553 100644 --- a/drivers/ddr/imx/imx8m/ddrphy_utils.c +++ b/drivers/ddr/imx/imx8m/ddrphy_utils.c @@ -198,9 +198,14 @@ unsigned int lpddr4_mr_read(unsigned int mr_rank, unsigned int mr_addr) tmp = reg32_read(DRC_PERF_MON_MRR0_DAT(0)); } while ((tmp & 0x8) == 0); tmp = reg32_read(DRC_PERF_MON_MRR1_DAT(0)); - tmp = tmp & 0xff; reg32_write(DRC_PERF_MON_MRR0_DAT(0), 0x4); - + while (tmp) { //try to find a significant byte in the word + if (tmp & 0xff) { + tmp &= 0xff; + break; + } + tmp >>= 8; + } return tmp; } diff --git a/drivers/gpio/imx_rgpio2p.c b/drivers/gpio/imx_rgpio2p.c index 0e2874c..175e460 100644 --- a/drivers/gpio/imx_rgpio2p.c +++ b/drivers/gpio/imx_rgpio2p.c @@ -39,6 +39,14 @@ static int imx_rgpio2p_is_output(struct gpio_regs *regs, int offset) return val & (1 << offset) ? 1 : 0; } +static int imx_rgpio2p_bank_get_direction(struct gpio_regs *regs, int offset) +{ + if ((readl(®s->gpio_pddr) >> offset) & 0x01) + return IMX_RGPIO2P_DIRECTION_OUT; + + return IMX_RGPIO2P_DIRECTION_IN; +} + static void imx_rgpio2p_bank_direction(struct gpio_regs *regs, int offset, enum imx_rgpio2p_direction direction) { @@ -67,7 +75,11 @@ static void imx_rgpio2p_bank_set_value(struct gpio_regs *regs, int offset, static int imx_rgpio2p_bank_get_value(struct gpio_regs *regs, int offset) { - return (readl(®s->gpio_pdir) >> offset) & 0x01; + if (imx_rgpio2p_bank_get_direction(regs, offset) == + IMX_RGPIO2P_DIRECTION_IN) + return (readl(®s->gpio_pdir) >> offset) & 0x01; + + return (readl(®s->gpio_pdor) >> offset) & 0x01; } static int imx_rgpio2p_direction_input(struct udevice *dev, unsigned offset) diff --git a/drivers/misc/imx8ulp/s400_api.c b/drivers/misc/imx8ulp/s400_api.c index 3ffdeb2..87f5880 100644 --- a/drivers/misc/imx8ulp/s400_api.c +++ b/drivers/misc/imx8ulp/s400_api.c @@ -272,6 +272,47 @@ int ahab_release_caam(u32 core_did, u32 *response) return ret; } +int ahab_get_fw_version(u32 *fw_version, u32 *sha1, u32 *response) +{ + struct udevice *dev = gd->arch.s400_dev; + int size = sizeof(struct imx8ulp_s400_msg); + struct imx8ulp_s400_msg msg; + int ret; + + if (!dev) { + printf("s400 dev is not initialized\n"); + return -ENODEV; + } + + if (!fw_version) { + printf("Invalid parameters for f/w version read\n"); + return -EINVAL; + } + + if (!sha1) { + printf("Invalid parameters for commit sha1\n"); + return -EINVAL; + } + + msg.version = AHAB_VERSION; + msg.tag = AHAB_CMD_TAG; + msg.size = 1; + msg.command = AHAB_GET_FW_VERSION_CID; + + ret = misc_call(dev, false, &msg, size, &msg, size); + if (ret) + printf("Error: %s: ret %d, response 0x%x\n", + __func__, ret, msg.data[0]); + + if (response) + *response = msg.data[0]; + + *fw_version = msg.data[1]; + *sha1 = msg.data[2]; + + return ret; +} + int ahab_dump_buffer(u32 *buffer, u32 buffer_length) { struct udevice *dev = gd->arch.s400_dev; diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index ee5d7fd..7893e9d 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -1246,22 +1246,6 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) /* Enable BCH complete interrupt */ writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); - /* Hook some operations at the MTD level. */ - if (mtd->_read_oob != mxs_nand_hook_read_oob) { - nand_info->hooked_read_oob = mtd->_read_oob; - mtd->_read_oob = mxs_nand_hook_read_oob; - } - - if (mtd->_write_oob != mxs_nand_hook_write_oob) { - nand_info->hooked_write_oob = mtd->_write_oob; - mtd->_write_oob = mxs_nand_hook_write_oob; - } - - if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { - nand_info->hooked_block_markbad = mtd->_block_markbad; - mtd->_block_markbad = mxs_nand_hook_block_markbad; - } - return 0; } @@ -1380,6 +1364,9 @@ int mxs_nand_init_spl(struct nand_chip *nand) else nand_info->max_ecc_strength_supported = 40; + if (IS_ENABLED(CONFIG_NAND_MXS_USE_MINIMUM_ECC)) + nand_info->use_minimum_ecc = true; + err = mxs_nand_alloc_buffers(nand_info); if (err) return err; @@ -1467,6 +1454,22 @@ int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) if (err) goto err_free_buffers; + /* Hook some operations at the MTD level. */ + if (mtd->_read_oob != mxs_nand_hook_read_oob) { + nand_info->hooked_read_oob = mtd->_read_oob; + mtd->_read_oob = mxs_nand_hook_read_oob; + } + + if (mtd->_write_oob != mxs_nand_hook_write_oob) { + nand_info->hooked_write_oob = mtd->_write_oob; + mtd->_write_oob = mxs_nand_hook_write_oob; + } + + if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { + nand_info->hooked_block_markbad = mtd->_block_markbad; + mtd->_block_markbad = mxs_nand_hook_block_markbad; + } + err = nand_register(0, mtd); if (err) goto err_free_buffers; diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c index 59a67ee..2bfb181 100644 --- a/drivers/mtd/nand/raw/mxs_nand_spl.c +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c @@ -218,14 +218,14 @@ void nand_init(void) mxs_nand_setup_ecc(mtd); } -int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf) +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) { - struct nand_chip *chip; - unsigned int page; + unsigned int sz; + unsigned int block, lastblock; + unsigned int page, page_offset; unsigned int nand_page_per_block; - unsigned int sz = 0; + struct nand_chip *chip; u8 *page_buf = NULL; - u32 page_off; chip = mtd_to_nand(mtd); if (!chip->numchips) @@ -235,47 +235,42 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf) if (!page_buf) return -ENOMEM; - page = offs >> chip->page_shift; - page_off = offs & (mtd->writesize - 1); + /* offs has to be aligned to a page address! */ + block = offs / mtd->erasesize; + lastblock = (offs + size - 1) / mtd->erasesize; + page = (offs % mtd->erasesize) / mtd->writesize; + page_offset = offs % mtd->writesize; nand_page_per_block = mtd->erasesize / mtd->writesize; - debug("%s offset:0x%08x len:%d page:%x\n", __func__, offs, size, page); - - while (size) { - if (mxs_read_page_ecc(mtd, page_buf, page) < 0) - return -1; - - if (size > (mtd->writesize - page_off)) - sz = (mtd->writesize - page_off); - else - sz = size; - - memcpy(buf, page_buf + page_off, sz); - - offs += mtd->writesize; - page++; - buf += (mtd->writesize - page_off); - page_off = 0; - size -= sz; - - /* - * Check if we have crossed a block boundary, and if so - * check for bad block. - */ - if (!(page % nand_page_per_block)) { - /* - * Yes, new block. See if this block is good. If not, - * loop until we find a good block. - */ - while (is_badblock(mtd, offs, 1)) { - page = page + nand_page_per_block; - /* Check i we've reached the end of flash. */ - if (page >= mtd->size >> chip->page_shift) { + while (block <= lastblock && size > 0) { + if (!is_badblock(mtd, mtd->erasesize * block, 1)) { + /* Skip bad blocks */ + while (page < nand_page_per_block) { + int curr_page = nand_page_per_block * block + page; + + if (mxs_read_page_ecc(mtd, page_buf, curr_page) < 0) { free(page_buf); - return -ENOMEM; + return -EIO; } + + if (size > (mtd->writesize - page_offset)) + sz = (mtd->writesize - page_offset); + else + sz = size; + + memcpy(dst, page_buf + page_offset, sz); + dst += sz; + size -= sz; + page_offset = 0; + page++; } + + page = 0; + } else { + lastblock++; } + + block++; } free(page_buf); @@ -294,6 +289,19 @@ void nand_deselect(void) u32 nand_spl_adjust_offset(u32 sector, u32 offs) { - /* Handle the offset adjust in nand_spl_load_image,*/ + unsigned int block, lastblock; + + block = sector / mtd->erasesize; + lastblock = (sector + offs) / mtd->erasesize; + + while (block <= lastblock) { + if (is_badblock(mtd, block * mtd->erasesize, 1)) { + offs += mtd->erasesize; + lastblock++; + } + + block++; + } + return offs; } diff --git a/drivers/power/pmic/pca9450.c b/drivers/power/pmic/pca9450.c index 2394b19..116ac49 100644 --- a/drivers/power/pmic/pca9450.c +++ b/drivers/power/pmic/pca9450.c @@ -19,8 +19,10 @@ DECLARE_GLOBAL_DATA_PTR; static const struct pmic_child_info pmic_children_info[] = { /* buck */ { .prefix = "b", .driver = PCA9450_REGULATOR_DRIVER}, + { .prefix = "B", .driver = PCA9450_REGULATOR_DRIVER}, /* ldo */ { .prefix = "l", .driver = PCA9450_REGULATOR_DRIVER}, + { .prefix = "L", .driver = PCA9450_REGULATOR_DRIVER}, { }, }; @@ -81,9 +83,9 @@ static struct dm_pmic_ops pca9450_ops = { }; static const struct udevice_id pca9450_ids[] = { - { .compatible = "nxp,pca9450a", .data = 0x25, }, - { .compatible = "nxp,pca9450b", .data = 0x25, }, - { .compatible = "nxp,pca9450c", .data = 0x25, }, + { .compatible = "nxp,pca9450a", .data = NXP_CHIP_TYPE_PCA9450A, }, + { .compatible = "nxp,pca9450b", .data = NXP_CHIP_TYPE_PCA9450BC, }, + { .compatible = "nxp,pca9450c", .data = NXP_CHIP_TYPE_PCA9450BC, }, { } }; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 9145408..d6cea8e 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -60,6 +60,21 @@ config SPL_DM_REGULATOR_BD71837 This config enables implementation of driver-model regulator uclass features for regulators on ROHM BD71837 and BD71847 in SPL. +config DM_REGULATOR_PCA9450 + bool "Enable Driver Model for NXP PCA9450 regulators" + depends on DM_REGULATOR && DM_PMIC_PCA9450 + help + This config enables implementation of driver-model regulator uclass + features for regulators on NXP PCA9450 PMICs. PCA9450 contains 6 bucks + and 5 LDOS. The driver implements get/set api for value and enable. + +config SPL_DM_REGULATOR_PCA9450 + bool "Enable Driver Model for NXP PCA9450 regulators in SPL" + depends on DM_REGULATOR_PCA9450 + help + This config enables implementation of driver-model regulator uclass + features for regulators on ROHM PCA9450 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 b9883df..bc73606 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_BD71837) += bd71837.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_PCA9450) += pca9450.o obj-$(CONFIG_$(SPL_)REGULATOR_PWM) += pwm_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_FAN53555) += fan53555.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_COMMON) += regulator_common.o diff --git a/drivers/power/regulator/pca9450.c b/drivers/power/regulator/pca9450.c new file mode 100644 index 0000000..23badaa --- /dev/null +++ b/drivers/power/regulator/pca9450.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP PCA9450 regulator driver + * Copyright (C) 2022 Marek Vasut <marex@denx.de> + * + * Largely based on: + * ROHM BD71837 regulator driver + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <linux/bitops.h> +#include <power/pca9450.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define HW_STATE_CONTROL 0 +#define DEBUG + +/** + * struct pca9450_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 pca9450_vrange { + unsigned int min_volt; + unsigned int step; + u8 min_sel; + u8 max_sel; +}; + +/** + * struct pca9450_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 + * @dvs: whether the voltage can be changed when regulator is enabled + */ +struct pca9450_plat { + const char *name; + u8 enable_reg; + u8 enablemask; + u8 volt_reg; + u8 volt_mask; + struct pca9450_vrange *ranges; + unsigned int numranges; + bool dvs; +}; + +#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, _dvs) \ +{ \ + .name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \ + .volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \ + .numranges = ARRAY_SIZE(_range), .dvs = (_dvs), \ +} + +static struct pca9450_vrange pca9450_buck123_vranges[] = { + PCA_RANGE(600000, 12500, 0, 0x7f), +}; + +static struct pca9450_vrange pca9450_buck456_vranges[] = { + PCA_RANGE(600000, 25000, 0, 0x70), + PCA_RANGE(3400000, 0, 0x71, 0x7f), +}; + +static struct pca9450_vrange pca9450_ldo1_vranges[] = { + PCA_RANGE(1600000, 100000, 0x0, 0x3), + PCA_RANGE(3000000, 100000, 0x4, 0x7), +}; + +static struct pca9450_vrange pca9450_ldo2_vranges[] = { + PCA_RANGE(800000, 50000, 0x0, 0x7), +}; + +static struct pca9450_vrange pca9450_ldo34_vranges[] = { + PCA_RANGE(800000, 100000, 0x0, 0x19), + PCA_RANGE(3300000, 0, 0x1a, 0x1f), +}; + +static struct pca9450_vrange pca9450_ldo5_vranges[] = { + PCA_RANGE(1800000, 100000, 0x0, 0xf), +}; + +/* + * We use enable mask 'HW_STATE_CONTROL' to indicate that this regulator + * must not be enabled or disabled by SW. The typical use-case for PCA9450 + * is powering NXP i.MX8. In this use-case we (for now) only allow control + * for BUCK4, BUCK5, BUCK6 which are not boot critical. + */ +static struct pca9450_plat pca9450_reg_data[] = { + /* Bucks 1-3 which support dynamic voltage scaling */ + PCA_DATA("BUCK1", PCA9450_BUCK1CTRL, HW_STATE_CONTROL, + PCA9450_BUCK1OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, + pca9450_buck123_vranges, true), + PCA_DATA("BUCK2", PCA9450_BUCK2CTRL, HW_STATE_CONTROL, + PCA9450_BUCK2OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, + pca9450_buck123_vranges, true), + PCA_DATA("BUCK3", PCA9450_BUCK3CTRL, HW_STATE_CONTROL, + PCA9450_BUCK3OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, + pca9450_buck123_vranges, true), + /* Bucks 4-6 which do not support dynamic voltage scaling */ + PCA_DATA("BUCK4", PCA9450_BUCK4CTRL, HW_STATE_CONTROL, + PCA9450_BUCK4OUT, PCA9450_DVS_BUCK_RUN_MASK, + pca9450_buck456_vranges, false), + PCA_DATA("BUCK5", PCA9450_BUCK5CTRL, HW_STATE_CONTROL, + PCA9450_BUCK5OUT, PCA9450_DVS_BUCK_RUN_MASK, + pca9450_buck456_vranges, false), + PCA_DATA("BUCK6", PCA9450_BUCK6CTRL, HW_STATE_CONTROL, + PCA9450_BUCK6OUT, PCA9450_DVS_BUCK_RUN_MASK, + pca9450_buck456_vranges, false), + /* LDOs */ + PCA_DATA("LDO1", PCA9450_LDO1CTRL, HW_STATE_CONTROL, + PCA9450_LDO1CTRL, PCA9450_LDO12_MASK, + pca9450_ldo1_vranges, false), + PCA_DATA("LDO2", PCA9450_LDO2CTRL, HW_STATE_CONTROL, + PCA9450_LDO2CTRL, PCA9450_LDO12_MASK, + pca9450_ldo2_vranges, false), + PCA_DATA("LDO3", PCA9450_LDO3CTRL, HW_STATE_CONTROL, + PCA9450_LDO3CTRL, PCA9450_LDO34_MASK, + pca9450_ldo34_vranges, false), + PCA_DATA("LDO4", PCA9450_LDO4CTRL, HW_STATE_CONTROL, + PCA9450_LDO4CTRL, PCA9450_LDO34_MASK, + pca9450_ldo34_vranges, false), + PCA_DATA("LDO5", PCA9450_LDO5CTRL_H, HW_STATE_CONTROL, + PCA9450_LDO5CTRL_H, PCA9450_LDO5_MASK, + pca9450_ldo5_vranges, false), +}; + +static int vrange_find_value(struct pca9450_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 pca9450_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 pca9450_get_enable(struct udevice *dev) +{ + struct pca9450_plat *plat = dev_get_plat(dev); + int val; + + /* + * boot critical regulators on pca9450 must not be controlled by sw + * due to the 'feature' which leaves power rails down if pca9450 is + * reseted to snvs state. hence we can't get the state here. + * + * if we are alive it means we probably are on run state and + * if the regulator can't be controlled we can assume it is + * enabled. + */ + if (plat->enablemask == HW_STATE_CONTROL) + return 1; + + val = pmic_reg_read(dev->parent, plat->enable_reg); + if (val < 0) + return val; + + return (val & plat->enablemask); +} + +static int pca9450_set_enable(struct udevice *dev, bool enable) +{ + int val = 0; + struct pca9450_plat *plat = dev_get_plat(dev); + + /* + * boot critical regulators on pca9450 must not be controlled by sw + * due to the 'feature' which leaves power rails down if pca9450 is + * reseted to snvs state. Hence we can't set the state here. + */ + if (plat->enablemask == HW_STATE_CONTROL) + return enable ? 0 : -EINVAL; + + if (enable) + val = plat->enablemask; + + return pmic_clrsetbits(dev->parent, plat->enable_reg, plat->enablemask, + val); +} + +static int pca9450_get_value(struct udevice *dev) +{ + struct pca9450_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 pca9450_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 pca9450_set_value(struct udevice *dev, int uvolt) +{ + struct pca9450_plat *plat = dev_get_plat(dev); + unsigned int sel; + int i, found = 0; + + /* + * An under/overshooting may occur if voltage is changed for other + * regulators but buck 1,2,3 or 4 when regulator is enabled. Prevent + * change to protect the HW + */ + if (!plat->dvs) + if (pca9450_get_enable(dev)) { + /* If the value is already set, skip the warning. */ + if (pca9450_get_value(dev) == uvolt) + return 0; + pr_err("Only DVS bucks can be changed when enabled\n"); + return -EINVAL; + } + + for (i = 0; i < plat->numranges; i++) { + struct pca9450_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 pca9450_regulator_probe(struct udevice *dev) +{ + struct pca9450_plat *plat = dev_get_plat(dev); + int i, type; + + type = dev_get_driver_data(dev_get_parent(dev)); + + if (type != NXP_CHIP_TYPE_PCA9450A && type != NXP_CHIP_TYPE_PCA9450BC) { + debug("Unknown PMIC type\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(pca9450_reg_data); i++) { + if (strcmp(dev->name, pca9450_reg_data[i].name)) + continue; + + /* PCA9450B/PCA9450C uses BUCK1 and BUCK3 in dual-phase */ + if (type == NXP_CHIP_TYPE_PCA9450BC && + !strcmp(pca9450_reg_data[i].name, "BUCK3")) { + continue; + } + + *plat = pca9450_reg_data[i]; + + return 0; + } + + pr_err("Unknown regulator '%s'\n", dev->name); + + return -ENOENT; +} + +static const struct dm_regulator_ops pca9450_regulator_ops = { + .get_value = pca9450_get_value, + .set_value = pca9450_set_value, + .get_enable = pca9450_get_enable, + .set_enable = pca9450_set_enable, +}; + +U_BOOT_DRIVER(pca9450_regulator) = { + .name = PCA9450_REGULATOR_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &pca9450_regulator_ops, + .probe = pca9450_regulator_probe, + .plat_auto = sizeof(struct pca9450_plat), +}; |