diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk_stm32mp1.c | 295 | ||||
-rw-r--r-- | drivers/gpio/stm32_gpio.c | 149 | ||||
-rw-r--r-- | drivers/net/dwc_eth_qos.c | 2 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-stmfx.c | 225 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl_stm32.c | 27 | ||||
-rw-r--r-- | drivers/power/regulator/stm32-vrefbuf.c | 16 | ||||
-rw-r--r-- | drivers/ram/stm32mp1/stm32mp1_tests.c | 98 |
7 files changed, 674 insertions, 138 deletions
diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c index 6c5eddbd9a..c8840b9e5f 100644 --- a/drivers/clk/clk_stm32mp1.c +++ b/drivers/clk/clk_stm32mp1.c @@ -17,6 +17,7 @@ #include <linux/bitops.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <asm/arch/sys_proto.h> #include <dt-bindings/clock/stm32mp1-clks.h> #include <dt-bindings/clock/stm32mp1-clksrc.h> @@ -644,8 +645,18 @@ static const struct stm32mp1_clk_sel stm32mp1_clk_sel[_PARENT_SEL_NB] = { }; #ifdef STM32MP1_CLOCK_TREE_INIT + /* define characteristic of PLL according type */ +#define DIVM_MIN 0 +#define DIVM_MAX 63 #define DIVN_MIN 24 +#define DIVP_MIN 0 +#define DIVP_MAX 127 +#define FRAC_MAX 8192 + +#define PLL1600_VCO_MIN 800000000 +#define PLL1600_VCO_MAX 1600000000 + static const struct stm32mp1_pll stm32mp1_pll[PLL_TYPE_NB] = { [PLL_800] = { .refclk_min = 4, @@ -1190,6 +1201,213 @@ static ulong stm32mp1_clk_get_rate(struct clk *clk) } #ifdef STM32MP1_CLOCK_TREE_INIT + +bool stm32mp1_supports_opp(u32 opp_id, u32 cpu_type) +{ + unsigned int id; + + switch (opp_id) { + case 1: + case 2: + id = opp_id; + break; + default: + id = 1; /* default value */ + break; + } + + switch (cpu_type) { + case CPU_STM32MP157Fxx: + case CPU_STM32MP157Dxx: + case CPU_STM32MP153Fxx: + case CPU_STM32MP153Dxx: + case CPU_STM32MP151Fxx: + case CPU_STM32MP151Dxx: + return true; + default: + return id == 1; + } +} + +__weak void board_vddcore_init(u32 voltage_mv) +{ +} + +/* + * gets OPP parameters (frequency in KHz and voltage in mV) from + * an OPP table subnode. Platform HW support capabilities are also checked. + * Returns 0 on success and a negative FDT error code on failure. + */ +static int stm32mp1_get_opp(u32 cpu_type, ofnode subnode, + u32 *freq_khz, u32 *voltage_mv) +{ + u32 opp_hw; + u64 read_freq_64; + u32 read_voltage_32; + + *freq_khz = 0; + *voltage_mv = 0; + + opp_hw = ofnode_read_u32_default(subnode, "opp-supported-hw", 0); + if (opp_hw) + if (!stm32mp1_supports_opp(opp_hw, cpu_type)) + return -FDT_ERR_BADVALUE; + + read_freq_64 = ofnode_read_u64_default(subnode, "opp-hz", 0) / + 1000ULL; + read_voltage_32 = ofnode_read_u32_default(subnode, "opp-microvolt", 0) / + 1000U; + + if (!read_voltage_32 || !read_freq_64) + return -FDT_ERR_NOTFOUND; + + /* Frequency value expressed in KHz must fit on 32 bits */ + if (read_freq_64 > U32_MAX) + return -FDT_ERR_BADVALUE; + + /* Millivolt value must fit on 16 bits */ + if (read_voltage_32 > U16_MAX) + return -FDT_ERR_BADVALUE; + + *freq_khz = (u32)read_freq_64; + *voltage_mv = read_voltage_32; + + return 0; +} + +/* + * parses OPP table in DT and finds the parameters for the + * highest frequency supported by the HW platform. + * Returns 0 on success and a negative FDT error code on failure. + */ +int stm32mp1_get_max_opp_freq(struct stm32mp1_clk_priv *priv, u64 *freq_hz) +{ + ofnode node, subnode; + int ret; + u32 freq = 0U, voltage = 0U; + u32 cpu_type = get_cpu_type(); + + node = ofnode_by_compatible(ofnode_null(), "operating-points-v2"); + if (!ofnode_valid(node)) + return -FDT_ERR_NOTFOUND; + + ofnode_for_each_subnode(subnode, node) { + unsigned int read_freq; + unsigned int read_voltage; + + ret = stm32mp1_get_opp(cpu_type, subnode, + &read_freq, &read_voltage); + if (ret) + continue; + + if (read_freq > freq) { + freq = read_freq; + voltage = read_voltage; + } + } + + if (!freq || !voltage) + return -FDT_ERR_NOTFOUND; + + *freq_hz = (u64)1000U * freq; + board_vddcore_init(voltage); + + return 0; +} + +static int stm32mp1_pll1_opp(struct stm32mp1_clk_priv *priv, int clksrc, + u32 *pllcfg, u32 *fracv) +{ + u32 post_divm; + u32 input_freq; + u64 output_freq; + u64 freq; + u64 vco; + u32 divm, divn, divp, frac; + int i, ret; + u32 diff; + u32 best_diff = U32_MAX; + + /* PLL1 is 1600 */ + const u32 DIVN_MAX = stm32mp1_pll[PLL_1600].divn_max; + const u32 POST_DIVM_MIN = stm32mp1_pll[PLL_1600].refclk_min * 1000000U; + const u32 POST_DIVM_MAX = stm32mp1_pll[PLL_1600].refclk_max * 1000000U; + + ret = stm32mp1_get_max_opp_freq(priv, &output_freq); + if (ret) { + debug("PLL1 OPP configuration not found (%d).\n", ret); + return ret; + } + + switch (clksrc) { + case CLK_PLL12_HSI: + input_freq = stm32mp1_clk_get_fixed(priv, _HSI); + break; + case CLK_PLL12_HSE: + input_freq = stm32mp1_clk_get_fixed(priv, _HSE); + break; + default: + return -EINTR; + } + + /* Following parameters have always the same value */ + pllcfg[PLLCFG_Q] = 0; + pllcfg[PLLCFG_R] = 0; + pllcfg[PLLCFG_O] = PQR(1, 0, 0); + + for (divm = DIVM_MAX; divm >= DIVM_MIN; divm--) { + post_divm = (u32)(input_freq / (divm + 1)); + if (post_divm < POST_DIVM_MIN || post_divm > POST_DIVM_MAX) + continue; + + for (divp = DIVP_MIN; divp <= DIVP_MAX; divp++) { + freq = output_freq * (divm + 1) * (divp + 1); + divn = (u32)((freq / input_freq) - 1); + if (divn < DIVN_MIN || divn > DIVN_MAX) + continue; + + frac = (u32)(((freq * FRAC_MAX) / input_freq) - + ((divn + 1) * FRAC_MAX)); + /* 2 loops to refine the fractional part */ + for (i = 2; i != 0; i--) { + if (frac > FRAC_MAX) + break; + + vco = (post_divm * (divn + 1)) + + ((post_divm * (u64)frac) / + FRAC_MAX); + if (vco < (PLL1600_VCO_MIN / 2) || + vco > (PLL1600_VCO_MAX / 2)) { + frac++; + continue; + } + freq = vco / (divp + 1); + if (output_freq < freq) + diff = (u32)(freq - output_freq); + else + diff = (u32)(output_freq - freq); + if (diff < best_diff) { + pllcfg[PLLCFG_M] = divm; + pllcfg[PLLCFG_N] = divn; + pllcfg[PLLCFG_P] = divp; + *fracv = frac; + + if (diff == 0) + return 0; + + best_diff = diff; + } + frac++; + } + } + } + + if (best_diff == U32_MAX) + return -1; + + return 0; +} + static void stm32mp1_ls_osc_set(int enable, fdt_addr_t rcc, u32 offset, u32 mask_on) { @@ -1661,9 +1879,12 @@ static int stm32mp1_clktree(struct udevice *dev) unsigned int clksrc[CLKSRC_NB]; unsigned int clkdiv[CLKDIV_NB]; unsigned int pllcfg[_PLL_NB][PLLCFG_NB]; - ofnode plloff[_PLL_NB]; - int ret, len; - uint i; + unsigned int pllfracv[_PLL_NB]; + unsigned int pllcsg[_PLL_NB][PLLCSG_NB]; + bool pllcfg_valid[_PLL_NB]; + bool pllcsg_set[_PLL_NB]; + int ret; + int i, len; int lse_css = 0; const u32 *pkcs_cell; @@ -1683,16 +1904,43 @@ static int stm32mp1_clktree(struct udevice *dev) /* check mandatory field in each pll */ for (i = 0; i < _PLL_NB; i++) { char name[12]; + ofnode node; sprintf(name, "st,pll@%d", i); - plloff[i] = dev_read_subnode(dev, name); - if (!ofnode_valid(plloff[i])) - continue; - ret = ofnode_read_u32_array(plloff[i], "cfg", - pllcfg[i], PLLCFG_NB); - if (ret < 0) { - debug("field cfg invalid: error %d\n", ret); - return -FDT_ERR_NOTFOUND; + node = dev_read_subnode(dev, name); + pllcfg_valid[i] = ofnode_valid(node); + pllcsg_set[i] = false; + if (pllcfg_valid[i]) { + debug("DT for PLL %d @ %s\n", i, name); + ret = ofnode_read_u32_array(node, "cfg", + pllcfg[i], PLLCFG_NB); + if (ret < 0) { + debug("field cfg invalid: error %d\n", ret); + return -FDT_ERR_NOTFOUND; + } + pllfracv[i] = ofnode_read_u32_default(node, "frac", 0); + + ret = ofnode_read_u32_array(node, "csg", pllcsg[i], + PLLCSG_NB); + if (!ret) { + pllcsg_set[i] = true; + } else if (ret != -FDT_ERR_NOTFOUND) { + debug("invalid csg node for pll@%d res=%d\n", + i, ret); + return ret; + } + } else if (i == _PLL1) { + /* use OPP for PLL1 for A7 CPU */ + debug("DT for PLL %d with OPP\n", i); + ret = stm32mp1_pll1_opp(priv, + clksrc[CLKSRC_PLL12], + pllcfg[i], + &pllfracv[i]); + if (ret) { + debug("PLL %d with OPP error = %d\n", i, ret); + return ret; + } + pllcfg_valid[i] = true; } } @@ -1778,29 +2026,18 @@ static int stm32mp1_clktree(struct udevice *dev) /* configure and start PLLs */ debug("configure PLLs\n"); for (i = 0; i < _PLL_NB; i++) { - u32 fracv; - u32 csg[PLLCSG_NB]; - - debug("configure PLL %d @ %d\n", i, - ofnode_to_offset(plloff[i])); - if (!ofnode_valid(plloff[i])) + if (!pllcfg_valid[i]) continue; - - fracv = ofnode_read_u32_default(plloff[i], "frac", 0); - pll_config(priv, i, pllcfg[i], fracv); - ret = ofnode_read_u32_array(plloff[i], "csg", csg, PLLCSG_NB); - if (!ret) { - pll_csg(priv, i, csg); - } else if (ret != -FDT_ERR_NOTFOUND) { - debug("invalid csg node for pll@%d res=%d\n", i, ret); - return ret; - } + debug("configure PLL %d\n", i); + pll_config(priv, i, pllcfg[i], pllfracv[i]); + if (pllcsg_set[i]) + pll_csg(priv, i, pllcsg[i]); pll_start(priv, i); } /* wait and start PLLs ouptut when ready */ for (i = 0; i < _PLL_NB; i++) { - if (!ofnode_valid(plloff[i])) + if (!pllcfg_valid[i]) continue; debug("output PLL %d\n", i); pll_output(priv, i, pllcfg[i][PLLCFG_O]); @@ -2050,6 +2287,8 @@ static int stm32mp1_clk_probe(struct udevice *dev) /* clock tree init is done only one time, before relocation */ if (!(gd->flags & GD_FLG_RELOC)) result = stm32mp1_clktree(dev); + if (result) + printf("clock tree initialization failed (%d)\n", result); #endif #ifndef CONFIG_SPL_BUILD diff --git a/drivers/gpio/stm32_gpio.c b/drivers/gpio/stm32_gpio.c index 4f710b6b6a..5bff27f75b 100644 --- a/drivers/gpio/stm32_gpio.c +++ b/drivers/gpio/stm32_gpio.c @@ -18,9 +18,65 @@ #include <linux/errno.h> #include <linux/io.h> -#define MODE_BITS(gpio_pin) (gpio_pin * 2) +#define MODE_BITS(gpio_pin) ((gpio_pin) * 2) #define MODE_BITS_MASK 3 -#define BSRR_BIT(gpio_pin, value) BIT(gpio_pin + (value ? 0 : 16)) +#define BSRR_BIT(gpio_pin, value) BIT((gpio_pin) + (value ? 0 : 16)) + +#define PUPD_BITS(gpio_pin) ((gpio_pin) * 2) +#define PUPD_MASK 3 + +#define OTYPE_BITS(gpio_pin) (gpio_pin) +#define OTYPE_MSK 1 + +static void stm32_gpio_set_moder(struct stm32_gpio_regs *regs, + int idx, + int mode) +{ + int bits_index; + int mask; + + bits_index = MODE_BITS(idx); + mask = MODE_BITS_MASK << bits_index; + + clrsetbits_le32(®s->moder, mask, mode << bits_index); +} + +static int stm32_gpio_get_moder(struct stm32_gpio_regs *regs, int idx) +{ + return (readl(®s->moder) >> MODE_BITS(idx)) & MODE_BITS_MASK; +} + +static void stm32_gpio_set_otype(struct stm32_gpio_regs *regs, + int idx, + enum stm32_gpio_otype otype) +{ + int bits; + + bits = OTYPE_BITS(idx); + clrsetbits_le32(®s->otyper, OTYPE_MSK << bits, otype << bits); +} + +static enum stm32_gpio_otype stm32_gpio_get_otype(struct stm32_gpio_regs *regs, + int idx) +{ + return (readl(®s->otyper) >> OTYPE_BITS(idx)) & OTYPE_MSK; +} + +static void stm32_gpio_set_pupd(struct stm32_gpio_regs *regs, + int idx, + enum stm32_gpio_pupd pupd) +{ + int bits; + + bits = PUPD_BITS(idx); + clrsetbits_le32(®s->pupdr, PUPD_MASK << bits, pupd << bits); +} + +static enum stm32_gpio_pupd stm32_gpio_get_pupd(struct stm32_gpio_regs *regs, + int idx) +{ + return (readl(®s->pupdr) >> PUPD_BITS(idx)) & PUPD_MASK; +} /* * convert gpio offset to gpio index taking into account gpio holes @@ -47,18 +103,13 @@ static int stm32_gpio_direction_input(struct udevice *dev, unsigned offset) { struct stm32_gpio_priv *priv = dev_get_priv(dev); struct stm32_gpio_regs *regs = priv->regs; - int bits_index; - int mask; int idx; idx = stm32_offset_to_index(dev, offset); if (idx < 0) return idx; - bits_index = MODE_BITS(idx); - mask = MODE_BITS_MASK << bits_index; - - clrsetbits_le32(®s->moder, mask, STM32_GPIO_MODE_IN << bits_index); + stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_IN); return 0; } @@ -68,18 +119,13 @@ static int stm32_gpio_direction_output(struct udevice *dev, unsigned offset, { struct stm32_gpio_priv *priv = dev_get_priv(dev); struct stm32_gpio_regs *regs = priv->regs; - int bits_index; - int mask; int idx; idx = stm32_offset_to_index(dev, offset); if (idx < 0) return idx; - bits_index = MODE_BITS(idx); - mask = MODE_BITS_MASK << bits_index; - - clrsetbits_le32(®s->moder, mask, STM32_GPIO_MODE_OUT << bits_index); + stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_OUT); writel(BSRR_BIT(idx, value), ®s->bsrr); @@ -141,12 +187,87 @@ static int stm32_gpio_get_function(struct udevice *dev, unsigned int offset) return GPIOF_FUNC; } +static int stm32_gpio_set_dir_flags(struct udevice *dev, unsigned int offset, + ulong flags) +{ + struct stm32_gpio_priv *priv = dev_get_priv(dev); + struct stm32_gpio_regs *regs = priv->regs; + int idx; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + if (flags & GPIOD_IS_OUT) { + int value = GPIOD_FLAGS_OUTPUT(flags); + + if (flags & GPIOD_OPEN_DRAIN) + stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_OD); + else + stm32_gpio_set_otype(regs, idx, STM32_GPIO_OTYPE_PP); + stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_OUT); + writel(BSRR_BIT(idx, value), ®s->bsrr); + + } else if (flags & GPIOD_IS_IN) { + stm32_gpio_set_moder(regs, idx, STM32_GPIO_MODE_IN); + if (flags & GPIOD_PULL_UP) + stm32_gpio_set_pupd(regs, idx, STM32_GPIO_PUPD_UP); + else if (flags & GPIOD_PULL_DOWN) + stm32_gpio_set_pupd(regs, idx, STM32_GPIO_PUPD_DOWN); + } + + return 0; +} + +static int stm32_gpio_get_dir_flags(struct udevice *dev, unsigned int offset, + ulong *flags) +{ + struct stm32_gpio_priv *priv = dev_get_priv(dev); + struct stm32_gpio_regs *regs = priv->regs; + int idx; + ulong dir_flags = 0; + + idx = stm32_offset_to_index(dev, offset); + if (idx < 0) + return idx; + + switch (stm32_gpio_get_moder(regs, idx)) { + case STM32_GPIO_MODE_OUT: + dir_flags |= GPIOD_IS_OUT; + if (stm32_gpio_get_otype(regs, idx) == STM32_GPIO_OTYPE_OD) + dir_flags |= GPIOD_OPEN_DRAIN; + if (readl(®s->idr) & BIT(idx)) + dir_flags |= GPIOD_IS_OUT_ACTIVE; + break; + case STM32_GPIO_MODE_IN: + dir_flags |= GPIOD_IS_IN; + switch (stm32_gpio_get_pupd(regs, idx)) { + case STM32_GPIO_PUPD_UP: + dir_flags |= GPIOD_PULL_UP; + break; + case STM32_GPIO_PUPD_DOWN: + dir_flags |= GPIOD_PULL_DOWN; + break; + default: + break; + } + break; + default: + break; + } + *flags = dir_flags; + + return 0; +} + static const struct dm_gpio_ops gpio_stm32_ops = { .direction_input = stm32_gpio_direction_input, .direction_output = stm32_gpio_direction_output, .get_value = stm32_gpio_get_value, .set_value = stm32_gpio_set_value, .get_function = stm32_gpio_get_function, + .set_dir_flags = stm32_gpio_set_dir_flags, + .get_dir_flags = stm32_gpio_get_dir_flags, }; static int gpio_stm32_probe(struct udevice *dev) diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 3f4437069b..62941bb175 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -2170,7 +2170,7 @@ static const struct udevice_id eqos_ids[] = { .data = (ulong)&eqos_tegra186_config }, { - .compatible = "snps,dwmac-4.20a", + .compatible = "st,stm32mp1-dwmac", .data = (ulong)&eqos_stm32_config }, { diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c index a3240ccd5a..c2ea82770e 100644 --- a/drivers/pinctrl/pinctrl-stmfx.c +++ b/drivers/pinctrl/pinctrl-stmfx.c @@ -74,17 +74,61 @@ static int stmfx_write(struct udevice *dev, uint offset, unsigned int val) return dm_i2c_reg_write(dev_get_parent(dev), offset, val); } -static int stmfx_gpio_get(struct udevice *dev, unsigned int offset) +static int stmfx_read_reg(struct udevice *dev, u8 reg_base, uint offset) { - u32 reg = STMFX_REG_GPIO_STATE + get_reg(offset); + u8 reg = reg_base + get_reg(offset); u32 mask = get_mask(offset); int ret; ret = stmfx_read(dev, reg); + if (ret < 0) + return ret; return ret < 0 ? ret : !!(ret & mask); } +static int stmfx_write_reg(struct udevice *dev, u8 reg_base, uint offset, + uint val) +{ + u8 reg = reg_base + get_reg(offset); + u32 mask = get_mask(offset); + int ret; + + ret = stmfx_read(dev, reg); + if (ret < 0) + return ret; + ret = (ret & ~mask) | (val ? mask : 0); + + return stmfx_write(dev, reg, ret); +} + +static int stmfx_conf_set_pupd(struct udevice *dev, unsigned int offset, + uint pupd) +{ + return stmfx_write_reg(dev, STMFX_REG_GPIO_PUPD, offset, pupd); +} + +static int stmfx_conf_get_pupd(struct udevice *dev, unsigned int offset) +{ + return stmfx_read_reg(dev, STMFX_REG_GPIO_PUPD, offset); +} + +static int stmfx_conf_set_type(struct udevice *dev, unsigned int offset, + uint type) +{ + return stmfx_write_reg(dev, STMFX_REG_GPIO_TYPE, offset, type); +} + +static int stmfx_conf_get_type(struct udevice *dev, unsigned int offset) +{ + return stmfx_read_reg(dev, STMFX_REG_GPIO_TYPE, offset); +} + +static int stmfx_gpio_get(struct udevice *dev, unsigned int offset) +{ + return stmfx_read_reg(dev, STMFX_REG_GPIO_STATE, offset); +} + static int stmfx_gpio_set(struct udevice *dev, unsigned int offset, int value) { u32 reg = value ? STMFX_REG_GPO_SET : STMFX_REG_GPO_CLR; @@ -95,50 +139,103 @@ static int stmfx_gpio_set(struct udevice *dev, unsigned int offset, int value) static int stmfx_gpio_get_function(struct udevice *dev, unsigned int offset) { - u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); - u32 mask = get_mask(offset); - int ret; - - ret = stmfx_read(dev, reg); + int ret = stmfx_read_reg(dev, STMFX_REG_GPIO_DIR, offset); if (ret < 0) return ret; /* On stmfx, gpio pins direction is (0)input, (1)output. */ - return ret & mask ? GPIOF_OUTPUT : GPIOF_INPUT; + return ret ? GPIOF_OUTPUT : GPIOF_INPUT; } static int stmfx_gpio_direction_input(struct udevice *dev, unsigned int offset) { - u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); - u32 mask = get_mask(offset); - int ret; + return stmfx_write_reg(dev, STMFX_REG_GPIO_DIR, offset, 0); +} - ret = stmfx_read(dev, reg); +static int stmfx_gpio_direction_output(struct udevice *dev, + unsigned int offset, int value) +{ + int ret = stmfx_gpio_set(dev, offset, value); if (ret < 0) return ret; - ret &= ~mask; + return stmfx_write_reg(dev, STMFX_REG_GPIO_DIR, offset, 1); +} - return stmfx_write(dev, reg, ret & ~mask); +static int stmfx_gpio_set_dir_flags(struct udevice *dev, unsigned int offset, + ulong flags) +{ + int ret = -ENOTSUPP; + + if (flags & GPIOD_IS_OUT) { + if (flags & GPIOD_OPEN_SOURCE) + return -ENOTSUPP; + if (flags & GPIOD_OPEN_DRAIN) + ret = stmfx_conf_set_type(dev, offset, 0); + else /* PUSH-PULL */ + ret = stmfx_conf_set_type(dev, offset, 1); + if (ret) + return ret; + ret = stmfx_gpio_direction_output(dev, offset, + GPIOD_FLAGS_OUTPUT(flags)); + } else if (flags & GPIOD_IS_IN) { + ret = stmfx_gpio_direction_input(dev, offset); + if (ret) + return ret; + if (flags & GPIOD_PULL_UP) { + ret = stmfx_conf_set_type(dev, offset, 1); + if (ret) + return ret; + ret = stmfx_conf_set_pupd(dev, offset, 1); + } else if (flags & GPIOD_PULL_DOWN) { + ret = stmfx_conf_set_type(dev, offset, 1); + if (ret) + return ret; + ret = stmfx_conf_set_pupd(dev, offset, 0); + } + } + + return ret; } -static int stmfx_gpio_direction_output(struct udevice *dev, - unsigned int offset, int value) +static int stmfx_gpio_get_dir_flags(struct udevice *dev, unsigned int offset, + ulong *flags) { - u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); - u32 mask = get_mask(offset); + ulong dir_flags = 0; int ret; - ret = stmfx_gpio_set(dev, offset, value); - if (ret < 0) - return ret; - - ret = stmfx_read(dev, reg); - if (ret < 0) - return ret; + if (stmfx_gpio_get_function(dev, offset) == GPIOF_OUTPUT) { + dir_flags |= GPIOD_IS_OUT; + ret = stmfx_conf_get_type(dev, offset); + if (ret < 0) + return ret; + if (ret == 0) + dir_flags |= GPIOD_OPEN_DRAIN; + /* 1 = push-pull (default), open source not supported */ + ret = stmfx_gpio_get(dev, offset); + if (ret < 0) + return ret; + if (ret) + dir_flags |= GPIOD_IS_OUT_ACTIVE; + } else { + dir_flags |= GPIOD_IS_IN; + ret = stmfx_conf_get_type(dev, offset); + if (ret < 0) + return ret; + if (ret == 1) { + ret = stmfx_conf_get_pupd(dev, offset); + if (ret < 0) + return ret; + if (ret == 1) + dir_flags |= GPIOD_PULL_UP; + else + dir_flags |= GPIOD_PULL_DOWN; + } + } + *flags = dir_flags; - return stmfx_write(dev, reg, ret | mask); + return 0; } static int stmfx_gpio_probe(struct udevice *dev) @@ -169,6 +266,8 @@ static const struct dm_gpio_ops stmfx_gpio_ops = { .get_function = stmfx_gpio_get_function, .direction_input = stmfx_gpio_direction_input, .direction_output = stmfx_gpio_direction_output, + .set_dir_flags = stmfx_gpio_set_dir_flags, + .get_dir_flags = stmfx_gpio_get_dir_flags, }; U_BOOT_DRIVER(stmfx_gpio) = { @@ -190,36 +289,6 @@ static const struct pinconf_param stmfx_pinctrl_conf_params[] = { { "output-low", PIN_CONFIG_OUTPUT, 0 }, }; -static int stmfx_pinctrl_set_pupd(struct udevice *dev, - unsigned int pin, u32 pupd) -{ - u8 reg = STMFX_REG_GPIO_PUPD + get_reg(pin); - u32 mask = get_mask(pin); - int ret; - - ret = stmfx_read(dev, reg); - if (ret < 0) - return ret; - ret = (ret & ~mask) | (pupd ? mask : 0); - - return stmfx_write(dev, reg, ret); -} - -static int stmfx_pinctrl_set_type(struct udevice *dev, - unsigned int pin, u32 type) -{ - u8 reg = STMFX_REG_GPIO_TYPE + get_reg(pin); - u32 mask = get_mask(pin); - int ret; - - ret = stmfx_read(dev, reg); - if (ret < 0) - return ret; - ret = (ret & ~mask) | (type ? mask : 0); - - return stmfx_write(dev, reg, ret); -} - static int stmfx_pinctrl_conf_set(struct udevice *dev, unsigned int pin, unsigned int param, unsigned int arg) { @@ -235,22 +304,22 @@ static int stmfx_pinctrl_conf_set(struct udevice *dev, unsigned int pin, case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_DRIVE_PUSH_PULL: - ret = stmfx_pinctrl_set_type(dev, pin, 0); + ret = stmfx_conf_set_type(dev, pin, 0); break; case PIN_CONFIG_BIAS_PULL_DOWN: - ret = stmfx_pinctrl_set_type(dev, pin, 1); + ret = stmfx_conf_set_type(dev, pin, 1); if (ret) return ret; - ret = stmfx_pinctrl_set_pupd(dev, pin, 0); + ret = stmfx_conf_set_pupd(dev, pin, 0); break; case PIN_CONFIG_BIAS_PULL_UP: - ret = stmfx_pinctrl_set_type(dev, pin, 1); + ret = stmfx_conf_set_type(dev, pin, 1); if (ret) return ret; - ret = stmfx_pinctrl_set_pupd(dev, pin, 1); + ret = stmfx_conf_set_pupd(dev, pin, 1); break; case PIN_CONFIG_DRIVE_OPEN_DRAIN: - ret = stmfx_pinctrl_set_type(dev, pin, 1); + ret = stmfx_conf_set_type(dev, pin, 1); break; case PIN_CONFIG_OUTPUT: ret = stmfx_gpio_direction_output(plat->gpio, pin, arg); @@ -289,6 +358,34 @@ static const char *stmfx_pinctrl_get_pin_name(struct udevice *dev, return pin_name; } +static const char *stmfx_pinctrl_get_pin_conf(struct udevice *dev, + unsigned int pin, int func) +{ + int pupd, type; + + type = stmfx_conf_get_type(dev, pin); + if (type < 0) + return ""; + + if (func == GPIOF_OUTPUT) { + if (type) + return "drive-open-drain"; + else + return ""; /* default: push-pull*/ + } + if (!type) + return ""; /* default: bias-disable*/ + + pupd = stmfx_conf_get_pupd(dev, pin); + if (pupd < 0) + return ""; + + if (pupd) + return "bias-pull-up"; + else + return "bias-pull-down"; +} + static int stmfx_pinctrl_get_pin_muxing(struct udevice *dev, unsigned int selector, char *buf, int size) @@ -300,7 +397,9 @@ static int stmfx_pinctrl_get_pin_muxing(struct udevice *dev, if (func < 0) return func; - snprintf(buf, size, "%s", func == GPIOF_INPUT ? "input" : "output"); + snprintf(buf, size, "%s ", func == GPIOF_INPUT ? "input" : "output"); + + strncat(buf, stmfx_pinctrl_get_pin_conf(dev, selector, func), size); return 0; } diff --git a/drivers/pinctrl/pinctrl_stm32.c b/drivers/pinctrl/pinctrl_stm32.c index fc241fdcde..71fa29a389 100644 --- a/drivers/pinctrl/pinctrl_stm32.c +++ b/drivers/pinctrl/pinctrl_stm32.c @@ -45,6 +45,17 @@ static const char * const pinmux_mode[PINMUX_MODE_COUNT] = { "alt function", }; +static const char * const pinmux_output[] = { + [STM32_GPIO_PUPD_NO] = "bias-disable", + [STM32_GPIO_PUPD_UP] = "bias-pull-up", + [STM32_GPIO_PUPD_DOWN] = "bias-pull-down", +}; + +static const char * const pinmux_input[] = { + [STM32_GPIO_OTYPE_PP] = "drive-push-pull", + [STM32_GPIO_OTYPE_OD] = "drive-open-drain", +}; + static int stm32_pinctrl_get_af(struct udevice *dev, unsigned int offset) { struct stm32_gpio_priv *priv = dev_get_priv(dev); @@ -182,10 +193,12 @@ static int stm32_pinctrl_get_pin_muxing(struct udevice *dev, int size) { struct udevice *gpio_dev; + struct stm32_gpio_priv *priv; const char *label; int mode; int af_num; unsigned int gpio_idx; + u32 pupd, otype; /* look up for the bank which owns the requested pin */ gpio_dev = stm32_pinctrl_get_gpio_dev(dev, selector, &gpio_idx); @@ -194,9 +207,9 @@ static int stm32_pinctrl_get_pin_muxing(struct udevice *dev, return -ENODEV; mode = gpio_get_raw_function(gpio_dev, gpio_idx, &label); - dev_dbg(dev, "selector = %d gpio_idx = %d mode = %d\n", selector, gpio_idx, mode); + priv = dev_get_priv(gpio_dev); switch (mode) { @@ -211,9 +224,17 @@ static int stm32_pinctrl_get_pin_muxing(struct udevice *dev, snprintf(buf, size, "%s %d", pinmux_mode[mode], af_num); break; case GPIOF_OUTPUT: + pupd = (readl(&priv->regs->pupdr) >> (gpio_idx * 2)) & + PUPD_MASK; + snprintf(buf, size, "%s %s %s", + pinmux_mode[mode], pinmux_output[pupd], + label ? label : ""); + break; case GPIOF_INPUT: - snprintf(buf, size, "%s %s", - pinmux_mode[mode], label ? label : ""); + otype = (readl(&priv->regs->otyper) >> gpio_idx) & OTYPE_MSK; + snprintf(buf, size, "%s %s %s", + pinmux_mode[mode], pinmux_input[otype], + label ? label : ""); break; } diff --git a/drivers/power/regulator/stm32-vrefbuf.c b/drivers/power/regulator/stm32-vrefbuf.c index 250773514f..92136961c2 100644 --- a/drivers/power/regulator/stm32-vrefbuf.c +++ b/drivers/power/regulator/stm32-vrefbuf.c @@ -43,8 +43,20 @@ static int stm32_vrefbuf_set_enable(struct udevice *dev, bool enable) u32 val; int ret; - clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_HIZ | STM32_ENVR, - enable ? STM32_ENVR : STM32_HIZ); + if (enable && !(readl(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR)) { + /* + * There maybe an overshoot: + * - when disabling, then re-enabling vrefbuf too quickly + * - or upon platform reset as external capacitor maybe slow + * discharging (VREFBUF is HiZ at reset by default). + * So force active discharge (HiZ=0) for 1ms before enabling. + */ + clrbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_HIZ); + udelay(1000); + } + + clrsetbits_le32(priv->base + STM32_VREFBUF_CSR, STM32_ENVR, + enable ? STM32_ENVR : 0); if (!enable) return 0; diff --git a/drivers/ram/stm32mp1/stm32mp1_tests.c b/drivers/ram/stm32mp1/stm32mp1_tests.c index bacdd74705..952006aa14 100644 --- a/drivers/ram/stm32mp1/stm32mp1_tests.c +++ b/drivers/ram/stm32mp1/stm32mp1_tests.c @@ -14,10 +14,12 @@ #define ADDR_INVALID 0xFFFFFFFF +#define PATTERN_DEFAULT "-" + DECLARE_GLOBAL_DATA_PTR; static int get_bufsize(char *string, int argc, char *argv[], int arg_nb, - size_t *bufsize, size_t default_size) + size_t *bufsize, size_t default_size, size_t min_size) { unsigned long value; @@ -27,13 +29,14 @@ static int get_bufsize(char *string, int argc, char *argv[], int arg_nb, arg_nb, argv[arg_nb]); return -1; } - if (value > STM32_DDR_SIZE || value == 0) { - sprintf(string, "invalid size %s", argv[arg_nb]); + if (value > STM32_DDR_SIZE || value < min_size) { + sprintf(string, "invalid size %s (min=%d)", + argv[arg_nb], min_size); return -1; } - if (value & 0x3) { - sprintf(string, "unaligned size %s", - argv[arg_nb]); + if (value & (min_size - 1)) { + sprintf(string, "unaligned size %s (min=%d)", + argv[arg_nb], min_size); return -1; } *bufsize = value; @@ -102,6 +105,10 @@ static int get_pattern(char *string, int argc, char *argv[], int arg_nb, unsigned long value; if (argc > arg_nb) { + if (!strcmp(argv[arg_nb], PATTERN_DEFAULT)) { + *pattern = default_pattern; + return 0; + } if (strict_strtoul(argv[arg_nb], 16, &value) < 0) { sprintf(string, "invalid %d parameter %s", arg_nb, argv[arg_nb]); @@ -441,7 +448,7 @@ static enum test_result test_addressbus(struct stm32mp1_ddrctl *ctl, u32 bufsize; u32 error; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, STM32_DDR_SIZE, 4)) return TEST_ERROR; if (!is_power_of_2(bufsize)) { sprintf(string, "size 0x%x is not a power of 2", @@ -451,6 +458,7 @@ static enum test_result test_addressbus(struct stm32mp1_ddrctl *ctl, if (get_addr(string, argc, argv, 1, &addr)) return TEST_ERROR; + printf("running at 0x%08x length 0x%x\n", addr, bufsize); error = (u32)addressbus((u32 *)addr, bufsize); if (error) { sprintf(string, "0x%x: error for address 0x%x", @@ -470,7 +478,7 @@ static enum test_result test_memdevice(struct stm32mp1_ddrctl *ctl, size_t bufsize; u32 error; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4)) return TEST_ERROR; if (get_addr(string, argc, argv, 1, &addr)) return TEST_ERROR; @@ -512,7 +520,7 @@ static enum test_result test_sso(struct stm32mp1_ddrctl *ctl, u32 error = 0; u32 data; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4)) return TEST_ERROR; if (get_addr(string, argc, argv, 1, &addr)) return TEST_ERROR; @@ -584,7 +592,7 @@ static enum test_result test_random(struct stm32mp1_ddrctl *ctl, u32 error = 0; unsigned int seed; - if (get_bufsize(string, argc, argv, 0, &bufsize, 8 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 8)) return TEST_ERROR; if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) return TEST_ERROR; @@ -744,7 +752,7 @@ static enum test_result test_noise_burst(struct stm32mp1_ddrctl *ctl, int i; enum test_result res = TEST_PASSED; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 128)) return TEST_ERROR; if (get_pattern(string, argc, argv, 1, &pattern, 0xFFFFFFFF)) return TEST_ERROR; @@ -918,9 +926,11 @@ static enum test_result test_freq_pattern(struct stm32mp1_ddrctl *ctl, enum test_result res = TEST_PASSED, pattern_res; int i, bus_width; const u32 **patterns; - u32 bufsize; + u32 bufsize, addr; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 128)) + return TEST_ERROR; + if (get_addr(string, argc, argv, 1, &addr)) return TEST_ERROR; switch (readl(&ctl->mstr) & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK) { @@ -934,15 +944,14 @@ static enum test_result test_freq_pattern(struct stm32mp1_ddrctl *ctl, } printf("running test pattern at 0x%08x length 0x%x width = %d\n", - STM32_DDR_BASE, bufsize, bus_width); + addr, bufsize, bus_width); patterns = (const u32 **)(bus_width == 16 ? patterns_x16 : patterns_x32); for (i = 0; i < NB_PATTERN; i++) { printf("test data pattern %s:", patterns_comments[i]); - pattern_res = test_loop(patterns[i], (u32 *)STM32_DDR_BASE, - bufsize); + pattern_res = test_loop(patterns[i], (u32 *)addr, bufsize); if (pattern_res != TEST_PASSED) { printf("Failed\n"); return pattern_res; @@ -1007,7 +1016,7 @@ static enum test_result test_checkboard(struct stm32mp1_ddrctl *ctl, u32 checkboard[2] = {0x55555555, 0xAAAAAAAA}; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 8)) return TEST_ERROR; if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) return TEST_ERROR; @@ -1042,7 +1051,7 @@ static enum test_result test_blockseq(struct stm32mp1_ddrctl *ctl, u32 bufsize, nb_loop, loop = 0, addr, value; int i; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4)) return TEST_ERROR; if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) return TEST_ERROR; @@ -1076,7 +1085,7 @@ static enum test_result test_walkbit0(struct stm32mp1_ddrctl *ctl, u32 bufsize, nb_loop, loop = 0, addr, value; int i; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4)) return TEST_ERROR; if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) return TEST_ERROR; @@ -1114,7 +1123,7 @@ static enum test_result test_walkbit1(struct stm32mp1_ddrctl *ctl, u32 bufsize, nb_loop, loop = 0, addr, value; int i; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4)) return TEST_ERROR; if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) return TEST_ERROR; @@ -1156,7 +1165,7 @@ static enum test_result test_bitspread(struct stm32mp1_ddrctl *ctl, u32 bufsize, nb_loop, loop = 0, addr, bitspread[4]; int i, j; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 32)) return TEST_ERROR; if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) return TEST_ERROR; @@ -1203,7 +1212,7 @@ static enum test_result test_bitflip(struct stm32mp1_ddrctl *ctl, u32 bitflip[4]; - if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024)) + if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 32)) return TEST_ERROR; if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1)) return TEST_ERROR; @@ -1340,17 +1349,52 @@ static enum test_result test_all(struct stm32mp1_ddrctl *ctl, char *string, int argc, char *argv[]) { enum test_result res = TEST_PASSED, result; - int i, nb_error = 0; + int i, j, nb_error = 0, len; u32 loop = 0, nb_loop; + int argc_test; + char *argv_test[4]; + char loop_string[] = "1"; + char pattern_string[] = PATTERN_DEFAULT; + u32 *addr; if (get_nb_loop(string, argc, argv, 0, &nb_loop, 1)) return TEST_ERROR; + if (get_addr(string, argc, argv, 2, (u32 *)&addr)) + return TEST_ERROR; + while (!nb_error) { /* execute all the test except the lasts which are infinite */ for (i = 1; i < test_nb - NB_TEST_INFINITE; i++) { + argc_test = 0; + j = 0; + len = strlen(test[i].usage); + if (argc > 1 && j < len && + !strncmp("[size]", &test[i].usage[j], 6)) { + argv_test[argc_test++] = argv[1]; + j += 7; + } + if (argc > 2) { + if (j < len && + !strncmp("[loop]", &test[i].usage[j], 6)) { + argv_test[argc_test++] = loop_string; + j += 7; + } + if (j < len && + !strncmp("[pattern]", &test[i].usage[j], + 9)) { + argv_test[argc_test++] = pattern_string; + j += 10; + } + if (j < len && + !strncmp("[addr]", &test[i].usage[j], 6)) { + argv_test[argc_test++] = argv[2]; + j += 7; + } + } printf("execute %d:%s\n", (int)i, test[i].name); - result = test[i].fct(ctl, phy, string, 0, NULL); + result = test[i].fct(ctl, phy, string, + argc_test, argv_test); printf("result %d:%s = ", (int)i, test[i].name); if (result != TEST_PASSED) { nb_error++; @@ -1381,7 +1425,7 @@ static enum test_result test_all(struct stm32mp1_ddrctl *ctl, ****************************************************************/ const struct test_desc test[] = { - {test_all, "All", "[loop]", "Execute all tests", 1 }, + {test_all, "All", "[loop] [size] [addr]", "Execute all tests", 3 }, {test_databus, "Simple DataBus", "[addr]", "Verifies each data line by walking 1 on fixed address", 1 @@ -1418,9 +1462,9 @@ const struct test_desc test[] = { "Verifies r/w and memcopy(burst for pseudo random value.", 3 }, - {test_freq_pattern, "FrequencySelectivePattern", "[size]", + {test_freq_pattern, "FrequencySelectivePattern", "[size] [addr]", "write & test patterns: Mostly Zero, Mostly One and F/n", - 1 + 2 }, {test_blockseq, "BlockSequential", "[size] [loop] [addr]", "test incremental pattern", |