.. | .. |
---|
| 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); |
---|