Commit c12bfa0f authored by Prathamesh Shete's avatar Prathamesh Shete Committed by Linus Walleij
Browse files

pinctrl-tegra: Restore SFSEL bit when freeing pins



Each pin can be configured as a Special Function IO (SFIO) or GPIO,
where the SFIO enables the pin to operate in alternative modes such as
I2C, SPI, etc.

The current implementation sets all the pins back to SFIO mode
even if they were initially in GPIO mode. This can cause glitches
on the pins when pinctrl_gpio_free() is called.

Avoid these undesired glitches by storing the pin's SFIO/GPIO
state on GPIO request and restoring it on GPIO free.

Signed-off-by: default avatarPrathamesh Shete <pshete@nvidia.com>
Link: https://lore.kernel.org/20250305104939.15168-2-pshete@nvidia.com


Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 4fd41e74
Loading
Loading
Loading
Loading
+51 −8
Original line number Diff line number Diff line
@@ -276,7 +276,7 @@ static int tegra_pinctrl_set_mux(struct pinctrl_dev *pctldev,
	return 0;
}

static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev *pctldev,
static int tegra_pinctrl_get_group_index(struct pinctrl_dev *pctldev,
					 unsigned int offset)
{
	struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
@@ -290,12 +290,35 @@ static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev *
			continue;
		for (j = 0; j < num_pins; j++) {
			if (offset == pins[j])
				return &pmx->soc->groups[group];
				return group;
		}
	}

	return -EINVAL;
}

	dev_err(pctldev->dev, "Pingroup not found for pin %u\n", offset);
static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev *pctldev,
							    unsigned int offset,
							    int group_index)
{
	struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);

	if (group_index < 0 || group_index > pmx->soc->ngroups)
		return NULL;

	return &pmx->soc->groups[group_index];
}

static struct tegra_pingroup_config *tegra_pinctrl_get_group_config(struct pinctrl_dev *pctldev,
								    unsigned int offset,
								    int group_index)
{
	struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);

	if (group_index < 0)
		return NULL;

	return &pmx->pingroup_configs[group_index];
}

static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
@@ -304,12 +327,15 @@ static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
{
	struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
	const struct tegra_pingroup *group;
	struct tegra_pingroup_config *config;
	int group_index;
	u32 value;

	if (!pmx->soc->sfsel_in_mux)
		return 0;

	group = tegra_pinctrl_get_group(pctldev, offset);
	group_index = tegra_pinctrl_get_group_index(pctldev, offset);
	group = tegra_pinctrl_get_group(pctldev, offset, group_index);

	if (!group)
		return -EINVAL;
@@ -317,7 +343,11 @@ static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
	if (group->mux_reg < 0 || group->sfsel_bit < 0)
		return -EINVAL;

	config = tegra_pinctrl_get_group_config(pctldev, offset, group_index);
	if (!config)
		return -EINVAL;
	value = pmx_readl(pmx, group->mux_bank, group->mux_reg);
	config->is_sfsel = (value & BIT(group->sfsel_bit)) != 0;
	value &= ~BIT(group->sfsel_bit);
	pmx_writel(pmx, value, group->mux_bank, group->mux_reg);

@@ -330,12 +360,15 @@ static void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev,
{
	struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
	const struct tegra_pingroup *group;
	struct tegra_pingroup_config *config;
	int group_index;
	u32 value;

	if (!pmx->soc->sfsel_in_mux)
		return;

	group = tegra_pinctrl_get_group(pctldev, offset);
	group_index = tegra_pinctrl_get_group_index(pctldev, offset);
	group = tegra_pinctrl_get_group(pctldev, offset, group_index);

	if (!group)
		return;
@@ -343,7 +376,11 @@ static void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev,
	if (group->mux_reg < 0 || group->sfsel_bit < 0)
		return;

	config = tegra_pinctrl_get_group_config(pctldev, offset, group_index);
	if (!config)
		return;
	value = pmx_readl(pmx, group->mux_bank, group->mux_reg);
	if (config->is_sfsel)
		value |= BIT(group->sfsel_bit);
	pmx_writel(pmx, value, group->mux_bank, group->mux_reg);
}
@@ -799,6 +836,12 @@ int tegra_pinctrl_probe(struct platform_device *pdev,
	pmx->dev = &pdev->dev;
	pmx->soc = soc_data;

	pmx->pingroup_configs = devm_kcalloc(&pdev->dev,
					     pmx->soc->ngroups, sizeof(*pmx->pingroup_configs),
					     GFP_KERNEL);
	if (!pmx->pingroup_configs)
		return -ENOMEM;

	/*
	 * Each mux group will appear in 4 functions' list of groups.
	 * This over-allocates slightly, since not all groups are mux groups.
+6 −0
Original line number Diff line number Diff line
@@ -8,6 +8,10 @@
#ifndef __PINMUX_TEGRA_H__
#define __PINMUX_TEGRA_H__

struct tegra_pingroup_config {
	bool is_sfsel;
};

struct tegra_pmx {
	struct device *dev;
	struct pinctrl_dev *pctl;
@@ -21,6 +25,8 @@ struct tegra_pmx {
	int nbanks;
	void __iomem **regs;
	u32 *backup_regs;
	/* Array of size soc->ngroups */
	struct tegra_pingroup_config *pingroup_configs;
};

enum tegra_pinconf_param {