162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm/mach-tegra/gpio.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 Google, Inc 662306a36Sopenharmony_ci * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: 962306a36Sopenharmony_ci * Erik Gilling <konkers@google.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/irq.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/seq_file.h> 2262306a36Sopenharmony_ci#include <linux/irqdomain.h> 2362306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 2462306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2562306a36Sopenharmony_ci#include <linux/pm.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define GPIO_BANK(x) ((x) >> 5) 2862306a36Sopenharmony_ci#define GPIO_PORT(x) (((x) >> 3) & 0x3) 2962306a36Sopenharmony_ci#define GPIO_BIT(x) ((x) & 0x7) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define GPIO_REG(tgi, x) (GPIO_BANK(x) * tgi->soc->bank_stride + \ 3262306a36Sopenharmony_ci GPIO_PORT(x) * 4) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define GPIO_CNF(t, x) (GPIO_REG(t, x) + 0x00) 3562306a36Sopenharmony_ci#define GPIO_OE(t, x) (GPIO_REG(t, x) + 0x10) 3662306a36Sopenharmony_ci#define GPIO_OUT(t, x) (GPIO_REG(t, x) + 0X20) 3762306a36Sopenharmony_ci#define GPIO_IN(t, x) (GPIO_REG(t, x) + 0x30) 3862306a36Sopenharmony_ci#define GPIO_INT_STA(t, x) (GPIO_REG(t, x) + 0x40) 3962306a36Sopenharmony_ci#define GPIO_INT_ENB(t, x) (GPIO_REG(t, x) + 0x50) 4062306a36Sopenharmony_ci#define GPIO_INT_LVL(t, x) (GPIO_REG(t, x) + 0x60) 4162306a36Sopenharmony_ci#define GPIO_INT_CLR(t, x) (GPIO_REG(t, x) + 0x70) 4262306a36Sopenharmony_ci#define GPIO_DBC_CNT(t, x) (GPIO_REG(t, x) + 0xF0) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define GPIO_MSK_CNF(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x00) 4662306a36Sopenharmony_ci#define GPIO_MSK_OE(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x10) 4762306a36Sopenharmony_ci#define GPIO_MSK_OUT(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0X20) 4862306a36Sopenharmony_ci#define GPIO_MSK_DBC_EN(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x30) 4962306a36Sopenharmony_ci#define GPIO_MSK_INT_STA(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x40) 5062306a36Sopenharmony_ci#define GPIO_MSK_INT_ENB(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x50) 5162306a36Sopenharmony_ci#define GPIO_MSK_INT_LVL(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x60) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define GPIO_INT_LVL_MASK 0x010101 5462306a36Sopenharmony_ci#define GPIO_INT_LVL_EDGE_RISING 0x000101 5562306a36Sopenharmony_ci#define GPIO_INT_LVL_EDGE_FALLING 0x000100 5662306a36Sopenharmony_ci#define GPIO_INT_LVL_EDGE_BOTH 0x010100 5762306a36Sopenharmony_ci#define GPIO_INT_LVL_LEVEL_HIGH 0x000001 5862306a36Sopenharmony_ci#define GPIO_INT_LVL_LEVEL_LOW 0x000000 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct tegra_gpio_info; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct tegra_gpio_bank { 6362306a36Sopenharmony_ci unsigned int bank; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * IRQ-core code uses raw locking, and thus, nested locking also 6762306a36Sopenharmony_ci * should be raw in order not to trip spinlock debug warnings. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci raw_spinlock_t lvl_lock[4]; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* Lock for updating debounce count register */ 7262306a36Sopenharmony_ci spinlock_t dbc_lock[4]; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 7562306a36Sopenharmony_ci u32 cnf[4]; 7662306a36Sopenharmony_ci u32 out[4]; 7762306a36Sopenharmony_ci u32 oe[4]; 7862306a36Sopenharmony_ci u32 int_enb[4]; 7962306a36Sopenharmony_ci u32 int_lvl[4]; 8062306a36Sopenharmony_ci u32 wake_enb[4]; 8162306a36Sopenharmony_ci u32 dbc_enb[4]; 8262306a36Sopenharmony_ci#endif 8362306a36Sopenharmony_ci u32 dbc_cnt[4]; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct tegra_gpio_soc_config { 8762306a36Sopenharmony_ci bool debounce_supported; 8862306a36Sopenharmony_ci u32 bank_stride; 8962306a36Sopenharmony_ci u32 upper_offset; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct tegra_gpio_info { 9362306a36Sopenharmony_ci struct device *dev; 9462306a36Sopenharmony_ci void __iomem *regs; 9562306a36Sopenharmony_ci struct tegra_gpio_bank *bank_info; 9662306a36Sopenharmony_ci const struct tegra_gpio_soc_config *soc; 9762306a36Sopenharmony_ci struct gpio_chip gc; 9862306a36Sopenharmony_ci u32 bank_count; 9962306a36Sopenharmony_ci unsigned int *irqs; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, 10362306a36Sopenharmony_ci u32 val, u32 reg) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci writel_relaxed(val, tgi->regs + reg); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return readl_relaxed(tgi->regs + reg); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port, 11462306a36Sopenharmony_ci unsigned int bit) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void tegra_gpio_mask_write(struct tegra_gpio_info *tgi, u32 reg, 12062306a36Sopenharmony_ci unsigned int gpio, u32 value) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci u32 val; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci val = 0x100 << GPIO_BIT(gpio); 12562306a36Sopenharmony_ci if (value) 12662306a36Sopenharmony_ci val |= 1 << GPIO_BIT(gpio); 12762306a36Sopenharmony_ci tegra_gpio_writel(tgi, val, reg); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void tegra_gpio_enable(struct tegra_gpio_info *tgi, unsigned int gpio) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_CNF(tgi, gpio), gpio, 1); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void tegra_gpio_disable(struct tegra_gpio_info *tgi, unsigned int gpio) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_CNF(tgi, gpio), gpio, 0); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int tegra_gpio_request(struct gpio_chip *chip, unsigned int offset) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci return pinctrl_gpio_request(chip->base + offset); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci pinctrl_gpio_free(chip->base + offset); 15062306a36Sopenharmony_ci tegra_gpio_disable(tgi, offset); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, 15462306a36Sopenharmony_ci int value) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int tegra_gpio_get(struct gpio_chip *chip, unsigned int offset) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 16462306a36Sopenharmony_ci unsigned int bval = BIT(GPIO_BIT(offset)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* If gpio is in output mode then read from the out value */ 16762306a36Sopenharmony_ci if (tegra_gpio_readl(tgi, GPIO_OE(tgi, offset)) & bval) 16862306a36Sopenharmony_ci return !!(tegra_gpio_readl(tgi, GPIO_OUT(tgi, offset)) & bval); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return !!(tegra_gpio_readl(tgi, GPIO_IN(tgi, offset)) & bval); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int tegra_gpio_direction_input(struct gpio_chip *chip, 17462306a36Sopenharmony_ci unsigned int offset) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 17762306a36Sopenharmony_ci int ret; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); 18062306a36Sopenharmony_ci tegra_gpio_enable(tgi, offset); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ret = pinctrl_gpio_direction_input(chip->base + offset); 18362306a36Sopenharmony_ci if (ret < 0) 18462306a36Sopenharmony_ci dev_err(tgi->dev, 18562306a36Sopenharmony_ci "Failed to set pinctrl input direction of GPIO %d: %d", 18662306a36Sopenharmony_ci chip->base + offset, ret); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int tegra_gpio_direction_output(struct gpio_chip *chip, 19262306a36Sopenharmony_ci unsigned int offset, 19362306a36Sopenharmony_ci int value) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 19662306a36Sopenharmony_ci int ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci tegra_gpio_set(chip, offset, value); 19962306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); 20062306a36Sopenharmony_ci tegra_gpio_enable(tgi, offset); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = pinctrl_gpio_direction_output(chip->base + offset); 20362306a36Sopenharmony_ci if (ret < 0) 20462306a36Sopenharmony_ci dev_err(tgi->dev, 20562306a36Sopenharmony_ci "Failed to set pinctrl output direction of GPIO %d: %d", 20662306a36Sopenharmony_ci chip->base + offset, ret); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int tegra_gpio_get_direction(struct gpio_chip *chip, 21262306a36Sopenharmony_ci unsigned int offset) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 21562306a36Sopenharmony_ci u32 pin_mask = BIT(GPIO_BIT(offset)); 21662306a36Sopenharmony_ci u32 cnf, oe; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci cnf = tegra_gpio_readl(tgi, GPIO_CNF(tgi, offset)); 21962306a36Sopenharmony_ci if (!(cnf & pin_mask)) 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci oe = tegra_gpio_readl(tgi, GPIO_OE(tgi, offset)); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (oe & pin_mask) 22562306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int tegra_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, 23162306a36Sopenharmony_ci unsigned int debounce) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 23462306a36Sopenharmony_ci struct tegra_gpio_bank *bank = &tgi->bank_info[GPIO_BANK(offset)]; 23562306a36Sopenharmony_ci unsigned int debounce_ms = DIV_ROUND_UP(debounce, 1000); 23662306a36Sopenharmony_ci unsigned long flags; 23762306a36Sopenharmony_ci unsigned int port; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!debounce_ms) { 24062306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_DBC_EN(tgi, offset), 24162306a36Sopenharmony_ci offset, 0); 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci debounce_ms = min(debounce_ms, 255U); 24662306a36Sopenharmony_ci port = GPIO_PORT(offset); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* There is only one debounce count register per port and hence 24962306a36Sopenharmony_ci * set the maximum of current and requested debounce time. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci spin_lock_irqsave(&bank->dbc_lock[port], flags); 25262306a36Sopenharmony_ci if (bank->dbc_cnt[port] < debounce_ms) { 25362306a36Sopenharmony_ci tegra_gpio_writel(tgi, debounce_ms, GPIO_DBC_CNT(tgi, offset)); 25462306a36Sopenharmony_ci bank->dbc_cnt[port] = debounce_ms; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci spin_unlock_irqrestore(&bank->dbc_lock[port], flags); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_DBC_EN(tgi, offset), offset, 1); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return 0; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset, 26462306a36Sopenharmony_ci unsigned long config) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci u32 debounce; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) 26962306a36Sopenharmony_ci return -ENOTSUPP; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci debounce = pinconf_to_config_argument(config); 27262306a36Sopenharmony_ci return tegra_gpio_set_debounce(chip, offset, debounce); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void tegra_gpio_irq_ack(struct irq_data *d) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 27862306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 27962306a36Sopenharmony_ci unsigned int gpio = d->hwirq; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio)); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void tegra_gpio_irq_mask(struct irq_data *d) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 28762306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 28862306a36Sopenharmony_ci unsigned int gpio = d->hwirq; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0); 29162306a36Sopenharmony_ci gpiochip_disable_irq(chip, gpio); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic void tegra_gpio_irq_unmask(struct irq_data *d) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 29762306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 29862306a36Sopenharmony_ci unsigned int gpio = d->hwirq; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci gpiochip_enable_irq(chip, gpio); 30162306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type; 30762306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 30862306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 30962306a36Sopenharmony_ci struct tegra_gpio_bank *bank; 31062306a36Sopenharmony_ci unsigned long flags; 31162306a36Sopenharmony_ci int ret; 31262306a36Sopenharmony_ci u32 val; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci bank = &tgi->bank_info[GPIO_BANK(d->hwirq)]; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 31762306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 31862306a36Sopenharmony_ci lvl_type = GPIO_INT_LVL_EDGE_RISING; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 32262306a36Sopenharmony_ci lvl_type = GPIO_INT_LVL_EDGE_FALLING; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 32662306a36Sopenharmony_ci lvl_type = GPIO_INT_LVL_EDGE_BOTH; 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 33062306a36Sopenharmony_ci lvl_type = GPIO_INT_LVL_LEVEL_HIGH; 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 33462306a36Sopenharmony_ci lvl_type = GPIO_INT_LVL_LEVEL_LOW; 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci default: 33862306a36Sopenharmony_ci return -EINVAL; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci raw_spin_lock_irqsave(&bank->lvl_lock[port], flags); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci val = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio)); 34462306a36Sopenharmony_ci val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio)); 34562306a36Sopenharmony_ci val |= lvl_type << GPIO_BIT(gpio); 34662306a36Sopenharmony_ci tegra_gpio_writel(tgi, val, GPIO_INT_LVL(tgi, gpio)); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&bank->lvl_lock[port], flags); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, gpio), gpio, 0); 35162306a36Sopenharmony_ci tegra_gpio_enable(tgi, gpio); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ret = gpiochip_lock_as_irq(&tgi->gc, gpio); 35462306a36Sopenharmony_ci if (ret) { 35562306a36Sopenharmony_ci dev_err(tgi->dev, 35662306a36Sopenharmony_ci "unable to lock Tegra GPIO %u as IRQ\n", gpio); 35762306a36Sopenharmony_ci tegra_gpio_disable(tgi, gpio); 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) 36262306a36Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 36362306a36Sopenharmony_ci else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) 36462306a36Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (d->parent_data) 36762306a36Sopenharmony_ci ret = irq_chip_set_type_parent(d, type); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return ret; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void tegra_gpio_irq_shutdown(struct irq_data *d) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 37562306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 37662306a36Sopenharmony_ci unsigned int gpio = d->hwirq; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci tegra_gpio_irq_mask(d); 37962306a36Sopenharmony_ci gpiochip_unlock_as_irq(&tgi->gc, gpio); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic void tegra_gpio_irq_handler(struct irq_desc *desc) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct tegra_gpio_info *tgi = irq_desc_get_handler_data(desc); 38562306a36Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 38662306a36Sopenharmony_ci struct irq_domain *domain = tgi->gc.irq.domain; 38762306a36Sopenharmony_ci unsigned int irq = irq_desc_get_irq(desc); 38862306a36Sopenharmony_ci struct tegra_gpio_bank *bank = NULL; 38962306a36Sopenharmony_ci unsigned int port, pin, gpio, i; 39062306a36Sopenharmony_ci bool unmasked = false; 39162306a36Sopenharmony_ci unsigned long sta; 39262306a36Sopenharmony_ci u32 lvl; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 39562306a36Sopenharmony_ci if (tgi->irqs[i] == irq) { 39662306a36Sopenharmony_ci bank = &tgi->bank_info[i]; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (WARN_ON(bank == NULL)) 40262306a36Sopenharmony_ci return; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci chained_irq_enter(chip, desc); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (port = 0; port < 4; port++) { 40762306a36Sopenharmony_ci gpio = tegra_gpio_compose(bank->bank, port, 0); 40862306a36Sopenharmony_ci sta = tegra_gpio_readl(tgi, GPIO_INT_STA(tgi, gpio)) & 40962306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_ENB(tgi, gpio)); 41062306a36Sopenharmony_ci lvl = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio)); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci for_each_set_bit(pin, &sta, 8) { 41362306a36Sopenharmony_ci int ret; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci tegra_gpio_writel(tgi, 1 << pin, 41662306a36Sopenharmony_ci GPIO_INT_CLR(tgi, gpio)); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* if gpio is edge triggered, clear condition 41962306a36Sopenharmony_ci * before executing the handler so that we don't 42062306a36Sopenharmony_ci * miss edges 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci if (!unmasked && lvl & (0x100 << pin)) { 42362306a36Sopenharmony_ci unmasked = true; 42462306a36Sopenharmony_ci chained_irq_exit(chip, desc); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ret = generic_handle_domain_irq(domain, gpio + pin); 42862306a36Sopenharmony_ci WARN_RATELIMIT(ret, "hwirq = %d", gpio + pin); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!unmasked) 43362306a36Sopenharmony_ci chained_irq_exit(chip, desc); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic int tegra_gpio_child_to_parent_hwirq(struct gpio_chip *chip, 43762306a36Sopenharmony_ci unsigned int hwirq, 43862306a36Sopenharmony_ci unsigned int type, 43962306a36Sopenharmony_ci unsigned int *parent_hwirq, 44062306a36Sopenharmony_ci unsigned int *parent_type) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq); 44362306a36Sopenharmony_ci *parent_type = type; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int tegra_gpio_populate_parent_fwspec(struct gpio_chip *chip, 44962306a36Sopenharmony_ci union gpio_irq_fwspec *gfwspec, 45062306a36Sopenharmony_ci unsigned int parent_hwirq, 45162306a36Sopenharmony_ci unsigned int parent_type) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct irq_fwspec *fwspec = &gfwspec->fwspec; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci fwspec->fwnode = chip->irq.parent_domain->fwnode; 45662306a36Sopenharmony_ci fwspec->param_count = 3; 45762306a36Sopenharmony_ci fwspec->param[0] = 0; 45862306a36Sopenharmony_ci fwspec->param[1] = parent_hwirq; 45962306a36Sopenharmony_ci fwspec->param[2] = parent_type; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 46562306a36Sopenharmony_cistatic int tegra_gpio_resume(struct device *dev) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct tegra_gpio_info *tgi = dev_get_drvdata(dev); 46862306a36Sopenharmony_ci unsigned int b, p; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci for (b = 0; b < tgi->bank_count; b++) { 47162306a36Sopenharmony_ci struct tegra_gpio_bank *bank = &tgi->bank_info[b]; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { 47462306a36Sopenharmony_ci unsigned int gpio = (b << 5) | (p << 3); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->cnf[p], 47762306a36Sopenharmony_ci GPIO_CNF(tgi, gpio)); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (tgi->soc->debounce_supported) { 48062306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->dbc_cnt[p], 48162306a36Sopenharmony_ci GPIO_DBC_CNT(tgi, gpio)); 48262306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->dbc_enb[p], 48362306a36Sopenharmony_ci GPIO_MSK_DBC_EN(tgi, gpio)); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->out[p], 48762306a36Sopenharmony_ci GPIO_OUT(tgi, gpio)); 48862306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->oe[p], 48962306a36Sopenharmony_ci GPIO_OE(tgi, gpio)); 49062306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->int_lvl[p], 49162306a36Sopenharmony_ci GPIO_INT_LVL(tgi, gpio)); 49262306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->int_enb[p], 49362306a36Sopenharmony_ci GPIO_INT_ENB(tgi, gpio)); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int tegra_gpio_suspend(struct device *dev) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct tegra_gpio_info *tgi = dev_get_drvdata(dev); 50362306a36Sopenharmony_ci unsigned int b, p; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (b = 0; b < tgi->bank_count; b++) { 50662306a36Sopenharmony_ci struct tegra_gpio_bank *bank = &tgi->bank_info[b]; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { 50962306a36Sopenharmony_ci unsigned int gpio = (b << 5) | (p << 3); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci bank->cnf[p] = tegra_gpio_readl(tgi, 51262306a36Sopenharmony_ci GPIO_CNF(tgi, gpio)); 51362306a36Sopenharmony_ci bank->out[p] = tegra_gpio_readl(tgi, 51462306a36Sopenharmony_ci GPIO_OUT(tgi, gpio)); 51562306a36Sopenharmony_ci bank->oe[p] = tegra_gpio_readl(tgi, 51662306a36Sopenharmony_ci GPIO_OE(tgi, gpio)); 51762306a36Sopenharmony_ci if (tgi->soc->debounce_supported) { 51862306a36Sopenharmony_ci bank->dbc_enb[p] = tegra_gpio_readl(tgi, 51962306a36Sopenharmony_ci GPIO_MSK_DBC_EN(tgi, gpio)); 52062306a36Sopenharmony_ci bank->dbc_enb[p] = (bank->dbc_enb[p] << 8) | 52162306a36Sopenharmony_ci bank->dbc_enb[p]; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci bank->int_enb[p] = tegra_gpio_readl(tgi, 52562306a36Sopenharmony_ci GPIO_INT_ENB(tgi, gpio)); 52662306a36Sopenharmony_ci bank->int_lvl[p] = tegra_gpio_readl(tgi, 52762306a36Sopenharmony_ci GPIO_INT_LVL(tgi, gpio)); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Enable gpio irq for wake up source */ 53062306a36Sopenharmony_ci tegra_gpio_writel(tgi, bank->wake_enb[p], 53162306a36Sopenharmony_ci GPIO_INT_ENB(tgi, gpio)); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 54162306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 54262306a36Sopenharmony_ci struct tegra_gpio_bank *bank; 54362306a36Sopenharmony_ci unsigned int gpio = d->hwirq; 54462306a36Sopenharmony_ci u32 port, bit, mask; 54562306a36Sopenharmony_ci int err; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci bank = &tgi->bank_info[GPIO_BANK(d->hwirq)]; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci port = GPIO_PORT(gpio); 55062306a36Sopenharmony_ci bit = GPIO_BIT(gpio); 55162306a36Sopenharmony_ci mask = BIT(bit); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci err = irq_set_irq_wake(tgi->irqs[bank->bank], enable); 55462306a36Sopenharmony_ci if (err) 55562306a36Sopenharmony_ci return err; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci if (d->parent_data) { 55862306a36Sopenharmony_ci err = irq_chip_set_wake_parent(d, enable); 55962306a36Sopenharmony_ci if (err) { 56062306a36Sopenharmony_ci irq_set_irq_wake(tgi->irqs[bank->bank], !enable); 56162306a36Sopenharmony_ci return err; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (enable) 56662306a36Sopenharmony_ci bank->wake_enb[port] |= mask; 56762306a36Sopenharmony_ci else 56862306a36Sopenharmony_ci bank->wake_enb[port] &= ~mask; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci#endif 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int tegra_gpio_irq_set_affinity(struct irq_data *data, 57562306a36Sopenharmony_ci const struct cpumask *dest, 57662306a36Sopenharmony_ci bool force) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci if (data->parent_data) 57962306a36Sopenharmony_ci return irq_chip_set_affinity_parent(data, dest, force); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return -EINVAL; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int tegra_gpio_irq_request_resources(struct irq_data *d) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 58762306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci tegra_gpio_enable(tgi, d->hwirq); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return gpiochip_reqres_irq(chip, d->hwirq); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void tegra_gpio_irq_release_resources(struct irq_data *d) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 59762306a36Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci gpiochip_relres_irq(chip, d->hwirq); 60062306a36Sopenharmony_ci tegra_gpio_enable(tgi, d->hwirq); 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic void tegra_gpio_irq_print_chip(struct irq_data *d, struct seq_file *s) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct gpio_chip *chip = irq_data_get_irq_chip_data(d); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci seq_printf(s, dev_name(chip->parent)); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic const struct irq_chip tegra_gpio_irq_chip = { 61162306a36Sopenharmony_ci .irq_shutdown = tegra_gpio_irq_shutdown, 61262306a36Sopenharmony_ci .irq_ack = tegra_gpio_irq_ack, 61362306a36Sopenharmony_ci .irq_mask = tegra_gpio_irq_mask, 61462306a36Sopenharmony_ci .irq_unmask = tegra_gpio_irq_unmask, 61562306a36Sopenharmony_ci .irq_set_type = tegra_gpio_irq_set_type, 61662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 61762306a36Sopenharmony_ci .irq_set_wake = tegra_gpio_irq_set_wake, 61862306a36Sopenharmony_ci#endif 61962306a36Sopenharmony_ci .irq_print_chip = tegra_gpio_irq_print_chip, 62062306a36Sopenharmony_ci .irq_request_resources = tegra_gpio_irq_request_resources, 62162306a36Sopenharmony_ci .irq_release_resources = tegra_gpio_irq_release_resources, 62262306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 62362306a36Sopenharmony_ci}; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic const struct irq_chip tegra210_gpio_irq_chip = { 62662306a36Sopenharmony_ci .irq_shutdown = tegra_gpio_irq_shutdown, 62762306a36Sopenharmony_ci .irq_ack = tegra_gpio_irq_ack, 62862306a36Sopenharmony_ci .irq_mask = tegra_gpio_irq_mask, 62962306a36Sopenharmony_ci .irq_unmask = tegra_gpio_irq_unmask, 63062306a36Sopenharmony_ci .irq_set_affinity = tegra_gpio_irq_set_affinity, 63162306a36Sopenharmony_ci .irq_set_type = tegra_gpio_irq_set_type, 63262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 63362306a36Sopenharmony_ci .irq_set_wake = tegra_gpio_irq_set_wake, 63462306a36Sopenharmony_ci#endif 63562306a36Sopenharmony_ci .irq_print_chip = tegra_gpio_irq_print_chip, 63662306a36Sopenharmony_ci .irq_request_resources = tegra_gpio_irq_request_resources, 63762306a36Sopenharmony_ci .irq_release_resources = tegra_gpio_irq_release_resources, 63862306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci#include <linux/debugfs.h> 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int tegra_dbg_gpio_show(struct seq_file *s, void *unused) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct tegra_gpio_info *tgi = dev_get_drvdata(s->private); 64862306a36Sopenharmony_ci unsigned int i, j; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 65162306a36Sopenharmony_ci for (j = 0; j < 4; j++) { 65262306a36Sopenharmony_ci unsigned int gpio = tegra_gpio_compose(i, j, 0); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci seq_printf(s, 65562306a36Sopenharmony_ci "%u:%u %02x %02x %02x %02x %02x %02x %06x\n", 65662306a36Sopenharmony_ci i, j, 65762306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_CNF(tgi, gpio)), 65862306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_OE(tgi, gpio)), 65962306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_OUT(tgi, gpio)), 66062306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_IN(tgi, gpio)), 66162306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_STA(tgi, gpio)), 66262306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_ENB(tgi, gpio)), 66362306a36Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio))); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci return 0; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci debugfs_create_devm_seqfile(tgi->dev, "tegra_gpio", NULL, 67262306a36Sopenharmony_ci tegra_dbg_gpio_show); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci#else 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci#endif 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic const struct dev_pm_ops tegra_gpio_pm_ops = { 68462306a36Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic const struct of_device_id tegra_pmc_of_match[] = { 68862306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-pmc", }, 68962306a36Sopenharmony_ci { /* sentinel */ }, 69062306a36Sopenharmony_ci}; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int tegra_gpio_probe(struct platform_device *pdev) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct tegra_gpio_bank *bank; 69562306a36Sopenharmony_ci struct tegra_gpio_info *tgi; 69662306a36Sopenharmony_ci struct gpio_irq_chip *irq; 69762306a36Sopenharmony_ci struct device_node *np; 69862306a36Sopenharmony_ci unsigned int i, j; 69962306a36Sopenharmony_ci int ret; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); 70262306a36Sopenharmony_ci if (!tgi) 70362306a36Sopenharmony_ci return -ENODEV; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci tgi->soc = of_device_get_match_data(&pdev->dev); 70662306a36Sopenharmony_ci tgi->dev = &pdev->dev; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ret = platform_irq_count(pdev); 70962306a36Sopenharmony_ci if (ret < 0) 71062306a36Sopenharmony_ci return ret; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci tgi->bank_count = ret; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (!tgi->bank_count) { 71562306a36Sopenharmony_ci dev_err(&pdev->dev, "Missing IRQ resource\n"); 71662306a36Sopenharmony_ci return -ENODEV; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci tgi->gc.label = "tegra-gpio"; 72062306a36Sopenharmony_ci tgi->gc.request = tegra_gpio_request; 72162306a36Sopenharmony_ci tgi->gc.free = tegra_gpio_free; 72262306a36Sopenharmony_ci tgi->gc.direction_input = tegra_gpio_direction_input; 72362306a36Sopenharmony_ci tgi->gc.get = tegra_gpio_get; 72462306a36Sopenharmony_ci tgi->gc.direction_output = tegra_gpio_direction_output; 72562306a36Sopenharmony_ci tgi->gc.set = tegra_gpio_set; 72662306a36Sopenharmony_ci tgi->gc.get_direction = tegra_gpio_get_direction; 72762306a36Sopenharmony_ci tgi->gc.base = 0; 72862306a36Sopenharmony_ci tgi->gc.ngpio = tgi->bank_count * 32; 72962306a36Sopenharmony_ci tgi->gc.parent = &pdev->dev; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci platform_set_drvdata(pdev, tgi); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (tgi->soc->debounce_supported) 73462306a36Sopenharmony_ci tgi->gc.set_config = tegra_gpio_set_config; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci tgi->bank_info = devm_kcalloc(&pdev->dev, tgi->bank_count, 73762306a36Sopenharmony_ci sizeof(*tgi->bank_info), GFP_KERNEL); 73862306a36Sopenharmony_ci if (!tgi->bank_info) 73962306a36Sopenharmony_ci return -ENOMEM; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci tgi->irqs = devm_kcalloc(&pdev->dev, tgi->bank_count, 74262306a36Sopenharmony_ci sizeof(*tgi->irqs), GFP_KERNEL); 74362306a36Sopenharmony_ci if (!tgi->irqs) 74462306a36Sopenharmony_ci return -ENOMEM; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 74762306a36Sopenharmony_ci ret = platform_get_irq(pdev, i); 74862306a36Sopenharmony_ci if (ret < 0) 74962306a36Sopenharmony_ci return ret; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci bank = &tgi->bank_info[i]; 75262306a36Sopenharmony_ci bank->bank = i; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci tgi->irqs[i] = ret; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci for (j = 0; j < 4; j++) { 75762306a36Sopenharmony_ci raw_spin_lock_init(&bank->lvl_lock[j]); 75862306a36Sopenharmony_ci spin_lock_init(&bank->dbc_lock[j]); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci irq = &tgi->gc.irq; 76362306a36Sopenharmony_ci irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); 76462306a36Sopenharmony_ci irq->child_to_parent_hwirq = tegra_gpio_child_to_parent_hwirq; 76562306a36Sopenharmony_ci irq->populate_parent_alloc_arg = tegra_gpio_populate_parent_fwspec; 76662306a36Sopenharmony_ci irq->handler = handle_simple_irq; 76762306a36Sopenharmony_ci irq->default_type = IRQ_TYPE_NONE; 76862306a36Sopenharmony_ci irq->parent_handler = tegra_gpio_irq_handler; 76962306a36Sopenharmony_ci irq->parent_handler_data = tgi; 77062306a36Sopenharmony_ci irq->num_parents = tgi->bank_count; 77162306a36Sopenharmony_ci irq->parents = tgi->irqs; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci np = of_find_matching_node(NULL, tegra_pmc_of_match); 77462306a36Sopenharmony_ci if (np) { 77562306a36Sopenharmony_ci irq->parent_domain = irq_find_host(np); 77662306a36Sopenharmony_ci of_node_put(np); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (!irq->parent_domain) 77962306a36Sopenharmony_ci return -EPROBE_DEFER; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci gpio_irq_chip_set_chip(irq, &tegra210_gpio_irq_chip); 78262306a36Sopenharmony_ci } else { 78362306a36Sopenharmony_ci gpio_irq_chip_set_chip(irq, &tegra_gpio_irq_chip); 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci tgi->regs = devm_platform_ioremap_resource(pdev, 0); 78762306a36Sopenharmony_ci if (IS_ERR(tgi->regs)) 78862306a36Sopenharmony_ci return PTR_ERR(tgi->regs); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 79162306a36Sopenharmony_ci for (j = 0; j < 4; j++) { 79262306a36Sopenharmony_ci int gpio = tegra_gpio_compose(i, j, 0); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci tegra_gpio_writel(tgi, 0x00, GPIO_INT_ENB(tgi, gpio)); 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi); 79962306a36Sopenharmony_ci if (ret < 0) 80062306a36Sopenharmony_ci return ret; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci tegra_gpio_debuginit(tgi); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic const struct tegra_gpio_soc_config tegra20_gpio_config = { 80862306a36Sopenharmony_ci .bank_stride = 0x80, 80962306a36Sopenharmony_ci .upper_offset = 0x800, 81062306a36Sopenharmony_ci}; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic const struct tegra_gpio_soc_config tegra30_gpio_config = { 81362306a36Sopenharmony_ci .bank_stride = 0x100, 81462306a36Sopenharmony_ci .upper_offset = 0x80, 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic const struct tegra_gpio_soc_config tegra210_gpio_config = { 81862306a36Sopenharmony_ci .debounce_supported = true, 81962306a36Sopenharmony_ci .bank_stride = 0x100, 82062306a36Sopenharmony_ci .upper_offset = 0x80, 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic const struct of_device_id tegra_gpio_of_match[] = { 82462306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-gpio", .data = &tegra210_gpio_config }, 82562306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-gpio", .data = &tegra30_gpio_config }, 82662306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config }, 82762306a36Sopenharmony_ci { }, 82862306a36Sopenharmony_ci}; 82962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_gpio_of_match); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic struct platform_driver tegra_gpio_driver = { 83262306a36Sopenharmony_ci .driver = { 83362306a36Sopenharmony_ci .name = "tegra-gpio", 83462306a36Sopenharmony_ci .pm = &tegra_gpio_pm_ops, 83562306a36Sopenharmony_ci .of_match_table = tegra_gpio_of_match, 83662306a36Sopenharmony_ci }, 83762306a36Sopenharmony_ci .probe = tegra_gpio_probe, 83862306a36Sopenharmony_ci}; 83962306a36Sopenharmony_cimodule_platform_driver(tegra_gpio_driver); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra GPIO controller driver"); 84262306a36Sopenharmony_ciMODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); 84362306a36Sopenharmony_ciMODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 84462306a36Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); 84562306a36Sopenharmony_ciMODULE_AUTHOR("Erik Gilling <konkers@google.com>"); 84662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 847