From: Fabien Dessenne <fabien.dessenne@xxxxxxxxxxx> Support the following IO synchronization parameters: - Delay (in ns) - Delay path (input / output) - Clock edge (single / double edge) - Clock inversion - Retiming These settings allow a fine tuning of the high speed interface signals. Update the debugfs pinconf-pins entry so it shows these parameters if one of them is set. Enable this feature for the stm32mp257 SoC. Co-developed-by: Valentin Caron <valentin.caron@xxxxxxxxxxx> Signed-off-by: Valentin Caron <valentin.caron@xxxxxxxxxxx> Signed-off-by: Fabien Dessenne <fabien.dessenne@xxxxxxxxxxx> Signed-off-by: Antonio Borneo <antonio.borneo@xxxxxxxxxxx> --- drivers/pinctrl/stm32/pinctrl-stm32.c | 207 +++++++++++++++++++++ drivers/pinctrl/stm32/pinctrl-stm32.h | 1 + drivers/pinctrl/stm32/pinctrl-stm32mp257.c | 2 + 3 files changed, 210 insertions(+) diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index b387f6caaf3f0..5b9c637ca0c93 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -46,6 +46,13 @@ #define STM32_GPIO_AFRL 0x20 #define STM32_GPIO_AFRH 0x24 #define STM32_GPIO_SECCFGR 0x30 +#define STM32_GPIO_DELAYRL 0x40 +#define STM32_GPIO_ADVCFGRL 0x48 + +#define STM32_GPIO_ADVCFGR_DLYPATH_POS 0 +#define STM32_GPIO_ADVCFGR_DE_POS 1 +#define STM32_GPIO_ADVCFGR_INVCLK_POS 2 +#define STM32_GPIO_ADVCFGR_RET_POS 3 /* custom bitfield to backup pin status */ #define STM32_GPIO_BKP_MODE_SHIFT 0 @@ -58,12 +65,23 @@ #define STM32_GPIO_BKP_PUPD_MASK GENMASK(9, 8) #define STM32_GPIO_BKP_TYPE 10 #define STM32_GPIO_BKP_VAL 11 +#define STM32_GPIO_BKP_ADVCFG_SHIFT 12 +#define STM32_GPIO_BKP_ADVCFG_MASK GENMASK(15, 12) +#define STM32_GPIO_BKP_DELAY_SHIFT 16 +#define STM32_GPIO_BKP_DELAY_MASK GENMASK(19, 16) #define STM32_GPIO_PINS_PER_BANK 16 #define STM32_GPIO_IRQ_LINE 16 #define SYSCFG_IRQMUX_MASK GENMASK(3, 0) +/* Vendor specific pin configurations */ +#define STM32_GPIO_PIN_CONFIG_DELAY_PATH (PIN_CONFIG_END + 1) +#define STM32_GPIO_PIN_CONFIG_CLK_EDGE (PIN_CONFIG_END + 2) +#define STM32_GPIO_PIN_CONFIG_CLK_TYPE (PIN_CONFIG_END + 3) +#define STM32_GPIO_PIN_CONFIG_RETIME (PIN_CONFIG_END + 4) +#define STM32_GPIO_PIN_CONFIG_DELAY (PIN_CONFIG_END + 5) + #define gpio_range_to_bank(chip) \ container_of(chip, struct stm32_gpio_bank, range) @@ -79,6 +97,14 @@ static const char * const stm32_gpio_functions[] = { "reserved", }; +static const struct pinconf_generic_params stm32_gpio_bindings[] = { + {"st,io-delay-path", STM32_GPIO_PIN_CONFIG_DELAY_PATH, 0}, + {"st,io-clk-edge", STM32_GPIO_PIN_CONFIG_CLK_EDGE, 0}, + {"st,io-clk-type", STM32_GPIO_PIN_CONFIG_CLK_TYPE, 0}, + {"st,io-retime", STM32_GPIO_PIN_CONFIG_RETIME, 0}, + {"st,io-delay", STM32_GPIO_PIN_CONFIG_DELAY, 0}, +}; + struct stm32_pinctrl_group { const char *name; unsigned long config; @@ -99,6 +125,7 @@ struct stm32_gpio_bank { u32 pin_backup[STM32_GPIO_PINS_PER_BANK]; u8 irq_type[STM32_GPIO_PINS_PER_BANK]; bool secure_control; + bool io_sync_control; }; struct stm32_pinctrl { @@ -194,6 +221,18 @@ static void stm32_gpio_backup_bias(struct stm32_gpio_bank *bank, u32 offset, bank->pin_backup[offset] |= bias << STM32_GPIO_BKP_PUPD_SHIFT; } +static void stm32_gpio_backup_advcfg(struct stm32_gpio_bank *bank, u32 offset, u32 bpos, u32 bval) +{ + bank->pin_backup[offset] &= ~BIT(STM32_GPIO_BKP_ADVCFG_SHIFT + bpos); + bank->pin_backup[offset] |= bval << (STM32_GPIO_BKP_ADVCFG_SHIFT + bpos); +} + +static void stm32_gpio_backup_delay(struct stm32_gpio_bank *bank, u32 offset, u32 delay) +{ + bank->pin_backup[offset] &= ~STM32_GPIO_BKP_DELAY_MASK; + bank->pin_backup[offset] |= delay << STM32_GPIO_BKP_DELAY_SHIFT; +} + /* GPIO functions */ static inline void __stm32_gpio_set(struct stm32_gpio_bank *bank, @@ -1051,6 +1090,106 @@ static u32 stm32_pconf_get_bias(struct stm32_gpio_bank *bank, return (val >> (offset * 2)); } +static int stm32_pconf_set_advcfgr(struct stm32_gpio_bank *bank, int offset, u32 bpos, u32 bval) +{ + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + int advcfgr_offset = STM32_GPIO_ADVCFGRL + (offset / 8) * 4; + int advcfgr_bit = bpos + (offset % 8) * 4; + unsigned long flags; + int err = 0; + u32 val; + + if (!bank->io_sync_control) + return -ENOTSUPP; + + spin_lock_irqsave(&bank->lock, flags); + + if (pctl->hwlock) { + err = hwspin_lock_timeout_in_atomic(pctl->hwlock, HWSPNLCK_TIMEOUT); + if (err) { + dev_err(pctl->dev, "Can't get hwspinlock\n"); + goto unlock; + } + } + + val = readl_relaxed(bank->base + advcfgr_offset); + val &= ~BIT(advcfgr_bit); + val |= bval << advcfgr_bit; + writel_relaxed(val, bank->base + advcfgr_offset); + + if (pctl->hwlock) + hwspin_unlock_in_atomic(pctl->hwlock); + + stm32_gpio_backup_advcfg(bank, offset, bpos, bval); + +unlock: + spin_unlock_irqrestore(&bank->lock, flags); + + return err; +} + +static u32 stm32_pconf_get_advcfgr(struct stm32_gpio_bank *bank, int offset, u32 bpos) +{ + int advcfgr_offset = STM32_GPIO_ADVCFGRL + (offset / 8) * 4; + int advcfgr_bit = bpos + (offset % 8) * 4; + u32 val; + + val = readl_relaxed(bank->base + advcfgr_offset); + val &= BIT(advcfgr_bit); + + return val >> advcfgr_bit; +} + +static int stm32_pconf_set_delay(struct stm32_gpio_bank *bank, int offset, u32 delay) +{ + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + int delay_offset = STM32_GPIO_DELAYRL + (offset / 8) * 4; + int delay_shift = (offset % 8) * 4; + unsigned long flags; + int err = 0; + u32 val; + + if (!bank->io_sync_control) + return -ENOTSUPP; + + spin_lock_irqsave(&bank->lock, flags); + + if (pctl->hwlock) { + err = hwspin_lock_timeout_in_atomic(pctl->hwlock, HWSPNLCK_TIMEOUT); + if (err) { + dev_err(pctl->dev, "Can't get hwspinlock\n"); + goto unlock; + } + } + + val = readl_relaxed(bank->base + delay_offset); + val &= ~GENMASK(delay_shift + 3, delay_shift); + val |= (delay << delay_shift); + writel_relaxed(val, bank->base + delay_offset); + + if (pctl->hwlock) + hwspin_unlock_in_atomic(pctl->hwlock); + + stm32_gpio_backup_delay(bank, offset, delay); + +unlock: + spin_unlock_irqrestore(&bank->lock, flags); + + return err; +} + +static u32 stm32_pconf_get_delay(struct stm32_gpio_bank *bank, int offset) +{ + int delay_offset = STM32_GPIO_DELAYRL + (offset / 8) * 4; + int delay_shift = (offset % 8) * 4; + u32 val; + + val = readl_relaxed(bank->base + delay_offset); + val &= GENMASK(delay_shift + 3, delay_shift); + + return val >> delay_shift; +} + static bool stm32_pconf_get(struct stm32_gpio_bank *bank, unsigned int offset, bool dir) { @@ -1118,6 +1257,21 @@ static int stm32_pconf_parse_conf(struct pinctrl_dev *pctldev, __stm32_gpio_set(bank, offset, arg); ret = stm32_pmx_gpio_set_direction(pctldev, range, pin, false); break; + case STM32_GPIO_PIN_CONFIG_DELAY_PATH: + ret = stm32_pconf_set_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_DLYPATH_POS, arg); + break; + case STM32_GPIO_PIN_CONFIG_CLK_EDGE: + ret = stm32_pconf_set_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_DE_POS, arg); + break; + case STM32_GPIO_PIN_CONFIG_CLK_TYPE: + ret = stm32_pconf_set_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_INVCLK_POS, arg); + break; + case STM32_GPIO_PIN_CONFIG_RETIME: + ret = stm32_pconf_set_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_RET_POS, arg); + break; + case STM32_GPIO_PIN_CONFIG_DELAY: + ret = stm32_pconf_set_delay(bank, offset, arg); + break; default: ret = -ENOTSUPP; } @@ -1260,6 +1414,22 @@ static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev, case 3: break; } + + if (bank->io_sync_control) { + u32 retime, clk_type, clk_edge, delay_path, delay; + + retime = stm32_pconf_get_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_RET_POS); + clk_type = stm32_pconf_get_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_INVCLK_POS); + clk_edge = stm32_pconf_get_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_DE_POS); + delay_path = stm32_pconf_get_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_DLYPATH_POS); + delay = stm32_pconf_get_delay(bank, offset); + + if (retime || clk_type || clk_edge || delay_path || delay) + seq_printf(s, " - Retime:%d InvClk:%d DblEdge:%d DelayIn:%d", + retime, clk_type, clk_edge, delay_path); + if (delay) + seq_printf(s, " - Delay: %d (%d ps)", delay, delay * 250); + } } static const struct pinconf_ops stm32_pconf_ops = { @@ -1358,6 +1528,7 @@ static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl, struct fwnode bank->bank_nr = bank_nr; bank->bank_ioport_nr = bank_ioport_nr; bank->secure_control = pctl->match_data->secure_control; + bank->io_sync_control = pctl->match_data->io_sync_control; spin_lock_init(&bank->lock); if (pctl->domain) { @@ -1604,6 +1775,8 @@ int stm32_pctl_probe(struct platform_device *pdev) pctl->pctl_desc.confops = &stm32_pconf_ops; pctl->pctl_desc.pctlops = &stm32_pctrl_ops; pctl->pctl_desc.pmxops = &stm32_pmx_ops; + pctl->pctl_desc.num_custom_params = ARRAY_SIZE(stm32_gpio_bindings); + pctl->pctl_desc.custom_params = stm32_gpio_bindings; pctl->dev = &pdev->dev; pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc, @@ -1663,6 +1836,16 @@ int stm32_pctl_probe(struct platform_device *pdev) return 0; } +static int __maybe_unused stm32_pinctrl_restore_advcfgr(struct stm32_gpio_bank *bank, + int offset, u32 bpos) +{ + u32 val; + + val = bank->pin_backup[offset] & BIT(STM32_GPIO_BKP_ADVCFG_SHIFT + bpos); + val >>= (STM32_GPIO_BKP_ADVCFG_SHIFT + bpos); + return stm32_pconf_set_advcfgr(bank, offset, bpos, val); +} + static int __maybe_unused stm32_pinctrl_restore_gpio_regs( struct stm32_pinctrl *pctl, u32 pin) { @@ -1720,6 +1903,30 @@ static int __maybe_unused stm32_pinctrl_restore_gpio_regs( if (ret) return ret; + if (bank->io_sync_control) { + ret = stm32_pinctrl_restore_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_DLYPATH_POS); + if (ret) + return ret; + + ret = stm32_pinctrl_restore_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_DE_POS); + if (ret) + return ret; + + ret = stm32_pinctrl_restore_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_INVCLK_POS); + if (ret) + return ret; + + ret = stm32_pinctrl_restore_advcfgr(bank, offset, STM32_GPIO_ADVCFGR_RET_POS); + if (ret) + return ret; + + val = bank->pin_backup[offset] & STM32_GPIO_BKP_DELAY_MASK; + val >>= STM32_GPIO_BKP_DELAY_SHIFT; + ret = stm32_pconf_set_delay(bank, offset, val); + if (ret) + return ret; + } + if (pin_is_irq) regmap_field_write(pctl->irqmux[offset], bank->bank_ioport_nr); diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.h b/drivers/pinctrl/stm32/pinctrl-stm32.h index a5f62fb271442..9b319036f206d 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.h +++ b/drivers/pinctrl/stm32/pinctrl-stm32.h @@ -64,6 +64,7 @@ struct stm32_pinctrl_match_data { const struct stm32_desc_pin *pins; const unsigned int npins; bool secure_control; + bool io_sync_control; }; int stm32_pctl_probe(struct platform_device *pdev); diff --git a/drivers/pinctrl/stm32/pinctrl-stm32mp257.c b/drivers/pinctrl/stm32/pinctrl-stm32mp257.c index 23aebd4695e99..293b7acd82a3e 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32mp257.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32mp257.c @@ -2542,11 +2542,13 @@ static const struct stm32_desc_pin stm32mp257_z_pins[] = { static struct stm32_pinctrl_match_data stm32mp257_match_data = { .pins = stm32mp257_pins, .npins = ARRAY_SIZE(stm32mp257_pins), + .io_sync_control = true, }; static struct stm32_pinctrl_match_data stm32mp257_z_match_data = { .pins = stm32mp257_z_pins, .npins = ARRAY_SIZE(stm32mp257_z_pins), + .io_sync_control = true, }; static const struct of_device_id stm32mp257_pctrl_match[] = { -- 2.34.1