| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Driver for the NVIDIA Tegra pinmux |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * Copyright (C) 2010 Google, Inc. |
|---|
| 8 | 9 | * Copyright (C) 2010 NVIDIA Corporation |
|---|
| 9 | 10 | * Copyright (C) 2009-2011 ST-Ericsson AB |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 12 | | - * under the terms and conditions of the GNU General Public License, |
|---|
| 13 | | - * version 2, as published by the Free Software Foundation. |
|---|
| 14 | | - * |
|---|
| 15 | | - * This program is distributed in the hope it will be useful, but WITHOUT |
|---|
| 16 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 17 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 18 | | - * more details. |
|---|
| 19 | 11 | */ |
|---|
| 20 | 12 | |
|---|
| 21 | 13 | #include <linux/err.h> |
|---|
| .. | .. |
|---|
| 283 | 275 | return 0; |
|---|
| 284 | 276 | } |
|---|
| 285 | 277 | |
|---|
| 278 | +static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, |
|---|
| 279 | + struct pinctrl_gpio_range *range, |
|---|
| 280 | + unsigned int offset) |
|---|
| 281 | +{ |
|---|
| 282 | + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); |
|---|
| 283 | + const struct tegra_pingroup *group; |
|---|
| 284 | + u32 value; |
|---|
| 285 | + |
|---|
| 286 | + if (!pmx->soc->sfsel_in_mux) |
|---|
| 287 | + return 0; |
|---|
| 288 | + |
|---|
| 289 | + group = &pmx->soc->groups[offset]; |
|---|
| 290 | + |
|---|
| 291 | + if (group->mux_reg < 0 || group->sfsel_bit < 0) |
|---|
| 292 | + return -EINVAL; |
|---|
| 293 | + |
|---|
| 294 | + value = pmx_readl(pmx, group->mux_bank, group->mux_reg); |
|---|
| 295 | + value &= ~BIT(group->sfsel_bit); |
|---|
| 296 | + pmx_writel(pmx, value, group->mux_bank, group->mux_reg); |
|---|
| 297 | + |
|---|
| 298 | + return 0; |
|---|
| 299 | +} |
|---|
| 300 | + |
|---|
| 301 | +static void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, |
|---|
| 302 | + struct pinctrl_gpio_range *range, |
|---|
| 303 | + unsigned int offset) |
|---|
| 304 | +{ |
|---|
| 305 | + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); |
|---|
| 306 | + const struct tegra_pingroup *group; |
|---|
| 307 | + u32 value; |
|---|
| 308 | + |
|---|
| 309 | + if (!pmx->soc->sfsel_in_mux) |
|---|
| 310 | + return; |
|---|
| 311 | + |
|---|
| 312 | + group = &pmx->soc->groups[offset]; |
|---|
| 313 | + |
|---|
| 314 | + if (group->mux_reg < 0 || group->sfsel_bit < 0) |
|---|
| 315 | + return; |
|---|
| 316 | + |
|---|
| 317 | + value = pmx_readl(pmx, group->mux_bank, group->mux_reg); |
|---|
| 318 | + value |= BIT(group->sfsel_bit); |
|---|
| 319 | + pmx_writel(pmx, value, group->mux_bank, group->mux_reg); |
|---|
| 320 | +} |
|---|
| 321 | + |
|---|
| 286 | 322 | static const struct pinmux_ops tegra_pinmux_ops = { |
|---|
| 287 | 323 | .get_functions_count = tegra_pinctrl_get_funcs_count, |
|---|
| 288 | 324 | .get_function_name = tegra_pinctrl_get_func_name, |
|---|
| 289 | 325 | .get_function_groups = tegra_pinctrl_get_func_groups, |
|---|
| 290 | 326 | .set_mux = tegra_pinctrl_set_mux, |
|---|
| 327 | + .gpio_request_enable = tegra_pinctrl_gpio_request_enable, |
|---|
| 328 | + .gpio_disable_free = tegra_pinctrl_gpio_disable_free, |
|---|
| 291 | 329 | }; |
|---|
| 292 | 330 | |
|---|
| 293 | 331 | static int tegra_pinconf_reg(struct tegra_pmx *pmx, |
|---|
| 294 | 332 | const struct tegra_pingroup *g, |
|---|
| 295 | 333 | enum tegra_pinconf_param param, |
|---|
| 296 | 334 | bool report_err, |
|---|
| 297 | | - s8 *bank, s16 *reg, s8 *bit, s8 *width) |
|---|
| 335 | + s8 *bank, s32 *reg, s8 *bit, s8 *width) |
|---|
| 298 | 336 | { |
|---|
| 299 | 337 | switch (param) { |
|---|
| 300 | 338 | case TEGRA_PINCONF_PARAM_PULL: |
|---|
| .. | .. |
|---|
| 453 | 491 | const struct tegra_pingroup *g; |
|---|
| 454 | 492 | int ret; |
|---|
| 455 | 493 | s8 bank, bit, width; |
|---|
| 456 | | - s16 reg; |
|---|
| 494 | + s32 reg; |
|---|
| 457 | 495 | u32 val, mask; |
|---|
| 458 | 496 | |
|---|
| 459 | 497 | g = &pmx->soc->groups[group]; |
|---|
| .. | .. |
|---|
| 482 | 520 | const struct tegra_pingroup *g; |
|---|
| 483 | 521 | int ret, i; |
|---|
| 484 | 522 | s8 bank, bit, width; |
|---|
| 485 | | - s16 reg; |
|---|
| 523 | + s32 reg; |
|---|
| 486 | 524 | u32 val, mask; |
|---|
| 487 | 525 | |
|---|
| 488 | 526 | g = &pmx->soc->groups[group]; |
|---|
| .. | .. |
|---|
| 550 | 588 | const struct tegra_pingroup *g; |
|---|
| 551 | 589 | int i, ret; |
|---|
| 552 | 590 | s8 bank, bit, width; |
|---|
| 553 | | - s16 reg; |
|---|
| 591 | + s32 reg; |
|---|
| 554 | 592 | u32 val; |
|---|
| 555 | 593 | |
|---|
| 556 | 594 | g = &pmx->soc->groups[group]; |
|---|
| .. | .. |
|---|
| 623 | 661 | |
|---|
| 624 | 662 | for (i = 0; i < pmx->soc->ngroups; ++i) { |
|---|
| 625 | 663 | g = &pmx->soc->groups[i]; |
|---|
| 626 | | - if (g->parked_bit >= 0) { |
|---|
| 627 | | - val = pmx_readl(pmx, g->mux_bank, g->mux_reg); |
|---|
| 628 | | - val &= ~(1 << g->parked_bit); |
|---|
| 629 | | - pmx_writel(pmx, val, g->mux_bank, g->mux_reg); |
|---|
| 664 | + if (g->parked_bitmask > 0) { |
|---|
| 665 | + unsigned int bank, reg; |
|---|
| 666 | + |
|---|
| 667 | + if (g->mux_reg != -1) { |
|---|
| 668 | + bank = g->mux_bank; |
|---|
| 669 | + reg = g->mux_reg; |
|---|
| 670 | + } else { |
|---|
| 671 | + bank = g->drv_bank; |
|---|
| 672 | + reg = g->drv_reg; |
|---|
| 673 | + } |
|---|
| 674 | + |
|---|
| 675 | + val = pmx_readl(pmx, bank, reg); |
|---|
| 676 | + val &= ~g->parked_bitmask; |
|---|
| 677 | + pmx_writel(pmx, val, bank, reg); |
|---|
| 630 | 678 | } |
|---|
| 631 | 679 | } |
|---|
| 632 | 680 | } |
|---|
| 633 | 681 | |
|---|
| 634 | | -static bool gpio_node_has_range(const char *compatible) |
|---|
| 682 | +static size_t tegra_pinctrl_get_bank_size(struct device *dev, |
|---|
| 683 | + unsigned int bank_id) |
|---|
| 684 | +{ |
|---|
| 685 | + struct platform_device *pdev = to_platform_device(dev); |
|---|
| 686 | + struct resource *res; |
|---|
| 687 | + |
|---|
| 688 | + res = platform_get_resource(pdev, IORESOURCE_MEM, bank_id); |
|---|
| 689 | + |
|---|
| 690 | + return resource_size(res) / 4; |
|---|
| 691 | +} |
|---|
| 692 | + |
|---|
| 693 | +static int tegra_pinctrl_suspend(struct device *dev) |
|---|
| 694 | +{ |
|---|
| 695 | + struct tegra_pmx *pmx = dev_get_drvdata(dev); |
|---|
| 696 | + u32 *backup_regs = pmx->backup_regs; |
|---|
| 697 | + u32 __iomem *regs; |
|---|
| 698 | + size_t bank_size; |
|---|
| 699 | + unsigned int i, k; |
|---|
| 700 | + |
|---|
| 701 | + for (i = 0; i < pmx->nbanks; i++) { |
|---|
| 702 | + bank_size = tegra_pinctrl_get_bank_size(dev, i); |
|---|
| 703 | + regs = pmx->regs[i]; |
|---|
| 704 | + for (k = 0; k < bank_size; k++) |
|---|
| 705 | + *backup_regs++ = readl_relaxed(regs++); |
|---|
| 706 | + } |
|---|
| 707 | + |
|---|
| 708 | + return pinctrl_force_sleep(pmx->pctl); |
|---|
| 709 | +} |
|---|
| 710 | + |
|---|
| 711 | +static int tegra_pinctrl_resume(struct device *dev) |
|---|
| 712 | +{ |
|---|
| 713 | + struct tegra_pmx *pmx = dev_get_drvdata(dev); |
|---|
| 714 | + u32 *backup_regs = pmx->backup_regs; |
|---|
| 715 | + u32 __iomem *regs; |
|---|
| 716 | + size_t bank_size; |
|---|
| 717 | + unsigned int i, k; |
|---|
| 718 | + |
|---|
| 719 | + for (i = 0; i < pmx->nbanks; i++) { |
|---|
| 720 | + bank_size = tegra_pinctrl_get_bank_size(dev, i); |
|---|
| 721 | + regs = pmx->regs[i]; |
|---|
| 722 | + for (k = 0; k < bank_size; k++) |
|---|
| 723 | + writel_relaxed(*backup_regs++, regs++); |
|---|
| 724 | + } |
|---|
| 725 | + |
|---|
| 726 | + /* flush all the prior writes */ |
|---|
| 727 | + readl_relaxed(pmx->regs[0]); |
|---|
| 728 | + /* wait for pinctrl register read to complete */ |
|---|
| 729 | + rmb(); |
|---|
| 730 | + return 0; |
|---|
| 731 | +} |
|---|
| 732 | + |
|---|
| 733 | +const struct dev_pm_ops tegra_pinctrl_pm = { |
|---|
| 734 | + .suspend_noirq = &tegra_pinctrl_suspend, |
|---|
| 735 | + .resume_noirq = &tegra_pinctrl_resume |
|---|
| 736 | +}; |
|---|
| 737 | + |
|---|
| 738 | +static bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) |
|---|
| 635 | 739 | { |
|---|
| 636 | 740 | struct device_node *np; |
|---|
| 637 | 741 | bool has_prop = false; |
|---|
| 638 | 742 | |
|---|
| 639 | | - np = of_find_compatible_node(NULL, NULL, compatible); |
|---|
| 743 | + np = of_find_compatible_node(NULL, NULL, pmx->soc->gpio_compatible); |
|---|
| 640 | 744 | if (!np) |
|---|
| 641 | 745 | return has_prop; |
|---|
| 642 | 746 | |
|---|
| .. | .. |
|---|
| 655 | 759 | int i; |
|---|
| 656 | 760 | const char **group_pins; |
|---|
| 657 | 761 | int fn, gn, gfn; |
|---|
| 762 | + unsigned long backup_regs_size = 0; |
|---|
| 658 | 763 | |
|---|
| 659 | 764 | pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); |
|---|
| 660 | 765 | if (!pmx) |
|---|
| .. | .. |
|---|
| 707 | 812 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); |
|---|
| 708 | 813 | if (!res) |
|---|
| 709 | 814 | break; |
|---|
| 815 | + backup_regs_size += resource_size(res); |
|---|
| 710 | 816 | } |
|---|
| 711 | 817 | pmx->nbanks = i; |
|---|
| 712 | 818 | |
|---|
| .. | .. |
|---|
| 715 | 821 | if (!pmx->regs) |
|---|
| 716 | 822 | return -ENOMEM; |
|---|
| 717 | 823 | |
|---|
| 824 | + pmx->backup_regs = devm_kzalloc(&pdev->dev, backup_regs_size, |
|---|
| 825 | + GFP_KERNEL); |
|---|
| 826 | + if (!pmx->backup_regs) |
|---|
| 827 | + return -ENOMEM; |
|---|
| 828 | + |
|---|
| 718 | 829 | for (i = 0; i < pmx->nbanks; i++) { |
|---|
| 719 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, i); |
|---|
| 720 | | - pmx->regs[i] = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 830 | + pmx->regs[i] = devm_platform_ioremap_resource(pdev, i); |
|---|
| 721 | 831 | if (IS_ERR(pmx->regs[i])) |
|---|
| 722 | 832 | return PTR_ERR(pmx->regs[i]); |
|---|
| 723 | 833 | } |
|---|
| .. | .. |
|---|
| 730 | 840 | |
|---|
| 731 | 841 | tegra_pinctrl_clear_parked_bits(pmx); |
|---|
| 732 | 842 | |
|---|
| 733 | | - if (!gpio_node_has_range(pmx->soc->gpio_compatible)) |
|---|
| 843 | + if (pmx->soc->ngpios > 0 && !tegra_pinctrl_gpio_node_has_range(pmx)) |
|---|
| 734 | 844 | pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); |
|---|
| 735 | 845 | |
|---|
| 736 | 846 | platform_set_drvdata(pdev, pmx); |
|---|
| .. | .. |
|---|
| 739 | 849 | |
|---|
| 740 | 850 | return 0; |
|---|
| 741 | 851 | } |
|---|
| 742 | | -EXPORT_SYMBOL_GPL(tegra_pinctrl_probe); |
|---|