]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
pinctrl-tegra: Restore SFSEL bit when freeing pins
authorPrathamesh Shete <pshete@nvidia.com>
Wed, 5 Mar 2025 10:49:39 +0000 (16:19 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Jun 2025 12:40:06 +0000 (14:40 +0200)
[ Upstream commit c12bfa0fee65940b10ff5187349f76c6f6b1df9c ]

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: Prathamesh Shete <pshete@nvidia.com>
Link: https://lore.kernel.org/20250305104939.15168-2-pshete@nvidia.com
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/pinctrl/tegra/pinctrl-tegra.c
drivers/pinctrl/tegra/pinctrl-tegra.h

index 30341c43da59aa75678e9834076c4907fccb5590..ba7bcc876e30473b34889c2bd6a33dea5cd18355 100644 (file)
@@ -278,8 +278,8 @@ 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,
-                                       unsigned int offset)
+static int tegra_pinctrl_get_group_index(struct pinctrl_dev *pctldev,
+                                        unsigned int offset)
 {
        struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
        unsigned int group, num_pins, j;
@@ -292,12 +292,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;
                }
        }
 
-       dev_err(pctldev->dev, "Pingroup not found for pin %u\n", offset);
-       return NULL;
+       return -EINVAL;
+}
+
+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,
@@ -306,12 +329,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;
@@ -319,7 +345,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);
 
@@ -332,12 +362,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;
@@ -345,8 +378,12 @@ 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);
-       value |= BIT(group->sfsel_bit);
+       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.
index f8269858eb78a814e3d62943e9d8001f778bd719..ec5198d391ea5485477cab06bfef81ef9e81b4a3 100644 (file)
@@ -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;
@@ -18,6 +22,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 {