diff mbox series

[06/14] pinctrl: stm32: Support IO synchronization parameters

Message ID 20241022155658.1647350-7-antonio.borneo@foss.st.com (mailing list archive)
State New
Headers show
Series pinctrl: stm32: Add new features and support for more SoC | expand

Commit Message

Antonio Borneo Oct. 22, 2024, 3:56 p.m. UTC
From: Fabien Dessenne <fabien.dessenne@foss.st.com>

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@foss.st.com>
Signed-off-by: Valentin Caron <valentin.caron@foss.st.com>
Signed-off-by: Fabien Dessenne <fabien.dessenne@foss.st.com>
Signed-off-by: Antonio Borneo <antonio.borneo@foss.st.com>
---
 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 mbox series

Patch

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[] = {