18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * arch/arm/mach-tegra/gpio.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Google, Inc 68c2ecf20Sopenharmony_ci * Copyright (c) 2011-2016, NVIDIA CORPORATION. All rights reserved. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: 98c2ecf20Sopenharmony_ci * Erik Gilling <konkers@google.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 228c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 238c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 248c2ecf20Sopenharmony_ci#include <linux/pm.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define GPIO_BANK(x) ((x) >> 5) 278c2ecf20Sopenharmony_ci#define GPIO_PORT(x) (((x) >> 3) & 0x3) 288c2ecf20Sopenharmony_ci#define GPIO_BIT(x) ((x) & 0x7) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define GPIO_REG(tgi, x) (GPIO_BANK(x) * tgi->soc->bank_stride + \ 318c2ecf20Sopenharmony_ci GPIO_PORT(x) * 4) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define GPIO_CNF(t, x) (GPIO_REG(t, x) + 0x00) 348c2ecf20Sopenharmony_ci#define GPIO_OE(t, x) (GPIO_REG(t, x) + 0x10) 358c2ecf20Sopenharmony_ci#define GPIO_OUT(t, x) (GPIO_REG(t, x) + 0X20) 368c2ecf20Sopenharmony_ci#define GPIO_IN(t, x) (GPIO_REG(t, x) + 0x30) 378c2ecf20Sopenharmony_ci#define GPIO_INT_STA(t, x) (GPIO_REG(t, x) + 0x40) 388c2ecf20Sopenharmony_ci#define GPIO_INT_ENB(t, x) (GPIO_REG(t, x) + 0x50) 398c2ecf20Sopenharmony_ci#define GPIO_INT_LVL(t, x) (GPIO_REG(t, x) + 0x60) 408c2ecf20Sopenharmony_ci#define GPIO_INT_CLR(t, x) (GPIO_REG(t, x) + 0x70) 418c2ecf20Sopenharmony_ci#define GPIO_DBC_CNT(t, x) (GPIO_REG(t, x) + 0xF0) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define GPIO_MSK_CNF(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x00) 458c2ecf20Sopenharmony_ci#define GPIO_MSK_OE(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x10) 468c2ecf20Sopenharmony_ci#define GPIO_MSK_OUT(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0X20) 478c2ecf20Sopenharmony_ci#define GPIO_MSK_DBC_EN(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x30) 488c2ecf20Sopenharmony_ci#define GPIO_MSK_INT_STA(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x40) 498c2ecf20Sopenharmony_ci#define GPIO_MSK_INT_ENB(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x50) 508c2ecf20Sopenharmony_ci#define GPIO_MSK_INT_LVL(t, x) (GPIO_REG(t, x) + t->soc->upper_offset + 0x60) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define GPIO_INT_LVL_MASK 0x010101 538c2ecf20Sopenharmony_ci#define GPIO_INT_LVL_EDGE_RISING 0x000101 548c2ecf20Sopenharmony_ci#define GPIO_INT_LVL_EDGE_FALLING 0x000100 558c2ecf20Sopenharmony_ci#define GPIO_INT_LVL_EDGE_BOTH 0x010100 568c2ecf20Sopenharmony_ci#define GPIO_INT_LVL_LEVEL_HIGH 0x000001 578c2ecf20Sopenharmony_ci#define GPIO_INT_LVL_LEVEL_LOW 0x000000 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct tegra_gpio_info; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct tegra_gpio_bank { 628c2ecf20Sopenharmony_ci unsigned int bank; 638c2ecf20Sopenharmony_ci unsigned int irq; 648c2ecf20Sopenharmony_ci spinlock_t lvl_lock[4]; 658c2ecf20Sopenharmony_ci spinlock_t dbc_lock[4]; /* Lock for updating debounce count register */ 668c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 678c2ecf20Sopenharmony_ci u32 cnf[4]; 688c2ecf20Sopenharmony_ci u32 out[4]; 698c2ecf20Sopenharmony_ci u32 oe[4]; 708c2ecf20Sopenharmony_ci u32 int_enb[4]; 718c2ecf20Sopenharmony_ci u32 int_lvl[4]; 728c2ecf20Sopenharmony_ci u32 wake_enb[4]; 738c2ecf20Sopenharmony_ci u32 dbc_enb[4]; 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ci u32 dbc_cnt[4]; 768c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi; 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistruct tegra_gpio_soc_config { 808c2ecf20Sopenharmony_ci bool debounce_supported; 818c2ecf20Sopenharmony_ci u32 bank_stride; 828c2ecf20Sopenharmony_ci u32 upper_offset; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct tegra_gpio_info { 868c2ecf20Sopenharmony_ci struct device *dev; 878c2ecf20Sopenharmony_ci void __iomem *regs; 888c2ecf20Sopenharmony_ci struct irq_domain *irq_domain; 898c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank_info; 908c2ecf20Sopenharmony_ci const struct tegra_gpio_soc_config *soc; 918c2ecf20Sopenharmony_ci struct gpio_chip gc; 928c2ecf20Sopenharmony_ci struct irq_chip ic; 938c2ecf20Sopenharmony_ci u32 bank_count; 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline void tegra_gpio_writel(struct tegra_gpio_info *tgi, 978c2ecf20Sopenharmony_ci u32 val, u32 reg) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci writel_relaxed(val, tgi->regs + reg); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline u32 tegra_gpio_readl(struct tegra_gpio_info *tgi, u32 reg) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return readl_relaxed(tgi->regs + reg); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic unsigned int tegra_gpio_compose(unsigned int bank, unsigned int port, 1088c2ecf20Sopenharmony_ci unsigned int bit) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void tegra_gpio_mask_write(struct tegra_gpio_info *tgi, u32 reg, 1148c2ecf20Sopenharmony_ci unsigned int gpio, u32 value) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci u32 val; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci val = 0x100 << GPIO_BIT(gpio); 1198c2ecf20Sopenharmony_ci if (value) 1208c2ecf20Sopenharmony_ci val |= 1 << GPIO_BIT(gpio); 1218c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, val, reg); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void tegra_gpio_enable(struct tegra_gpio_info *tgi, unsigned int gpio) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_CNF(tgi, gpio), gpio, 1); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void tegra_gpio_disable(struct tegra_gpio_info *tgi, unsigned int gpio) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_CNF(tgi, gpio), gpio, 0); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int tegra_gpio_request(struct gpio_chip *chip, unsigned int offset) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return pinctrl_gpio_request(chip->base + offset); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci pinctrl_gpio_free(chip->base + offset); 1448c2ecf20Sopenharmony_ci tegra_gpio_disable(tgi, offset); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void tegra_gpio_set(struct gpio_chip *chip, unsigned int offset, 1488c2ecf20Sopenharmony_ci int value) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OUT(tgi, offset), offset, value); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int tegra_gpio_get(struct gpio_chip *chip, unsigned int offset) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 1588c2ecf20Sopenharmony_ci unsigned int bval = BIT(GPIO_BIT(offset)); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* If gpio is in output mode then read from the out value */ 1618c2ecf20Sopenharmony_ci if (tegra_gpio_readl(tgi, GPIO_OE(tgi, offset)) & bval) 1628c2ecf20Sopenharmony_ci return !!(tegra_gpio_readl(tgi, GPIO_OUT(tgi, offset)) & bval); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return !!(tegra_gpio_readl(tgi, GPIO_IN(tgi, offset)) & bval); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int tegra_gpio_direction_input(struct gpio_chip *chip, 1688c2ecf20Sopenharmony_ci unsigned int offset) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 1718c2ecf20Sopenharmony_ci int ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 0); 1748c2ecf20Sopenharmony_ci tegra_gpio_enable(tgi, offset); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = pinctrl_gpio_direction_input(chip->base + offset); 1778c2ecf20Sopenharmony_ci if (ret < 0) 1788c2ecf20Sopenharmony_ci dev_err(tgi->dev, 1798c2ecf20Sopenharmony_ci "Failed to set pinctrl input direction of GPIO %d: %d", 1808c2ecf20Sopenharmony_ci chip->base + offset, ret); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int tegra_gpio_direction_output(struct gpio_chip *chip, 1868c2ecf20Sopenharmony_ci unsigned int offset, 1878c2ecf20Sopenharmony_ci int value) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci tegra_gpio_set(chip, offset, value); 1938c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, offset), offset, 1); 1948c2ecf20Sopenharmony_ci tegra_gpio_enable(tgi, offset); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = pinctrl_gpio_direction_output(chip->base + offset); 1978c2ecf20Sopenharmony_ci if (ret < 0) 1988c2ecf20Sopenharmony_ci dev_err(tgi->dev, 1998c2ecf20Sopenharmony_ci "Failed to set pinctrl output direction of GPIO %d: %d", 2008c2ecf20Sopenharmony_ci chip->base + offset, ret); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int tegra_gpio_get_direction(struct gpio_chip *chip, 2068c2ecf20Sopenharmony_ci unsigned int offset) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 2098c2ecf20Sopenharmony_ci u32 pin_mask = BIT(GPIO_BIT(offset)); 2108c2ecf20Sopenharmony_ci u32 cnf, oe; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci cnf = tegra_gpio_readl(tgi, GPIO_CNF(tgi, offset)); 2138c2ecf20Sopenharmony_ci if (!(cnf & pin_mask)) 2148c2ecf20Sopenharmony_ci return -EINVAL; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci oe = tegra_gpio_readl(tgi, GPIO_OE(tgi, offset)); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (oe & pin_mask) 2198c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int tegra_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, 2258c2ecf20Sopenharmony_ci unsigned int debounce) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 2288c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = &tgi->bank_info[GPIO_BANK(offset)]; 2298c2ecf20Sopenharmony_ci unsigned int debounce_ms = DIV_ROUND_UP(debounce, 1000); 2308c2ecf20Sopenharmony_ci unsigned long flags; 2318c2ecf20Sopenharmony_ci unsigned int port; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!debounce_ms) { 2348c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_DBC_EN(tgi, offset), 2358c2ecf20Sopenharmony_ci offset, 0); 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci debounce_ms = min(debounce_ms, 255U); 2408c2ecf20Sopenharmony_ci port = GPIO_PORT(offset); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* There is only one debounce count register per port and hence 2438c2ecf20Sopenharmony_ci * set the maximum of current and requested debounce time. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci spin_lock_irqsave(&bank->dbc_lock[port], flags); 2468c2ecf20Sopenharmony_ci if (bank->dbc_cnt[port] < debounce_ms) { 2478c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, debounce_ms, GPIO_DBC_CNT(tgi, offset)); 2488c2ecf20Sopenharmony_ci bank->dbc_cnt[port] = debounce_ms; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bank->dbc_lock[port], flags); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_DBC_EN(tgi, offset), offset, 1); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int tegra_gpio_set_config(struct gpio_chip *chip, unsigned int offset, 2588c2ecf20Sopenharmony_ci unsigned long config) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci u32 debounce; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) 2638c2ecf20Sopenharmony_ci return -ENOTSUPP; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci debounce = pinconf_to_config_argument(config); 2668c2ecf20Sopenharmony_ci return tegra_gpio_set_debounce(chip, offset, debounce); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = gpiochip_get_data(chip); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return irq_find_mapping(tgi->irq_domain, offset); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic void tegra_gpio_irq_ack(struct irq_data *d) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); 2798c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = bank->tgi; 2808c2ecf20Sopenharmony_ci unsigned int gpio = d->hwirq; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, 1 << GPIO_BIT(gpio), GPIO_INT_CLR(tgi, gpio)); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic void tegra_gpio_irq_mask(struct irq_data *d) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); 2888c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = bank->tgi; 2898c2ecf20Sopenharmony_ci unsigned int gpio = d->hwirq; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 0); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void tegra_gpio_irq_unmask(struct irq_data *d) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); 2978c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = bank->tgi; 2988c2ecf20Sopenharmony_ci unsigned int gpio = d->hwirq; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_INT_ENB(tgi, gpio), gpio, 1); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci unsigned int gpio = d->hwirq, port = GPIO_PORT(gpio), lvl_type; 3068c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); 3078c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = bank->tgi; 3088c2ecf20Sopenharmony_ci unsigned long flags; 3098c2ecf20Sopenharmony_ci u32 val; 3108c2ecf20Sopenharmony_ci int ret; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci switch (type & IRQ_TYPE_SENSE_MASK) { 3138c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 3148c2ecf20Sopenharmony_ci lvl_type = GPIO_INT_LVL_EDGE_RISING; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 3188c2ecf20Sopenharmony_ci lvl_type = GPIO_INT_LVL_EDGE_FALLING; 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 3228c2ecf20Sopenharmony_ci lvl_type = GPIO_INT_LVL_EDGE_BOTH; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 3268c2ecf20Sopenharmony_ci lvl_type = GPIO_INT_LVL_LEVEL_HIGH; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 3308c2ecf20Sopenharmony_ci lvl_type = GPIO_INT_LVL_LEVEL_LOW; 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci default: 3348c2ecf20Sopenharmony_ci return -EINVAL; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci spin_lock_irqsave(&bank->lvl_lock[port], flags); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci val = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio)); 3408c2ecf20Sopenharmony_ci val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio)); 3418c2ecf20Sopenharmony_ci val |= lvl_type << GPIO_BIT(gpio); 3428c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, val, GPIO_INT_LVL(tgi, gpio)); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bank->lvl_lock[port], flags); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, gpio), gpio, 0); 3478c2ecf20Sopenharmony_ci tegra_gpio_enable(tgi, gpio); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = gpiochip_lock_as_irq(&tgi->gc, gpio); 3508c2ecf20Sopenharmony_ci if (ret) { 3518c2ecf20Sopenharmony_ci dev_err(tgi->dev, 3528c2ecf20Sopenharmony_ci "unable to lock Tegra GPIO %u as IRQ\n", gpio); 3538c2ecf20Sopenharmony_ci tegra_gpio_disable(tgi, gpio); 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) 3588c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 3598c2ecf20Sopenharmony_ci else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) 3608c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void tegra_gpio_irq_shutdown(struct irq_data *d) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); 3688c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = bank->tgi; 3698c2ecf20Sopenharmony_ci unsigned int gpio = d->hwirq; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci tegra_gpio_irq_mask(d); 3728c2ecf20Sopenharmony_ci gpiochip_unlock_as_irq(&tgi->gc, gpio); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void tegra_gpio_irq_handler(struct irq_desc *desc) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci unsigned int port, pin, gpio; 3788c2ecf20Sopenharmony_ci bool unmasked = false; 3798c2ecf20Sopenharmony_ci u32 lvl; 3808c2ecf20Sopenharmony_ci unsigned long sta; 3818c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 3828c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = irq_desc_get_handler_data(desc); 3838c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = bank->tgi; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci for (port = 0; port < 4; port++) { 3888c2ecf20Sopenharmony_ci gpio = tegra_gpio_compose(bank->bank, port, 0); 3898c2ecf20Sopenharmony_ci sta = tegra_gpio_readl(tgi, GPIO_INT_STA(tgi, gpio)) & 3908c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_ENB(tgi, gpio)); 3918c2ecf20Sopenharmony_ci lvl = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio)); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci for_each_set_bit(pin, &sta, 8) { 3948c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, 1 << pin, 3958c2ecf20Sopenharmony_ci GPIO_INT_CLR(tgi, gpio)); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* if gpio is edge triggered, clear condition 3988c2ecf20Sopenharmony_ci * before executing the handler so that we don't 3998c2ecf20Sopenharmony_ci * miss edges 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci if (!unmasked && lvl & (0x100 << pin)) { 4028c2ecf20Sopenharmony_ci unmasked = true; 4038c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(tgi->irq_domain, 4078c2ecf20Sopenharmony_ci gpio + pin)); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (!unmasked) 4128c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4178c2ecf20Sopenharmony_cistatic int tegra_gpio_resume(struct device *dev) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = dev_get_drvdata(dev); 4208c2ecf20Sopenharmony_ci unsigned int b, p; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci for (b = 0; b < tgi->bank_count; b++) { 4238c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = &tgi->bank_info[b]; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { 4268c2ecf20Sopenharmony_ci unsigned int gpio = (b << 5) | (p << 3); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->cnf[p], 4298c2ecf20Sopenharmony_ci GPIO_CNF(tgi, gpio)); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (tgi->soc->debounce_supported) { 4328c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->dbc_cnt[p], 4338c2ecf20Sopenharmony_ci GPIO_DBC_CNT(tgi, gpio)); 4348c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->dbc_enb[p], 4358c2ecf20Sopenharmony_ci GPIO_MSK_DBC_EN(tgi, gpio)); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->out[p], 4398c2ecf20Sopenharmony_ci GPIO_OUT(tgi, gpio)); 4408c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->oe[p], 4418c2ecf20Sopenharmony_ci GPIO_OE(tgi, gpio)); 4428c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->int_lvl[p], 4438c2ecf20Sopenharmony_ci GPIO_INT_LVL(tgi, gpio)); 4448c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->int_enb[p], 4458c2ecf20Sopenharmony_ci GPIO_INT_ENB(tgi, gpio)); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int tegra_gpio_suspend(struct device *dev) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = dev_get_drvdata(dev); 4558c2ecf20Sopenharmony_ci unsigned int b, p; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (b = 0; b < tgi->bank_count; b++) { 4588c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = &tgi->bank_info[b]; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { 4618c2ecf20Sopenharmony_ci unsigned int gpio = (b << 5) | (p << 3); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci bank->cnf[p] = tegra_gpio_readl(tgi, 4648c2ecf20Sopenharmony_ci GPIO_CNF(tgi, gpio)); 4658c2ecf20Sopenharmony_ci bank->out[p] = tegra_gpio_readl(tgi, 4668c2ecf20Sopenharmony_ci GPIO_OUT(tgi, gpio)); 4678c2ecf20Sopenharmony_ci bank->oe[p] = tegra_gpio_readl(tgi, 4688c2ecf20Sopenharmony_ci GPIO_OE(tgi, gpio)); 4698c2ecf20Sopenharmony_ci if (tgi->soc->debounce_supported) { 4708c2ecf20Sopenharmony_ci bank->dbc_enb[p] = tegra_gpio_readl(tgi, 4718c2ecf20Sopenharmony_ci GPIO_MSK_DBC_EN(tgi, gpio)); 4728c2ecf20Sopenharmony_ci bank->dbc_enb[p] = (bank->dbc_enb[p] << 8) | 4738c2ecf20Sopenharmony_ci bank->dbc_enb[p]; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci bank->int_enb[p] = tegra_gpio_readl(tgi, 4778c2ecf20Sopenharmony_ci GPIO_INT_ENB(tgi, gpio)); 4788c2ecf20Sopenharmony_ci bank->int_lvl[p] = tegra_gpio_readl(tgi, 4798c2ecf20Sopenharmony_ci GPIO_INT_LVL(tgi, gpio)); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Enable gpio irq for wake up source */ 4828c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, bank->wake_enb[p], 4838c2ecf20Sopenharmony_ci GPIO_INT_ENB(tgi, gpio)); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d); 4938c2ecf20Sopenharmony_ci unsigned int gpio = d->hwirq; 4948c2ecf20Sopenharmony_ci u32 port, bit, mask; 4958c2ecf20Sopenharmony_ci int err; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci err = irq_set_irq_wake(bank->irq, enable); 4988c2ecf20Sopenharmony_ci if (err) 4998c2ecf20Sopenharmony_ci return err; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci port = GPIO_PORT(gpio); 5028c2ecf20Sopenharmony_ci bit = GPIO_BIT(gpio); 5038c2ecf20Sopenharmony_ci mask = BIT(bit); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (enable) 5068c2ecf20Sopenharmony_ci bank->wake_enb[port] |= mask; 5078c2ecf20Sopenharmony_ci else 5088c2ecf20Sopenharmony_ci bank->wake_enb[port] &= ~mask; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci#endif 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 5178c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic int tegra_dbg_gpio_show(struct seq_file *s, void *unused) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi = s->private; 5228c2ecf20Sopenharmony_ci unsigned int i, j; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 5258c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 5268c2ecf20Sopenharmony_ci unsigned int gpio = tegra_gpio_compose(i, j, 0); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci seq_printf(s, 5298c2ecf20Sopenharmony_ci "%u:%u %02x %02x %02x %02x %02x %02x %06x\n", 5308c2ecf20Sopenharmony_ci i, j, 5318c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_CNF(tgi, gpio)), 5328c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_OE(tgi, gpio)), 5338c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_OUT(tgi, gpio)), 5348c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_IN(tgi, gpio)), 5358c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_STA(tgi, gpio)), 5368c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_ENB(tgi, gpio)), 5378c2ecf20Sopenharmony_ci tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio))); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci debugfs_create_file("tegra_gpio", 0444, NULL, tgi, 5488c2ecf20Sopenharmony_ci &tegra_dbg_gpio_fops); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci#else 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic inline void tegra_gpio_debuginit(struct tegra_gpio_info *tgi) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci#endif 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tegra_gpio_pm_ops = { 5608c2ecf20Sopenharmony_ci SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) 5618c2ecf20Sopenharmony_ci}; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int tegra_gpio_probe(struct platform_device *pdev) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct tegra_gpio_info *tgi; 5668c2ecf20Sopenharmony_ci struct tegra_gpio_bank *bank; 5678c2ecf20Sopenharmony_ci unsigned int gpio, i, j; 5688c2ecf20Sopenharmony_ci int ret; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci tgi = devm_kzalloc(&pdev->dev, sizeof(*tgi), GFP_KERNEL); 5718c2ecf20Sopenharmony_ci if (!tgi) 5728c2ecf20Sopenharmony_ci return -ENODEV; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci tgi->soc = of_device_get_match_data(&pdev->dev); 5758c2ecf20Sopenharmony_ci tgi->dev = &pdev->dev; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = platform_irq_count(pdev); 5788c2ecf20Sopenharmony_ci if (ret < 0) 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci tgi->bank_count = ret; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (!tgi->bank_count) { 5848c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing IRQ resource\n"); 5858c2ecf20Sopenharmony_ci return -ENODEV; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci tgi->gc.label = "tegra-gpio"; 5898c2ecf20Sopenharmony_ci tgi->gc.request = tegra_gpio_request; 5908c2ecf20Sopenharmony_ci tgi->gc.free = tegra_gpio_free; 5918c2ecf20Sopenharmony_ci tgi->gc.direction_input = tegra_gpio_direction_input; 5928c2ecf20Sopenharmony_ci tgi->gc.get = tegra_gpio_get; 5938c2ecf20Sopenharmony_ci tgi->gc.direction_output = tegra_gpio_direction_output; 5948c2ecf20Sopenharmony_ci tgi->gc.set = tegra_gpio_set; 5958c2ecf20Sopenharmony_ci tgi->gc.get_direction = tegra_gpio_get_direction; 5968c2ecf20Sopenharmony_ci tgi->gc.to_irq = tegra_gpio_to_irq; 5978c2ecf20Sopenharmony_ci tgi->gc.base = 0; 5988c2ecf20Sopenharmony_ci tgi->gc.ngpio = tgi->bank_count * 32; 5998c2ecf20Sopenharmony_ci tgi->gc.parent = &pdev->dev; 6008c2ecf20Sopenharmony_ci tgi->gc.of_node = pdev->dev.of_node; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci tgi->ic.name = "GPIO"; 6038c2ecf20Sopenharmony_ci tgi->ic.irq_ack = tegra_gpio_irq_ack; 6048c2ecf20Sopenharmony_ci tgi->ic.irq_mask = tegra_gpio_irq_mask; 6058c2ecf20Sopenharmony_ci tgi->ic.irq_unmask = tegra_gpio_irq_unmask; 6068c2ecf20Sopenharmony_ci tgi->ic.irq_set_type = tegra_gpio_irq_set_type; 6078c2ecf20Sopenharmony_ci tgi->ic.irq_shutdown = tegra_gpio_irq_shutdown; 6088c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6098c2ecf20Sopenharmony_ci tgi->ic.irq_set_wake = tegra_gpio_irq_set_wake; 6108c2ecf20Sopenharmony_ci#endif 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, tgi); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (tgi->soc->debounce_supported) 6158c2ecf20Sopenharmony_ci tgi->gc.set_config = tegra_gpio_set_config; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci tgi->bank_info = devm_kcalloc(&pdev->dev, tgi->bank_count, 6188c2ecf20Sopenharmony_ci sizeof(*tgi->bank_info), GFP_KERNEL); 6198c2ecf20Sopenharmony_ci if (!tgi->bank_info) 6208c2ecf20Sopenharmony_ci return -ENOMEM; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci tgi->irq_domain = irq_domain_add_linear(pdev->dev.of_node, 6238c2ecf20Sopenharmony_ci tgi->gc.ngpio, 6248c2ecf20Sopenharmony_ci &irq_domain_simple_ops, NULL); 6258c2ecf20Sopenharmony_ci if (!tgi->irq_domain) 6268c2ecf20Sopenharmony_ci return -ENODEV; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 6298c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, i); 6308c2ecf20Sopenharmony_ci if (ret < 0) 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci bank = &tgi->bank_info[i]; 6348c2ecf20Sopenharmony_ci bank->bank = i; 6358c2ecf20Sopenharmony_ci bank->irq = ret; 6368c2ecf20Sopenharmony_ci bank->tgi = tgi; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci tgi->regs = devm_platform_ioremap_resource(pdev, 0); 6408c2ecf20Sopenharmony_ci if (IS_ERR(tgi->regs)) 6418c2ecf20Sopenharmony_ci return PTR_ERR(tgi->regs); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 6448c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 6458c2ecf20Sopenharmony_ci int gpio = tegra_gpio_compose(i, j, 0); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci tegra_gpio_writel(tgi, 0x00, GPIO_INT_ENB(tgi, gpio)); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(&pdev->dev, &tgi->gc, tgi); 6528c2ecf20Sopenharmony_ci if (ret < 0) { 6538c2ecf20Sopenharmony_ci irq_domain_remove(tgi->irq_domain); 6548c2ecf20Sopenharmony_ci return ret; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci for (gpio = 0; gpio < tgi->gc.ngpio; gpio++) { 6588c2ecf20Sopenharmony_ci int irq = irq_create_mapping(tgi->irq_domain, gpio); 6598c2ecf20Sopenharmony_ci /* No validity check; all Tegra GPIOs are valid IRQs */ 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci bank = &tgi->bank_info[GPIO_BANK(gpio)]; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci irq_set_chip_data(irq, bank); 6648c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci for (i = 0; i < tgi->bank_count; i++) { 6688c2ecf20Sopenharmony_ci bank = &tgi->bank_info[i]; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(bank->irq, 6718c2ecf20Sopenharmony_ci tegra_gpio_irq_handler, bank); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 6748c2ecf20Sopenharmony_ci spin_lock_init(&bank->lvl_lock[j]); 6758c2ecf20Sopenharmony_ci spin_lock_init(&bank->dbc_lock[j]); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci tegra_gpio_debuginit(tgi); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci return 0; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic const struct tegra_gpio_soc_config tegra20_gpio_config = { 6858c2ecf20Sopenharmony_ci .bank_stride = 0x80, 6868c2ecf20Sopenharmony_ci .upper_offset = 0x800, 6878c2ecf20Sopenharmony_ci}; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic const struct tegra_gpio_soc_config tegra30_gpio_config = { 6908c2ecf20Sopenharmony_ci .bank_stride = 0x100, 6918c2ecf20Sopenharmony_ci .upper_offset = 0x80, 6928c2ecf20Sopenharmony_ci}; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic const struct tegra_gpio_soc_config tegra210_gpio_config = { 6958c2ecf20Sopenharmony_ci .debounce_supported = true, 6968c2ecf20Sopenharmony_ci .bank_stride = 0x100, 6978c2ecf20Sopenharmony_ci .upper_offset = 0x80, 6988c2ecf20Sopenharmony_ci}; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_gpio_of_match[] = { 7018c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra210-gpio", .data = &tegra210_gpio_config }, 7028c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-gpio", .data = &tegra30_gpio_config }, 7038c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-gpio", .data = &tegra20_gpio_config }, 7048c2ecf20Sopenharmony_ci { }, 7058c2ecf20Sopenharmony_ci}; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic struct platform_driver tegra_gpio_driver = { 7088c2ecf20Sopenharmony_ci .driver = { 7098c2ecf20Sopenharmony_ci .name = "tegra-gpio", 7108c2ecf20Sopenharmony_ci .pm = &tegra_gpio_pm_ops, 7118c2ecf20Sopenharmony_ci .of_match_table = tegra_gpio_of_match, 7128c2ecf20Sopenharmony_ci }, 7138c2ecf20Sopenharmony_ci .probe = tegra_gpio_probe, 7148c2ecf20Sopenharmony_ci}; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int __init tegra_gpio_init(void) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci return platform_driver_register(&tegra_gpio_driver); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_cisubsys_initcall(tegra_gpio_init); 721