// SPDX-License-Identifier: GPL-2.0-only/* * GPIO driver for AMD * * Copyright (c) 2014,2015 AMD Corporation. * Authors: Ken Xue <Ken.Xue@amd.com> * Wu, Jeff <Jeff.Wu@amd.com> * */ #include <linux/err.h>#include <linux/bug.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/compiler.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/log2.h>#include <linux/io.h>#include <linux/gpio/driver.h>#include <linux/slab.h>#include <linux/platform_device.h>#include <linux/acpi.h>#include <linux/seq_file.h>#include <linux/interrupt.h>#include <linux/bitops.h>#include <linux/pinctrl/pinconf.h>#include <linux/pinctrl/pinconf-generic.h>#include <linux/pinctrl/pinmux.h>#include <linux/string_choices.h>#include <linux/suspend.h> #include "core.h"#include "pinctrl-utils.h"#include "pinctrl-amd.h" #ifdef CONFIG_SUSPENDstatic struct amd_gpio *pinctrl_dev;#endif static int amd_gpio_get_direction(struct gpio_chip *gc, unsigned offset){ unsigned long flags; u32 pin_reg; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_IN;} static int amd_gpio_direction_input(struct gpio_chip *gc, unsigned offset){ unsigned long flags; u32 pin_reg; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); pin_reg &= ~BIT(OUTPUT_ENABLE_OFF); writel(pin_reg, gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return 0;} static int amd_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value){ u32 pin_reg; unsigned long flags; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); pin_reg |= BIT(OUTPUT_ENABLE_OFF); if (value) pin_reg |= BIT(OUTPUT_VALUE_OFF); else pin_reg &= ~BIT(OUTPUT_VALUE_OFF); writel(pin_reg, gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return 0;} static int amd_gpio_get_value(struct gpio_chip *gc, unsigned offset){ u32 pin_reg; unsigned long flags; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return !!(pin_reg & BIT(PIN_STS_OFF));} static int amd_gpio_set_value(struct gpio_chip *gc, unsigned int offset, int value){ u32 pin_reg; unsigned long flags; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); if (value) pin_reg |= BIT(OUTPUT_VALUE_OFF); else pin_reg &= ~BIT(OUTPUT_VALUE_OFF); writel(pin_reg, gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return 0;} static int amd_gpio_set_debounce(struct amd_gpio *gpio_dev, unsigned int offset, unsigned int debounce){ u32 time; u32 pin_reg; int ret = 0; /* Use special handling for Pin0 debounce */ if (offset == 0) { pin_reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); if (pin_reg & INTERNAL_GPIO0_DEBOUNCE) debounce = 0; } pin_reg = readl(gpio_dev->base + offset * 4); if (debounce) { pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF; pin_reg &= ~DB_TMR_OUT_MASK; /* Debounce Debounce Timer Max TmrLarge TmrOutUnit Unit Debounce Time 0 0 61 usec (2 RtcClk) 976 usec 0 1 244 usec (8 RtcClk) 3.9 msec 1 0 15.6 msec (512 RtcClk) 250 msec 1 1 62.5 msec (2048 RtcClk) 1 sec */ if (debounce < 61) { pin_reg |= 1; pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); } else if (debounce < 976) { time = debounce / 61; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); } else if (debounce < 3900) { time = debounce / 244; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); } else if (debounce < 250000) { time = debounce / 15625; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg |= BIT(DB_TMR_LARGE_OFF); } else if (debounce < 1000000) { time = debounce / 62500; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); pin_reg |= BIT(DB_TMR_LARGE_OFF); } else { pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF); ret = -EINVAL; } } else { pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); pin_reg &= ~DB_TMR_OUT_MASK; pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF); } writel(pin_reg, gpio_dev->base + offset * 4); return ret;} #ifdef CONFIG_DEBUG_FSstatic void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc){ u32 pin_reg; u32 db_cntrl; unsigned long flags; unsigned int bank, i, pin_num; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); bool tmr_out_unit; bool tmr_large; char *level_trig; char *active_level; char *interrupt_mask; char *wake_cntrl0; char *wake_cntrl1; char *wake_cntrl2; char *pin_sts; char *interrupt_sts; char *wake_sts; char *orientation; char debounce_value[40]; char *debounce_enable; char *wake_cntrlz; seq_printf(s, "WAKE_INT_MASTER_REG: 0x%08x\n", readl(gpio_dev->base + WAKE_INT_MASTER_REG)); for (bank = 0; bank < gpio_dev->hwbank_num; bank++) { unsigned int time = 0; unsigned int unit = 0; switch (bank) { case 0: i = 0; pin_num = AMD_GPIO_PINS_BANK0; break; case 1: i = 64; pin_num = AMD_GPIO_PINS_BANK1 + i; break; case 2: i = 128; pin_num = AMD_GPIO_PINS_BANK2 + i; break; case 3: i = 192; pin_num = AMD_GPIO_PINS_BANK3 + i; break; default: /* Illegal bank number, ignore */ continue; } seq_printf(s, "GPIO bank%d\n", bank); seq_puts(s, "gpio\t int|active|trigger|S0i3| S3|S4/S5| Z|wake|pull| orient| debounce|reg\n"); for (; i < pin_num; i++) { seq_printf(s, "#%d\t", i); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + i * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); if (pin_reg & BIT(INTERRUPT_ENABLE_OFF)) { u8 level = (pin_reg >> ACTIVE_LEVEL_OFF) & ACTIVE_LEVEL_MASK; if (level == ACTIVE_LEVEL_HIGH) active_level = "↑"; else if (level == ACTIVE_LEVEL_LOW) active_level = "↓"; else if (!(pin_reg & BIT(LEVEL_TRIG_OFF)) && level == ACTIVE_LEVEL_BOTH) active_level = "b"; else active_level = "?"; if (pin_reg & BIT(LEVEL_TRIG_OFF)) level_trig = "level"; else level_trig = " edge"; if (pin_reg & BIT(INTERRUPT_MASK_OFF)) interrupt_mask = "😛"; else interrupt_mask = "😷"; if (pin_reg & BIT(INTERRUPT_STS_OFF)) interrupt_sts = "🔥"; else interrupt_sts = " "; seq_printf(s, "%s %s| %s| %s|", interrupt_sts, interrupt_mask, active_level, level_trig); } else seq_puts(s, " ∅| | |"); if (pin_reg & BIT(WAKE_CNTRL_OFF_S0I3)) wake_cntrl0 = "⏰"; else wake_cntrl0 = " "; seq_printf(s, " %s| ", wake_cntrl0); if (pin_reg & BIT(WAKE_CNTRL_OFF_S3)) wake_cntrl1 = "⏰"; else wake_cntrl1 = " "; seq_printf(s, "%s|", wake_cntrl1); if (pin_reg & BIT(WAKE_CNTRL_OFF_S4)) wake_cntrl2 = "⏰"; else wake_cntrl2 = " "; seq_printf(s, " %s|", wake_cntrl2); if (pin_reg & BIT(WAKECNTRL_Z_OFF)) wake_cntrlz = "⏰"; else wake_cntrlz = " "; seq_printf(s, "%s|", wake_cntrlz); if (pin_reg & BIT(WAKE_STS_OFF)) wake_sts = "🔥"; else wake_sts = " "; seq_printf(s, " %s|", wake_sts); if (pin_reg & BIT(PULL_UP_ENABLE_OFF)) { seq_puts(s, " ↑ |"); } else if (pin_reg & BIT(PULL_DOWN_ENABLE_OFF)) { seq_puts(s, " ↓ |"); } else { seq_puts(s, " |"); } if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) { pin_sts = "output"; if (pin_reg & BIT(OUTPUT_VALUE_OFF)) orientation = "↑"; else orientation = "↓"; } else { pin_sts = "input "; if (pin_reg & BIT(PIN_STS_OFF)) orientation = "↑"; else orientation = "↓"; } seq_printf(s, "%s %s|", pin_sts, orientation); db_cntrl = (DB_CNTRl_MASK << DB_CNTRL_OFF) & pin_reg; if (db_cntrl) { tmr_out_unit = pin_reg & BIT(DB_TMR_OUT_UNIT_OFF); tmr_large = pin_reg & BIT(DB_TMR_LARGE_OFF); time = pin_reg & DB_TMR_OUT_MASK; if (tmr_large) { if (tmr_out_unit) unit = 62500; else unit = 15625; } else { if (tmr_out_unit) unit = 244; else unit = 61; } if ((DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF) == db_cntrl) debounce_enable = "b"; else if ((DB_TYPE_PRESERVE_LOW_GLITCH << DB_CNTRL_OFF) == db_cntrl) debounce_enable = "↓"; else debounce_enable = "↑"; snprintf(debounce_value, sizeof(debounce_value), "%06u", time * unit); seq_printf(s, "%s (🕑 %sus)|", debounce_enable, debounce_value); } else { seq_puts(s, " |"); } seq_printf(s, "0x%x\n", pin_reg); } }}#else#define amd_gpio_dbg_show NULL#endif static void amd_gpio_irq_enable(struct irq_data *d){ u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); gpiochip_enable_irq(gc, hwirq); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg |= BIT(INTERRUPT_ENABLE_OFF); pin_reg |= BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static void amd_gpio_irq_disable(struct irq_data *d){ u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg &= ~BIT(INTERRUPT_ENABLE_OFF); pin_reg &= ~BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); gpiochip_disable_irq(gc, hwirq);} static void amd_gpio_irq_mask(struct irq_data *d){ u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg &= ~BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static void amd_gpio_irq_unmask(struct irq_data *d){ u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg |= BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static int amd_gpio_irq_set_wake(struct irq_data *d, unsigned int on){ u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); u32 wake_mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); irq_hw_number_t hwirq = irqd_to_hwirq(d); int err; pm_pr_dbg("Setting wake for GPIO %lu to %s\n", hwirq, str_enable_disable(on)); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); if (on) pin_reg |= wake_mask; else pin_reg &= ~wake_mask; writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); if (on) err = enable_irq_wake(gpio_dev->irq); else err = disable_irq_wake(gpio_dev->irq); if (err) dev_err(&gpio_dev->pdev->dev, "failed to %s wake-up interrupt\n", str_enable_disable(on)); return 0;} static void amd_gpio_irq_eoi(struct irq_data *d){ u32 reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); reg |= EOI_MASK; writel(reg, gpio_dev->base + WAKE_INT_MASTER_REG); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags);} static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type){ int ret = 0; u32 pin_reg, pin_reg_irq_en, mask; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc);Lines 1–500 of 1297. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * GPIO driver for AMD * * Copyright (c) 2014,2015 AMD Corporation. * Authors: Ken Xue <Ken.Xue@amd.com> * Wu, Jeff <Jeff.Wu@amd.com> * */ #include <linux/err.h> #include <linux/bug.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/compiler.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/log2.h> #include <linux/io.h> #include <linux/gpio/driver.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/seq_file.h> #include <linux/interrupt.h> #include <linux/bitops.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinmux.h> #include <linux/string_choices.h> #include <linux/suspend.h> #include "core.h" #include "pinctrl-utils.h" #include "pinctrl-amd.h" #ifdef CONFIG_SUSPEND static struct amd_gpio *pinctrl_dev; #endif static int amd_gpio_get_direction(struct gpio_chip *gc, unsigned offset) { unsigned long flags; u32 pin_reg; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) return GPIO_LINE_DIRECTION_OUT; return GPIO_LINE_DIRECTION_IN; } static int amd_gpio_direction_input(struct gpio_chip *gc, unsigned offset) { unsigned long flags; u32 pin_reg; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); pin_reg &= ~BIT(OUTPUT_ENABLE_OFF); writel(pin_reg, gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return 0; } static int amd_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int value) { u32 pin_reg; unsigned long flags; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); pin_reg |= BIT(OUTPUT_ENABLE_OFF); if (value) pin_reg |= BIT(OUTPUT_VALUE_OFF); else pin_reg &= ~BIT(OUTPUT_VALUE_OFF); writel(pin_reg, gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return 0; } static int amd_gpio_get_value(struct gpio_chip *gc, unsigned offset) { u32 pin_reg; unsigned long flags; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return !!(pin_reg & BIT(PIN_STS_OFF)); } static int amd_gpio_set_value(struct gpio_chip *gc, unsigned int offset, int value) { u32 pin_reg; unsigned long flags; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + offset * 4); if (value) pin_reg |= BIT(OUTPUT_VALUE_OFF); else pin_reg &= ~BIT(OUTPUT_VALUE_OFF); writel(pin_reg, gpio_dev->base + offset * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); return 0; } static int amd_gpio_set_debounce(struct amd_gpio *gpio_dev, unsigned int offset, unsigned int debounce) { u32 time; u32 pin_reg; int ret = 0; /* Use special handling for Pin0 debounce */ if (offset == 0) { pin_reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); if (pin_reg & INTERNAL_GPIO0_DEBOUNCE) debounce = 0; } pin_reg = readl(gpio_dev->base + offset * 4); if (debounce) { pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF; pin_reg &= ~DB_TMR_OUT_MASK; /* Debounce Debounce Timer Max TmrLarge TmrOutUnit Unit Debounce Time 0 0 61 usec (2 RtcClk) 976 usec 0 1 244 usec (8 RtcClk) 3.9 msec 1 0 15.6 msec (512 RtcClk) 250 msec 1 1 62.5 msec (2048 RtcClk) 1 sec */ if (debounce < 61) { pin_reg |= 1; pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); } else if (debounce < 976) { time = debounce / 61; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); } else if (debounce < 3900) { time = debounce / 244; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); } else if (debounce < 250000) { time = debounce / 15625; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg |= BIT(DB_TMR_LARGE_OFF); } else if (debounce < 1000000) { time = debounce / 62500; pin_reg |= time & DB_TMR_OUT_MASK; pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); pin_reg |= BIT(DB_TMR_LARGE_OFF); } else { pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF); ret = -EINVAL; } } else { pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); pin_reg &= ~BIT(DB_TMR_LARGE_OFF); pin_reg &= ~DB_TMR_OUT_MASK; pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF); } writel(pin_reg, gpio_dev->base + offset * 4); return ret; } #ifdef CONFIG_DEBUG_FS static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) { u32 pin_reg; u32 db_cntrl; unsigned long flags; unsigned int bank, i, pin_num; struct amd_gpio *gpio_dev = gpiochip_get_data(gc); bool tmr_out_unit; bool tmr_large; char *level_trig; char *active_level; char *interrupt_mask; char *wake_cntrl0; char *wake_cntrl1; char *wake_cntrl2; char *pin_sts; char *interrupt_sts; char *wake_sts; char *orientation; char debounce_value[40]; char *debounce_enable; char *wake_cntrlz; seq_printf(s, "WAKE_INT_MASTER_REG: 0x%08x\n", readl(gpio_dev->base + WAKE_INT_MASTER_REG)); for (bank = 0; bank < gpio_dev->hwbank_num; bank++) { unsigned int time = 0; unsigned int unit = 0; switch (bank) { case 0: i = 0; pin_num = AMD_GPIO_PINS_BANK0; break; case 1: i = 64; pin_num = AMD_GPIO_PINS_BANK1 + i; break; case 2: i = 128; pin_num = AMD_GPIO_PINS_BANK2 + i; break; case 3: i = 192; pin_num = AMD_GPIO_PINS_BANK3 + i; break; default: /* Illegal bank number, ignore */ continue; } seq_printf(s, "GPIO bank%d\n", bank); seq_puts(s, "gpio\t int|active|trigger|S0i3| S3|S4/S5| Z|wake|pull| orient| debounce|reg\n"); for (; i < pin_num; i++) { seq_printf(s, "#%d\t", i); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + i * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); if (pin_reg & BIT(INTERRUPT_ENABLE_OFF)) { u8 level = (pin_reg >> ACTIVE_LEVEL_OFF) & ACTIVE_LEVEL_MASK; if (level == ACTIVE_LEVEL_HIGH) active_level = "↑"; else if (level == ACTIVE_LEVEL_LOW) active_level = "↓"; else if (!(pin_reg & BIT(LEVEL_TRIG_OFF)) && level == ACTIVE_LEVEL_BOTH) active_level = "b"; else active_level = "?"; if (pin_reg & BIT(LEVEL_TRIG_OFF)) level_trig = "level"; else level_trig = " edge"; if (pin_reg & BIT(INTERRUPT_MASK_OFF)) interrupt_mask = "😛"; else interrupt_mask = "😷"; if (pin_reg & BIT(INTERRUPT_STS_OFF)) interrupt_sts = "🔥"; else interrupt_sts = " "; seq_printf(s, "%s %s| %s| %s|", interrupt_sts, interrupt_mask, active_level, level_trig); } else seq_puts(s, " ∅| | |"); if (pin_reg & BIT(WAKE_CNTRL_OFF_S0I3)) wake_cntrl0 = "⏰"; else wake_cntrl0 = " "; seq_printf(s, " %s| ", wake_cntrl0); if (pin_reg & BIT(WAKE_CNTRL_OFF_S3)) wake_cntrl1 = "⏰"; else wake_cntrl1 = " "; seq_printf(s, "%s|", wake_cntrl1); if (pin_reg & BIT(WAKE_CNTRL_OFF_S4)) wake_cntrl2 = "⏰"; else wake_cntrl2 = " "; seq_printf(s, " %s|", wake_cntrl2); if (pin_reg & BIT(WAKECNTRL_Z_OFF)) wake_cntrlz = "⏰"; else wake_cntrlz = " "; seq_printf(s, "%s|", wake_cntrlz); if (pin_reg & BIT(WAKE_STS_OFF)) wake_sts = "🔥"; else wake_sts = " "; seq_printf(s, " %s|", wake_sts); if (pin_reg & BIT(PULL_UP_ENABLE_OFF)) { seq_puts(s, " ↑ |"); } else if (pin_reg & BIT(PULL_DOWN_ENABLE_OFF)) { seq_puts(s, " ↓ |"); } else { seq_puts(s, " |"); } if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) { pin_sts = "output"; if (pin_reg & BIT(OUTPUT_VALUE_OFF)) orientation = "↑"; else orientation = "↓"; } else { pin_sts = "input "; if (pin_reg & BIT(PIN_STS_OFF)) orientation = "↑"; else orientation = "↓"; } seq_printf(s, "%s %s|", pin_sts, orientation); db_cntrl = (DB_CNTRl_MASK << DB_CNTRL_OFF) & pin_reg; if (db_cntrl) { tmr_out_unit = pin_reg & BIT(DB_TMR_OUT_UNIT_OFF); tmr_large = pin_reg & BIT(DB_TMR_LARGE_OFF); time = pin_reg & DB_TMR_OUT_MASK; if (tmr_large) { if (tmr_out_unit) unit = 62500; else unit = 15625; } else { if (tmr_out_unit) unit = 244; else unit = 61; } if ((DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF) == db_cntrl) debounce_enable = "b"; else if ((DB_TYPE_PRESERVE_LOW_GLITCH << DB_CNTRL_OFF) == db_cntrl) debounce_enable = "↓"; else debounce_enable = "↑"; snprintf(debounce_value, sizeof(debounce_value), "%06u", time * unit); seq_printf(s, "%s (🕑 %sus)|", debounce_enable, debounce_value); } else { seq_puts(s, " |"); } seq_printf(s, "0x%x\n", pin_reg); } } } #else #define amd_gpio_dbg_show NULL #endif static void amd_gpio_irq_enable(struct irq_data *d) { u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); gpiochip_enable_irq(gc, hwirq); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg |= BIT(INTERRUPT_ENABLE_OFF); pin_reg |= BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); } static void amd_gpio_irq_disable(struct irq_data *d) { u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg &= ~BIT(INTERRUPT_ENABLE_OFF); pin_reg &= ~BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); gpiochip_disable_irq(gc, hwirq); } static void amd_gpio_irq_mask(struct irq_data *d) { u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg &= ~BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); } static void amd_gpio_irq_unmask(struct irq_data *d) { u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); pin_reg |= BIT(INTERRUPT_MASK_OFF); writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); } static int amd_gpio_irq_set_wake(struct irq_data *d, unsigned int on) { u32 pin_reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); u32 wake_mask = BIT(WAKE_CNTRL_OFF_S0I3) | BIT(WAKE_CNTRL_OFF_S3); irq_hw_number_t hwirq = irqd_to_hwirq(d); int err; pm_pr_dbg("Setting wake for GPIO %lu to %s\n", hwirq, str_enable_disable(on)); raw_spin_lock_irqsave(&gpio_dev->lock, flags); pin_reg = readl(gpio_dev->base + hwirq * 4); if (on) pin_reg |= wake_mask; else pin_reg &= ~wake_mask; writel(pin_reg, gpio_dev->base + hwirq * 4); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); if (on) err = enable_irq_wake(gpio_dev->irq); else err = disable_irq_wake(gpio_dev->irq); if (err) dev_err(&gpio_dev->pdev->dev, "failed to %s wake-up interrupt\n", str_enable_disable(on)); return 0; } static void amd_gpio_irq_eoi(struct irq_data *d) { u32 reg; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc); raw_spin_lock_irqsave(&gpio_dev->lock, flags); reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); reg |= EOI_MASK; writel(reg, gpio_dev->base + WAKE_INT_MASTER_REG); raw_spin_unlock_irqrestore(&gpio_dev->lock, flags); } static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type) { int ret = 0; u32 pin_reg, pin_reg_irq_en, mask; unsigned long flags; struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct amd_gpio *gpio_dev = gpiochip_get_data(gc);