Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Prathamesh Shete and committed by
Linus Walleij
c12bfa0f 4fd41e74

+57 -8
+51 -8
drivers/pinctrl/tegra/pinctrl-tegra.c
··· 276 276 return 0; 277 277 } 278 278 279 - static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev *pctldev, 280 - unsigned int offset) 279 + static int tegra_pinctrl_get_group_index(struct pinctrl_dev *pctldev, 280 + unsigned int offset) 281 281 { 282 282 struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 283 283 unsigned int group, num_pins, j; ··· 290 290 continue; 291 291 for (j = 0; j < num_pins; j++) { 292 292 if (offset == pins[j]) 293 - return &pmx->soc->groups[group]; 293 + return group; 294 294 } 295 295 } 296 296 297 - dev_err(pctldev->dev, "Pingroup not found for pin %u\n", offset); 298 - return NULL; 297 + return -EINVAL; 298 + } 299 + 300 + static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev *pctldev, 301 + unsigned int offset, 302 + int group_index) 303 + { 304 + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 305 + 306 + if (group_index < 0 || group_index > pmx->soc->ngroups) 307 + return NULL; 308 + 309 + return &pmx->soc->groups[group_index]; 310 + } 311 + 312 + static struct tegra_pingroup_config *tegra_pinctrl_get_group_config(struct pinctrl_dev *pctldev, 313 + unsigned int offset, 314 + int group_index) 315 + { 316 + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 317 + 318 + if (group_index < 0) 319 + return NULL; 320 + 321 + return &pmx->pingroup_configs[group_index]; 299 322 } 300 323 301 324 static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, ··· 327 304 { 328 305 struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 329 306 const struct tegra_pingroup *group; 307 + struct tegra_pingroup_config *config; 308 + int group_index; 330 309 u32 value; 331 310 332 311 if (!pmx->soc->sfsel_in_mux) 333 312 return 0; 334 313 335 - group = tegra_pinctrl_get_group(pctldev, offset); 314 + group_index = tegra_pinctrl_get_group_index(pctldev, offset); 315 + group = tegra_pinctrl_get_group(pctldev, offset, group_index); 336 316 337 317 if (!group) 338 318 return -EINVAL; ··· 343 317 if (group->mux_reg < 0 || group->sfsel_bit < 0) 344 318 return -EINVAL; 345 319 320 + config = tegra_pinctrl_get_group_config(pctldev, offset, group_index); 321 + if (!config) 322 + return -EINVAL; 346 323 value = pmx_readl(pmx, group->mux_bank, group->mux_reg); 324 + config->is_sfsel = (value & BIT(group->sfsel_bit)) != 0; 347 325 value &= ~BIT(group->sfsel_bit); 348 326 pmx_writel(pmx, value, group->mux_bank, group->mux_reg); 349 327 ··· 360 330 { 361 331 struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 362 332 const struct tegra_pingroup *group; 333 + struct tegra_pingroup_config *config; 334 + int group_index; 363 335 u32 value; 364 336 365 337 if (!pmx->soc->sfsel_in_mux) 366 338 return; 367 339 368 - group = tegra_pinctrl_get_group(pctldev, offset); 340 + group_index = tegra_pinctrl_get_group_index(pctldev, offset); 341 + group = tegra_pinctrl_get_group(pctldev, offset, group_index); 369 342 370 343 if (!group) 371 344 return; ··· 376 343 if (group->mux_reg < 0 || group->sfsel_bit < 0) 377 344 return; 378 345 346 + config = tegra_pinctrl_get_group_config(pctldev, offset, group_index); 347 + if (!config) 348 + return; 379 349 value = pmx_readl(pmx, group->mux_bank, group->mux_reg); 380 - value |= BIT(group->sfsel_bit); 350 + if (config->is_sfsel) 351 + value |= BIT(group->sfsel_bit); 381 352 pmx_writel(pmx, value, group->mux_bank, group->mux_reg); 382 353 } 383 354 ··· 835 798 836 799 pmx->dev = &pdev->dev; 837 800 pmx->soc = soc_data; 801 + 802 + pmx->pingroup_configs = devm_kcalloc(&pdev->dev, 803 + pmx->soc->ngroups, sizeof(*pmx->pingroup_configs), 804 + GFP_KERNEL); 805 + if (!pmx->pingroup_configs) 806 + return -ENOMEM; 838 807 839 808 /* 840 809 * Each mux group will appear in 4 functions' list of groups.
+6
drivers/pinctrl/tegra/pinctrl-tegra.h
··· 8 8 #ifndef __PINMUX_TEGRA_H__ 9 9 #define __PINMUX_TEGRA_H__ 10 10 11 + struct tegra_pingroup_config { 12 + bool is_sfsel; 13 + }; 14 + 11 15 struct tegra_pmx { 12 16 struct device *dev; 13 17 struct pinctrl_dev *pctl; ··· 25 21 int nbanks; 26 22 void __iomem **regs; 27 23 u32 *backup_regs; 24 + /* Array of size soc->ngroups */ 25 + struct tegra_pingroup_config *pingroup_configs; 28 26 }; 29 27 30 28 enum tegra_pinconf_param {