| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * GPIO driver for AMD |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * Authors: Ken Xue <Ken.Xue@amd.com> |
|---|
| 6 | 7 | * Wu, Jeff <Jeff.Wu@amd.com> |
|---|
| 7 | 8 | * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 9 | | - * under the terms and conditions of the GNU General Public License, |
|---|
| 10 | | - * version 2, as published by the Free Software Foundation. |
|---|
| 11 | | - * |
|---|
| 12 | 9 | * Contact Information: Nehal Shah <Nehal-bakulchandra.Shah@amd.com> |
|---|
| 13 | 10 | * Shyam Sundar S K <Shyam-sundar.S-k@amd.com> |
|---|
| 14 | | - * |
|---|
| 15 | 11 | */ |
|---|
| 16 | 12 | |
|---|
| 17 | 13 | #include <linux/err.h> |
|---|
| .. | .. |
|---|
| 24 | 20 | #include <linux/errno.h> |
|---|
| 25 | 21 | #include <linux/log2.h> |
|---|
| 26 | 22 | #include <linux/io.h> |
|---|
| 27 | | -#include <linux/gpio.h> |
|---|
| 23 | +#include <linux/gpio/driver.h> |
|---|
| 28 | 24 | #include <linux/slab.h> |
|---|
| 29 | 25 | #include <linux/platform_device.h> |
|---|
| 30 | 26 | #include <linux/mutex.h> |
|---|
| .. | .. |
|---|
| 50 | 46 | pin_reg = readl(gpio_dev->base + offset * 4); |
|---|
| 51 | 47 | raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); |
|---|
| 52 | 48 | |
|---|
| 53 | | - return !(pin_reg & BIT(OUTPUT_ENABLE_OFF)); |
|---|
| 49 | + if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) |
|---|
| 50 | + return GPIO_LINE_DIRECTION_OUT; |
|---|
| 51 | + |
|---|
| 52 | + return GPIO_LINE_DIRECTION_IN; |
|---|
| 54 | 53 | } |
|---|
| 55 | 54 | |
|---|
| 56 | 55 | static int amd_gpio_direction_input(struct gpio_chip *gc, unsigned offset) |
|---|
| .. | .. |
|---|
| 127 | 126 | struct amd_gpio *gpio_dev = gpiochip_get_data(gc); |
|---|
| 128 | 127 | |
|---|
| 129 | 128 | raw_spin_lock_irqsave(&gpio_dev->lock, flags); |
|---|
| 129 | + |
|---|
| 130 | + /* Use special handling for Pin0 debounce */ |
|---|
| 131 | + if (offset == 0) { |
|---|
| 132 | + pin_reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); |
|---|
| 133 | + if (pin_reg & INTERNAL_GPIO0_DEBOUNCE) |
|---|
| 134 | + debounce = 0; |
|---|
| 135 | + } |
|---|
| 136 | + |
|---|
| 130 | 137 | pin_reg = readl(gpio_dev->base + offset * 4); |
|---|
| 131 | 138 | |
|---|
| 132 | 139 | if (debounce) { |
|---|
| .. | .. |
|---|
| 182 | 189 | return ret; |
|---|
| 183 | 190 | } |
|---|
| 184 | 191 | |
|---|
| 185 | | -static int amd_gpio_set_config(struct gpio_chip *gc, unsigned offset, |
|---|
| 186 | | - unsigned long config) |
|---|
| 187 | | -{ |
|---|
| 188 | | - u32 debounce; |
|---|
| 189 | | - |
|---|
| 190 | | - if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) |
|---|
| 191 | | - return -ENOTSUPP; |
|---|
| 192 | | - |
|---|
| 193 | | - debounce = pinconf_to_config_argument(config); |
|---|
| 194 | | - return amd_gpio_set_debounce(gc, offset, debounce); |
|---|
| 195 | | -} |
|---|
| 196 | | - |
|---|
| 197 | 192 | #ifdef CONFIG_DEBUG_FS |
|---|
| 198 | 193 | static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) |
|---|
| 199 | 194 | { |
|---|
| .. | .. |
|---|
| 216 | 211 | char *output_value; |
|---|
| 217 | 212 | char *output_enable; |
|---|
| 218 | 213 | |
|---|
| 214 | + seq_printf(s, "WAKE_INT_MASTER_REG: 0x%08x\n", readl(gpio_dev->base + WAKE_INT_MASTER_REG)); |
|---|
| 219 | 215 | for (bank = 0; bank < gpio_dev->hwbank_num; bank++) { |
|---|
| 220 | 216 | seq_printf(s, "GPIO bank%d\t", bank); |
|---|
| 221 | 217 | |
|---|
| .. | .. |
|---|
| 418 | 414 | { |
|---|
| 419 | 415 | int ret = 0; |
|---|
| 420 | 416 | u32 pin_reg, pin_reg_irq_en, mask; |
|---|
| 421 | | - unsigned long flags, irq_flags; |
|---|
| 417 | + unsigned long flags; |
|---|
| 422 | 418 | struct gpio_chip *gc = irq_data_get_irq_chip_data(d); |
|---|
| 423 | 419 | struct amd_gpio *gpio_dev = gpiochip_get_data(gc); |
|---|
| 424 | 420 | |
|---|
| 425 | 421 | raw_spin_lock_irqsave(&gpio_dev->lock, flags); |
|---|
| 426 | 422 | pin_reg = readl(gpio_dev->base + (d->hwirq)*4); |
|---|
| 427 | | - |
|---|
| 428 | | - /* Ignore the settings coming from the client and |
|---|
| 429 | | - * read the values from the ACPI tables |
|---|
| 430 | | - * while setting the trigger type |
|---|
| 431 | | - */ |
|---|
| 432 | | - |
|---|
| 433 | | - irq_flags = irq_get_trigger_type(d->irq); |
|---|
| 434 | | - if (irq_flags != IRQ_TYPE_NONE) |
|---|
| 435 | | - type = irq_flags; |
|---|
| 436 | 423 | |
|---|
| 437 | 424 | switch (type & IRQ_TYPE_SENSE_MASK) { |
|---|
| 438 | 425 | case IRQ_TYPE_EDGE_RISING: |
|---|
| .. | .. |
|---|
| 482 | 469 | /* |
|---|
| 483 | 470 | * If WAKE_INT_MASTER_REG.MaskStsEn is set, a software write to the |
|---|
| 484 | 471 | * debounce registers of any GPIO will block wake/interrupt status |
|---|
| 485 | | - * generation for *all* GPIOs for a lenght of time that depends on |
|---|
| 472 | + * generation for *all* GPIOs for a length of time that depends on |
|---|
| 486 | 473 | * WAKE_INT_MASTER_REG.MaskStsLength[11:0]. During this period the |
|---|
| 487 | 474 | * INTERRUPT_ENABLE bit will read as 0. |
|---|
| 488 | 475 | * |
|---|
| .. | .. |
|---|
| 666 | 653 | break; |
|---|
| 667 | 654 | |
|---|
| 668 | 655 | default: |
|---|
| 669 | | - dev_err(&gpio_dev->pdev->dev, "Invalid config param %04x\n", |
|---|
| 656 | + dev_dbg(&gpio_dev->pdev->dev, "Invalid config param %04x\n", |
|---|
| 670 | 657 | param); |
|---|
| 671 | 658 | return -ENOTSUPP; |
|---|
| 672 | 659 | } |
|---|
| .. | .. |
|---|
| 677 | 664 | } |
|---|
| 678 | 665 | |
|---|
| 679 | 666 | static int amd_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, |
|---|
| 680 | | - unsigned long *configs, unsigned num_configs) |
|---|
| 667 | + unsigned long *configs, unsigned int num_configs) |
|---|
| 681 | 668 | { |
|---|
| 682 | 669 | int i; |
|---|
| 683 | 670 | u32 arg; |
|---|
| .. | .. |
|---|
| 719 | 706 | break; |
|---|
| 720 | 707 | |
|---|
| 721 | 708 | default: |
|---|
| 722 | | - dev_err(&gpio_dev->pdev->dev, |
|---|
| 709 | + dev_dbg(&gpio_dev->pdev->dev, |
|---|
| 723 | 710 | "Invalid config param %04x\n", param); |
|---|
| 724 | 711 | ret = -ENOTSUPP; |
|---|
| 725 | 712 | } |
|---|
| .. | .. |
|---|
| 767 | 754 | return 0; |
|---|
| 768 | 755 | } |
|---|
| 769 | 756 | |
|---|
| 757 | +static int amd_gpio_set_config(struct gpio_chip *gc, unsigned int pin, |
|---|
| 758 | + unsigned long config) |
|---|
| 759 | +{ |
|---|
| 760 | + struct amd_gpio *gpio_dev = gpiochip_get_data(gc); |
|---|
| 761 | + |
|---|
| 762 | + if (pinconf_to_config_param(config) == PIN_CONFIG_INPUT_DEBOUNCE) { |
|---|
| 763 | + u32 debounce = pinconf_to_config_argument(config); |
|---|
| 764 | + |
|---|
| 765 | + return amd_gpio_set_debounce(gc, pin, debounce); |
|---|
| 766 | + } |
|---|
| 767 | + |
|---|
| 768 | + return amd_pinconf_set(gpio_dev->pctrl, pin, &config, 1); |
|---|
| 769 | +} |
|---|
| 770 | + |
|---|
| 770 | 771 | static const struct pinconf_ops amd_pinconf_ops = { |
|---|
| 771 | 772 | .pin_config_get = amd_pinconf_get, |
|---|
| 772 | 773 | .pin_config_set = amd_pinconf_set, |
|---|
| 773 | 774 | .pin_config_group_get = amd_pinconf_group_get, |
|---|
| 774 | 775 | .pin_config_group_set = amd_pinconf_group_set, |
|---|
| 775 | 776 | }; |
|---|
| 777 | + |
|---|
| 778 | +static void amd_gpio_irq_init(struct amd_gpio *gpio_dev) |
|---|
| 779 | +{ |
|---|
| 780 | + struct pinctrl_desc *desc = gpio_dev->pctrl->desc; |
|---|
| 781 | + unsigned long flags; |
|---|
| 782 | + u32 pin_reg, mask; |
|---|
| 783 | + int i; |
|---|
| 784 | + |
|---|
| 785 | + mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3) | |
|---|
| 786 | + BIT(INTERRUPT_MASK_OFF) | BIT(INTERRUPT_ENABLE_OFF) | |
|---|
| 787 | + BIT(WAKE_CNTRL_OFF_S4); |
|---|
| 788 | + |
|---|
| 789 | + for (i = 0; i < desc->npins; i++) { |
|---|
| 790 | + int pin = desc->pins[i].number; |
|---|
| 791 | + const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin); |
|---|
| 792 | + |
|---|
| 793 | + if (!pd) |
|---|
| 794 | + continue; |
|---|
| 795 | + |
|---|
| 796 | + raw_spin_lock_irqsave(&gpio_dev->lock, flags); |
|---|
| 797 | + |
|---|
| 798 | + pin_reg = readl(gpio_dev->base + pin * 4); |
|---|
| 799 | + pin_reg &= ~mask; |
|---|
| 800 | + writel(pin_reg, gpio_dev->base + pin * 4); |
|---|
| 801 | + |
|---|
| 802 | + raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); |
|---|
| 803 | + } |
|---|
| 804 | +} |
|---|
| 776 | 805 | |
|---|
| 777 | 806 | #ifdef CONFIG_PM_SLEEP |
|---|
| 778 | 807 | static bool amd_gpio_should_save(struct amd_gpio *gpio_dev, unsigned int pin) |
|---|
| .. | .. |
|---|
| 795 | 824 | |
|---|
| 796 | 825 | static int amd_gpio_suspend(struct device *dev) |
|---|
| 797 | 826 | { |
|---|
| 798 | | - struct platform_device *pdev = to_platform_device(dev); |
|---|
| 799 | | - struct amd_gpio *gpio_dev = platform_get_drvdata(pdev); |
|---|
| 827 | + struct amd_gpio *gpio_dev = dev_get_drvdata(dev); |
|---|
| 800 | 828 | struct pinctrl_desc *desc = gpio_dev->pctrl->desc; |
|---|
| 829 | + unsigned long flags; |
|---|
| 801 | 830 | int i; |
|---|
| 802 | 831 | |
|---|
| 803 | 832 | for (i = 0; i < desc->npins; i++) { |
|---|
| .. | .. |
|---|
| 806 | 835 | if (!amd_gpio_should_save(gpio_dev, pin)) |
|---|
| 807 | 836 | continue; |
|---|
| 808 | 837 | |
|---|
| 809 | | - gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin*4); |
|---|
| 838 | + raw_spin_lock_irqsave(&gpio_dev->lock, flags); |
|---|
| 839 | + gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin * 4) & ~PIN_IRQ_PENDING; |
|---|
| 840 | + raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); |
|---|
| 810 | 841 | } |
|---|
| 811 | 842 | |
|---|
| 812 | 843 | return 0; |
|---|
| .. | .. |
|---|
| 814 | 845 | |
|---|
| 815 | 846 | static int amd_gpio_resume(struct device *dev) |
|---|
| 816 | 847 | { |
|---|
| 817 | | - struct platform_device *pdev = to_platform_device(dev); |
|---|
| 818 | | - struct amd_gpio *gpio_dev = platform_get_drvdata(pdev); |
|---|
| 848 | + struct amd_gpio *gpio_dev = dev_get_drvdata(dev); |
|---|
| 819 | 849 | struct pinctrl_desc *desc = gpio_dev->pctrl->desc; |
|---|
| 850 | + unsigned long flags; |
|---|
| 820 | 851 | int i; |
|---|
| 821 | 852 | |
|---|
| 822 | 853 | for (i = 0; i < desc->npins; i++) { |
|---|
| .. | .. |
|---|
| 825 | 856 | if (!amd_gpio_should_save(gpio_dev, pin)) |
|---|
| 826 | 857 | continue; |
|---|
| 827 | 858 | |
|---|
| 828 | | - writel(gpio_dev->saved_regs[i], gpio_dev->base + pin*4); |
|---|
| 859 | + raw_spin_lock_irqsave(&gpio_dev->lock, flags); |
|---|
| 860 | + gpio_dev->saved_regs[i] |= readl(gpio_dev->base + pin * 4) & PIN_IRQ_PENDING; |
|---|
| 861 | + writel(gpio_dev->saved_regs[i], gpio_dev->base + pin * 4); |
|---|
| 862 | + raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); |
|---|
| 829 | 863 | } |
|---|
| 830 | 864 | |
|---|
| 831 | 865 | return 0; |
|---|
| .. | .. |
|---|
| 851 | 885 | int irq_base; |
|---|
| 852 | 886 | struct resource *res; |
|---|
| 853 | 887 | struct amd_gpio *gpio_dev; |
|---|
| 888 | + struct gpio_irq_chip *girq; |
|---|
| 854 | 889 | |
|---|
| 855 | 890 | gpio_dev = devm_kzalloc(&pdev->dev, |
|---|
| 856 | 891 | sizeof(struct amd_gpio), GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 865 | 900 | return -EINVAL; |
|---|
| 866 | 901 | } |
|---|
| 867 | 902 | |
|---|
| 868 | | - gpio_dev->base = devm_ioremap_nocache(&pdev->dev, res->start, |
|---|
| 903 | + gpio_dev->base = devm_ioremap(&pdev->dev, res->start, |
|---|
| 869 | 904 | resource_size(res)); |
|---|
| 870 | 905 | if (!gpio_dev->base) |
|---|
| 871 | 906 | return -ENOMEM; |
|---|
| 872 | 907 | |
|---|
| 873 | 908 | irq_base = platform_get_irq(pdev, 0); |
|---|
| 874 | | - if (irq_base < 0) { |
|---|
| 875 | | - dev_err(&pdev->dev, "Failed to get gpio IRQ: %d\n", irq_base); |
|---|
| 909 | + if (irq_base < 0) |
|---|
| 876 | 910 | return irq_base; |
|---|
| 877 | | - } |
|---|
| 878 | 911 | |
|---|
| 879 | 912 | #ifdef CONFIG_PM_SLEEP |
|---|
| 880 | 913 | gpio_dev->saved_regs = devm_kcalloc(&pdev->dev, amd_pinctrl_desc.npins, |
|---|
| .. | .. |
|---|
| 914 | 947 | return PTR_ERR(gpio_dev->pctrl); |
|---|
| 915 | 948 | } |
|---|
| 916 | 949 | |
|---|
| 950 | + /* Disable and mask interrupts */ |
|---|
| 951 | + amd_gpio_irq_init(gpio_dev); |
|---|
| 952 | + |
|---|
| 953 | + girq = &gpio_dev->gc.irq; |
|---|
| 954 | + girq->chip = &amd_gpio_irqchip; |
|---|
| 955 | + /* This will let us handle the parent IRQ in the driver */ |
|---|
| 956 | + girq->parent_handler = NULL; |
|---|
| 957 | + girq->num_parents = 0; |
|---|
| 958 | + girq->parents = NULL; |
|---|
| 959 | + girq->default_type = IRQ_TYPE_NONE; |
|---|
| 960 | + girq->handler = handle_simple_irq; |
|---|
| 961 | + |
|---|
| 917 | 962 | ret = gpiochip_add_data(&gpio_dev->gc, gpio_dev); |
|---|
| 918 | 963 | if (ret) |
|---|
| 919 | 964 | return ret; |
|---|
| .. | .. |
|---|
| 925 | 970 | goto out2; |
|---|
| 926 | 971 | } |
|---|
| 927 | 972 | |
|---|
| 928 | | - ret = gpiochip_irqchip_add(&gpio_dev->gc, |
|---|
| 929 | | - &amd_gpio_irqchip, |
|---|
| 930 | | - 0, |
|---|
| 931 | | - handle_simple_irq, |
|---|
| 932 | | - IRQ_TYPE_NONE); |
|---|
| 933 | | - if (ret) { |
|---|
| 934 | | - dev_err(&pdev->dev, "could not add irqchip\n"); |
|---|
| 935 | | - ret = -ENODEV; |
|---|
| 936 | | - goto out2; |
|---|
| 937 | | - } |
|---|
| 938 | | - |
|---|
| 939 | | - ret = devm_request_irq(&pdev->dev, irq_base, amd_gpio_irq_handler, 0, |
|---|
| 940 | | - KBUILD_MODNAME, gpio_dev); |
|---|
| 973 | + ret = devm_request_irq(&pdev->dev, irq_base, amd_gpio_irq_handler, |
|---|
| 974 | + IRQF_SHARED, KBUILD_MODNAME, gpio_dev); |
|---|
| 941 | 975 | if (ret) |
|---|
| 942 | 976 | goto out2; |
|---|
| 943 | 977 | |
|---|
| .. | .. |
|---|
| 963 | 997 | return 0; |
|---|
| 964 | 998 | } |
|---|
| 965 | 999 | |
|---|
| 1000 | +#ifdef CONFIG_ACPI |
|---|
| 966 | 1001 | static const struct acpi_device_id amd_gpio_acpi_match[] = { |
|---|
| 967 | 1002 | { "AMD0030", 0 }, |
|---|
| 968 | 1003 | { "AMDI0030", 0}, |
|---|
| .. | .. |
|---|
| 970 | 1005 | { }, |
|---|
| 971 | 1006 | }; |
|---|
| 972 | 1007 | MODULE_DEVICE_TABLE(acpi, amd_gpio_acpi_match); |
|---|
| 1008 | +#endif |
|---|
| 973 | 1009 | |
|---|
| 974 | 1010 | static struct platform_driver amd_gpio_driver = { |
|---|
| 975 | 1011 | .driver = { |
|---|