18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2015-2017 Broadcom 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/bitops.h> 158c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 208c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cienum gio_reg_index { 248c2ecf20Sopenharmony_ci GIO_REG_ODEN = 0, 258c2ecf20Sopenharmony_ci GIO_REG_DATA, 268c2ecf20Sopenharmony_ci GIO_REG_IODIR, 278c2ecf20Sopenharmony_ci GIO_REG_EC, 288c2ecf20Sopenharmony_ci GIO_REG_EI, 298c2ecf20Sopenharmony_ci GIO_REG_MASK, 308c2ecf20Sopenharmony_ci GIO_REG_LEVEL, 318c2ecf20Sopenharmony_ci GIO_REG_STAT, 328c2ecf20Sopenharmony_ci NUMBER_OF_GIO_REGISTERS 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define GIO_BANK_SIZE (NUMBER_OF_GIO_REGISTERS * sizeof(u32)) 368c2ecf20Sopenharmony_ci#define GIO_BANK_OFF(bank, off) (((bank) * GIO_BANK_SIZE) + (off * sizeof(u32))) 378c2ecf20Sopenharmony_ci#define GIO_ODEN(bank) GIO_BANK_OFF(bank, GIO_REG_ODEN) 388c2ecf20Sopenharmony_ci#define GIO_DATA(bank) GIO_BANK_OFF(bank, GIO_REG_DATA) 398c2ecf20Sopenharmony_ci#define GIO_IODIR(bank) GIO_BANK_OFF(bank, GIO_REG_IODIR) 408c2ecf20Sopenharmony_ci#define GIO_EC(bank) GIO_BANK_OFF(bank, GIO_REG_EC) 418c2ecf20Sopenharmony_ci#define GIO_EI(bank) GIO_BANK_OFF(bank, GIO_REG_EI) 428c2ecf20Sopenharmony_ci#define GIO_MASK(bank) GIO_BANK_OFF(bank, GIO_REG_MASK) 438c2ecf20Sopenharmony_ci#define GIO_LEVEL(bank) GIO_BANK_OFF(bank, GIO_REG_LEVEL) 448c2ecf20Sopenharmony_ci#define GIO_STAT(bank) GIO_BANK_OFF(bank, GIO_REG_STAT) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct brcmstb_gpio_bank { 478c2ecf20Sopenharmony_ci struct list_head node; 488c2ecf20Sopenharmony_ci int id; 498c2ecf20Sopenharmony_ci struct gpio_chip gc; 508c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *parent_priv; 518c2ecf20Sopenharmony_ci u32 width; 528c2ecf20Sopenharmony_ci u32 wake_active; 538c2ecf20Sopenharmony_ci u32 saved_regs[GIO_REG_STAT]; /* Don't save and restore GIO_REG_STAT */ 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct brcmstb_gpio_priv { 578c2ecf20Sopenharmony_ci struct list_head bank_list; 588c2ecf20Sopenharmony_ci void __iomem *reg_base; 598c2ecf20Sopenharmony_ci struct platform_device *pdev; 608c2ecf20Sopenharmony_ci struct irq_domain *irq_domain; 618c2ecf20Sopenharmony_ci struct irq_chip irq_chip; 628c2ecf20Sopenharmony_ci int parent_irq; 638c2ecf20Sopenharmony_ci int gpio_base; 648c2ecf20Sopenharmony_ci int num_gpios; 658c2ecf20Sopenharmony_ci int parent_wake_irq; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define MAX_GPIO_PER_BANK 32 698c2ecf20Sopenharmony_ci#define GPIO_BANK(gpio) ((gpio) >> 5) 708c2ecf20Sopenharmony_ci/* assumes MAX_GPIO_PER_BANK is a multiple of 2 */ 718c2ecf20Sopenharmony_ci#define GPIO_BIT(gpio) ((gpio) & (MAX_GPIO_PER_BANK - 1)) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic inline struct brcmstb_gpio_priv * 748c2ecf20Sopenharmony_cibrcmstb_gpio_gc_to_priv(struct gpio_chip *gc) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 778c2ecf20Sopenharmony_ci return bank->parent_priv; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic unsigned long 818c2ecf20Sopenharmony_ci__brcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci void __iomem *reg_base = bank->parent_priv->reg_base; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return bank->gc.read_reg(reg_base + GIO_STAT(bank->id)) & 868c2ecf20Sopenharmony_ci bank->gc.read_reg(reg_base + GIO_MASK(bank->id)); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic unsigned long 908c2ecf20Sopenharmony_cibrcmstb_gpio_get_active_irqs(struct brcmstb_gpio_bank *bank) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci unsigned long status; 938c2ecf20Sopenharmony_ci unsigned long flags; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci spin_lock_irqsave(&bank->gc.bgpio_lock, flags); 968c2ecf20Sopenharmony_ci status = __brcmstb_gpio_get_active_irqs(bank); 978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return status; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq, 1038c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci return hwirq - (bank->gc.base - bank->parent_priv->gpio_base); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank, 1098c2ecf20Sopenharmony_ci unsigned int hwirq, bool enable) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct gpio_chip *gc = &bank->gc; 1128c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 1138c2ecf20Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(hwirq, bank)); 1148c2ecf20Sopenharmony_ci u32 imask; 1158c2ecf20Sopenharmony_ci unsigned long flags; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci spin_lock_irqsave(&gc->bgpio_lock, flags); 1188c2ecf20Sopenharmony_ci imask = gc->read_reg(priv->reg_base + GIO_MASK(bank->id)); 1198c2ecf20Sopenharmony_ci if (enable) 1208c2ecf20Sopenharmony_ci imask |= mask; 1218c2ecf20Sopenharmony_ci else 1228c2ecf20Sopenharmony_ci imask &= ~mask; 1238c2ecf20Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_MASK(bank->id), imask); 1248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&gc->bgpio_lock, flags); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); 1308c2ecf20Sopenharmony_ci /* gc_offset is relative to this gpio_chip; want real offset */ 1318c2ecf20Sopenharmony_ci int hwirq = offset + (gc->base - priv->gpio_base); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (hwirq >= priv->num_gpios) 1348c2ecf20Sopenharmony_ci return -ENXIO; 1358c2ecf20Sopenharmony_ci return irq_create_mapping(priv->irq_domain, hwirq); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* -------------------- IRQ chip functions -------------------- */ 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void brcmstb_gpio_irq_mask(struct irq_data *d) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1438c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci brcmstb_gpio_set_imask(bank, d->hwirq, false); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void brcmstb_gpio_irq_unmask(struct irq_data *d) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1518c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci brcmstb_gpio_set_imask(bank, d->hwirq, true); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void brcmstb_gpio_irq_ack(struct irq_data *d) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1598c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 1608c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 1618c2ecf20Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_STAT(bank->id), mask); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1698c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 1708c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 1718c2ecf20Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); 1728c2ecf20Sopenharmony_ci u32 edge_insensitive, iedge_insensitive; 1738c2ecf20Sopenharmony_ci u32 edge_config, iedge_config; 1748c2ecf20Sopenharmony_ci u32 level, ilevel; 1758c2ecf20Sopenharmony_ci unsigned long flags; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci switch (type) { 1788c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 1798c2ecf20Sopenharmony_ci level = mask; 1808c2ecf20Sopenharmony_ci edge_config = 0; 1818c2ecf20Sopenharmony_ci edge_insensitive = 0; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 1848c2ecf20Sopenharmony_ci level = mask; 1858c2ecf20Sopenharmony_ci edge_config = mask; 1868c2ecf20Sopenharmony_ci edge_insensitive = 0; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 1898c2ecf20Sopenharmony_ci level = 0; 1908c2ecf20Sopenharmony_ci edge_config = 0; 1918c2ecf20Sopenharmony_ci edge_insensitive = 0; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 1948c2ecf20Sopenharmony_ci level = 0; 1958c2ecf20Sopenharmony_ci edge_config = mask; 1968c2ecf20Sopenharmony_ci edge_insensitive = 0; 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 1998c2ecf20Sopenharmony_ci level = 0; 2008c2ecf20Sopenharmony_ci edge_config = 0; /* don't care, but want known value */ 2018c2ecf20Sopenharmony_ci edge_insensitive = mask; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci default: 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci spin_lock_irqsave(&bank->gc.bgpio_lock, flags); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci iedge_config = bank->gc.read_reg(priv->reg_base + 2108c2ecf20Sopenharmony_ci GIO_EC(bank->id)) & ~mask; 2118c2ecf20Sopenharmony_ci iedge_insensitive = bank->gc.read_reg(priv->reg_base + 2128c2ecf20Sopenharmony_ci GIO_EI(bank->id)) & ~mask; 2138c2ecf20Sopenharmony_ci ilevel = bank->gc.read_reg(priv->reg_base + 2148c2ecf20Sopenharmony_ci GIO_LEVEL(bank->id)) & ~mask; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci bank->gc.write_reg(priv->reg_base + GIO_EC(bank->id), 2178c2ecf20Sopenharmony_ci iedge_config | edge_config); 2188c2ecf20Sopenharmony_ci bank->gc.write_reg(priv->reg_base + GIO_EI(bank->id), 2198c2ecf20Sopenharmony_ci iedge_insensitive | edge_insensitive); 2208c2ecf20Sopenharmony_ci bank->gc.write_reg(priv->reg_base + GIO_LEVEL(bank->id), 2218c2ecf20Sopenharmony_ci ilevel | level); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bank->gc.bgpio_lock, flags); 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int brcmstb_gpio_priv_set_wake(struct brcmstb_gpio_priv *priv, 2288c2ecf20Sopenharmony_ci unsigned int enable) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci int ret = 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (enable) 2338c2ecf20Sopenharmony_ci ret = enable_irq_wake(priv->parent_wake_irq); 2348c2ecf20Sopenharmony_ci else 2358c2ecf20Sopenharmony_ci ret = disable_irq_wake(priv->parent_wake_irq); 2368c2ecf20Sopenharmony_ci if (ret) 2378c2ecf20Sopenharmony_ci dev_err(&priv->pdev->dev, "failed to %s wake-up interrupt\n", 2388c2ecf20Sopenharmony_ci enable ? "enable" : "disable"); 2398c2ecf20Sopenharmony_ci return ret; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2458c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 2468c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 2478c2ecf20Sopenharmony_ci u32 mask = BIT(brcmstb_gpio_hwirq_to_offset(d->hwirq, bank)); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * Do not do anything specific for now, suspend/resume callbacks will 2518c2ecf20Sopenharmony_ci * configure the interrupt mask appropriately 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci if (enable) 2548c2ecf20Sopenharmony_ci bank->wake_active |= mask; 2558c2ecf20Sopenharmony_ci else 2568c2ecf20Sopenharmony_ci bank->wake_active &= ~mask; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return brcmstb_gpio_priv_set_wake(priv, enable); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic irqreturn_t brcmstb_gpio_wake_irq_handler(int irq, void *data) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = data; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!priv || irq != priv->parent_wake_irq) 2668c2ecf20Sopenharmony_ci return IRQ_NONE; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Nothing to do */ 2698c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void brcmstb_gpio_irq_bank_handler(struct brcmstb_gpio_bank *bank) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = bank->parent_priv; 2758c2ecf20Sopenharmony_ci struct irq_domain *domain = priv->irq_domain; 2768c2ecf20Sopenharmony_ci int hwbase = bank->gc.base - priv->gpio_base; 2778c2ecf20Sopenharmony_ci unsigned long status; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci while ((status = brcmstb_gpio_get_active_irqs(bank))) { 2808c2ecf20Sopenharmony_ci unsigned int irq, offset; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci for_each_set_bit(offset, &status, 32) { 2838c2ecf20Sopenharmony_ci if (offset >= bank->width) 2848c2ecf20Sopenharmony_ci dev_warn(&priv->pdev->dev, 2858c2ecf20Sopenharmony_ci "IRQ for invalid GPIO (bank=%d, offset=%d)\n", 2868c2ecf20Sopenharmony_ci bank->id, offset); 2878c2ecf20Sopenharmony_ci irq = irq_linear_revmap(domain, hwbase + offset); 2888c2ecf20Sopenharmony_ci generic_handle_irq(irq); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* Each UPG GIO block has one IRQ for all banks */ 2948c2ecf20Sopenharmony_cistatic void brcmstb_gpio_irq_handler(struct irq_desc *desc) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = irq_desc_get_handler_data(desc); 2978c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 2988c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Interrupts weren't properly cleared during probe */ 3018c2ecf20Sopenharmony_ci BUG_ON(!priv || !chip); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 3048c2ecf20Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) 3058c2ecf20Sopenharmony_ci brcmstb_gpio_irq_bank_handler(bank); 3068c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic struct brcmstb_gpio_bank *brcmstb_gpio_hwirq_to_bank( 3108c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv, irq_hw_number_t hwirq) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank; 3138c2ecf20Sopenharmony_ci int i = 0; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* banks are in descending order */ 3168c2ecf20Sopenharmony_ci list_for_each_entry_reverse(bank, &priv->bank_list, node) { 3178c2ecf20Sopenharmony_ci i += bank->gc.ngpio; 3188c2ecf20Sopenharmony_ci if (hwirq < i) 3198c2ecf20Sopenharmony_ci return bank; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci return NULL; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/* 3258c2ecf20Sopenharmony_ci * This lock class tells lockdep that GPIO irqs are in a different 3268c2ecf20Sopenharmony_ci * category than their parents, so it won't report false recursion. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_cistatic struct lock_class_key brcmstb_gpio_irq_lock_class; 3298c2ecf20Sopenharmony_cistatic struct lock_class_key brcmstb_gpio_irq_request_class; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int brcmstb_gpio_irq_map(struct irq_domain *d, unsigned int irq, 3338c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = d->host_data; 3368c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = 3378c2ecf20Sopenharmony_ci brcmstb_gpio_hwirq_to_bank(priv, hwirq); 3388c2ecf20Sopenharmony_ci struct platform_device *pdev = priv->pdev; 3398c2ecf20Sopenharmony_ci int ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (!bank) 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Mapping irq %d for gpio line %d (bank %d)\n", 3458c2ecf20Sopenharmony_ci irq, (int)hwirq, bank->id); 3468c2ecf20Sopenharmony_ci ret = irq_set_chip_data(irq, &bank->gc); 3478c2ecf20Sopenharmony_ci if (ret < 0) 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci irq_set_lockdep_class(irq, &brcmstb_gpio_irq_lock_class, 3508c2ecf20Sopenharmony_ci &brcmstb_gpio_irq_request_class); 3518c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &priv->irq_chip, handle_level_irq); 3528c2ecf20Sopenharmony_ci irq_set_noprobe(irq); 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void brcmstb_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, NULL, NULL); 3598c2ecf20Sopenharmony_ci irq_set_chip_data(irq, NULL); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic const struct irq_domain_ops brcmstb_gpio_irq_domain_ops = { 3638c2ecf20Sopenharmony_ci .map = brcmstb_gpio_irq_map, 3648c2ecf20Sopenharmony_ci .unmap = brcmstb_gpio_irq_unmap, 3658c2ecf20Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* Make sure that the number of banks matches up between properties */ 3698c2ecf20Sopenharmony_cistatic int brcmstb_gpio_sanity_check_banks(struct device *dev, 3708c2ecf20Sopenharmony_ci struct device_node *np, struct resource *res) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int res_num_banks = resource_size(res) / GIO_BANK_SIZE; 3738c2ecf20Sopenharmony_ci int num_banks = 3748c2ecf20Sopenharmony_ci of_property_count_u32_elems(np, "brcm,gpio-bank-widths"); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (res_num_banks != num_banks) { 3778c2ecf20Sopenharmony_ci dev_err(dev, "Mismatch in banks: res had %d, bank-widths had %d\n", 3788c2ecf20Sopenharmony_ci res_num_banks, num_banks); 3798c2ecf20Sopenharmony_ci return -EINVAL; 3808c2ecf20Sopenharmony_ci } else { 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int brcmstb_gpio_remove(struct platform_device *pdev) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev); 3888c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank; 3898c2ecf20Sopenharmony_ci int offset, ret = 0, virq; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!priv) { 3928c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "called %s without drvdata!\n", __func__); 3938c2ecf20Sopenharmony_ci return -EFAULT; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (priv->parent_irq > 0) 3978c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(priv->parent_irq, NULL, NULL); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* Remove all IRQ mappings and delete the domain */ 4008c2ecf20Sopenharmony_ci if (priv->irq_domain) { 4018c2ecf20Sopenharmony_ci for (offset = 0; offset < priv->num_gpios; offset++) { 4028c2ecf20Sopenharmony_ci virq = irq_find_mapping(priv->irq_domain, offset); 4038c2ecf20Sopenharmony_ci irq_dispose_mapping(virq); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci irq_domain_remove(priv->irq_domain); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * You can lose return values below, but we report all errors, and it's 4108c2ecf20Sopenharmony_ci * more important to actually perform all of the steps. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) 4138c2ecf20Sopenharmony_ci gpiochip_remove(&bank->gc); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int brcmstb_gpio_of_xlate(struct gpio_chip *gc, 4198c2ecf20Sopenharmony_ci const struct of_phandle_args *gpiospec, u32 *flags) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc); 4228c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc); 4238c2ecf20Sopenharmony_ci int offset; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (gc->of_gpio_n_cells != 2) { 4268c2ecf20Sopenharmony_ci WARN_ON(1); 4278c2ecf20Sopenharmony_ci return -EINVAL; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) 4318c2ecf20Sopenharmony_ci return -EINVAL; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci offset = gpiospec->args[0] - (gc->base - priv->gpio_base); 4348c2ecf20Sopenharmony_ci if (offset >= gc->ngpio || offset < 0) 4358c2ecf20Sopenharmony_ci return -EINVAL; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (unlikely(offset >= bank->width)) { 4388c2ecf20Sopenharmony_ci dev_warn_ratelimited(&priv->pdev->dev, 4398c2ecf20Sopenharmony_ci "Received request for invalid GPIO offset %d\n", 4408c2ecf20Sopenharmony_ci gpiospec->args[0]); 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (flags) 4448c2ecf20Sopenharmony_ci *flags = gpiospec->args[1]; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return offset; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* priv->parent_irq and priv->num_gpios must be set before calling */ 4508c2ecf20Sopenharmony_cistatic int brcmstb_gpio_irq_setup(struct platform_device *pdev, 4518c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 4548c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 4558c2ecf20Sopenharmony_ci int err; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci priv->irq_domain = 4588c2ecf20Sopenharmony_ci irq_domain_add_linear(np, priv->num_gpios, 4598c2ecf20Sopenharmony_ci &brcmstb_gpio_irq_domain_ops, 4608c2ecf20Sopenharmony_ci priv); 4618c2ecf20Sopenharmony_ci if (!priv->irq_domain) { 4628c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't allocate IRQ domain\n"); 4638c2ecf20Sopenharmony_ci return -ENXIO; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "wakeup-source")) { 4678c2ecf20Sopenharmony_ci priv->parent_wake_irq = platform_get_irq(pdev, 1); 4688c2ecf20Sopenharmony_ci if (priv->parent_wake_irq < 0) { 4698c2ecf20Sopenharmony_ci priv->parent_wake_irq = 0; 4708c2ecf20Sopenharmony_ci dev_warn(dev, 4718c2ecf20Sopenharmony_ci "Couldn't get wake IRQ - GPIOs will not be able to wake from sleep"); 4728c2ecf20Sopenharmony_ci } else { 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * Set wakeup capability so we can process boot-time 4758c2ecf20Sopenharmony_ci * "wakeups" (e.g., from S5 cold boot) 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, true); 4788c2ecf20Sopenharmony_ci device_wakeup_enable(dev); 4798c2ecf20Sopenharmony_ci err = devm_request_irq(dev, priv->parent_wake_irq, 4808c2ecf20Sopenharmony_ci brcmstb_gpio_wake_irq_handler, 4818c2ecf20Sopenharmony_ci IRQF_SHARED, 4828c2ecf20Sopenharmony_ci "brcmstb-gpio-wake", priv); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (err < 0) { 4858c2ecf20Sopenharmony_ci dev_err(dev, "Couldn't request wake IRQ"); 4868c2ecf20Sopenharmony_ci goto out_free_domain; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci priv->irq_chip.name = dev_name(dev); 4928c2ecf20Sopenharmony_ci priv->irq_chip.irq_disable = brcmstb_gpio_irq_mask; 4938c2ecf20Sopenharmony_ci priv->irq_chip.irq_mask = brcmstb_gpio_irq_mask; 4948c2ecf20Sopenharmony_ci priv->irq_chip.irq_unmask = brcmstb_gpio_irq_unmask; 4958c2ecf20Sopenharmony_ci priv->irq_chip.irq_ack = brcmstb_gpio_irq_ack; 4968c2ecf20Sopenharmony_ci priv->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (priv->parent_wake_irq) 4998c2ecf20Sopenharmony_ci priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(priv->parent_irq, 5028c2ecf20Sopenharmony_ci brcmstb_gpio_irq_handler, priv); 5038c2ecf20Sopenharmony_ci irq_set_status_flags(priv->parent_irq, IRQ_DISABLE_UNLAZY); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ciout_free_domain: 5088c2ecf20Sopenharmony_ci irq_domain_remove(priv->irq_domain); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return err; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void brcmstb_gpio_bank_save(struct brcmstb_gpio_priv *priv, 5148c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci struct gpio_chip *gc = &bank->gc; 5178c2ecf20Sopenharmony_ci unsigned int i; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci for (i = 0; i < GIO_REG_STAT; i++) 5208c2ecf20Sopenharmony_ci bank->saved_regs[i] = gc->read_reg(priv->reg_base + 5218c2ecf20Sopenharmony_ci GIO_BANK_OFF(bank->id, i)); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic void brcmstb_gpio_quiesce(struct device *dev, bool save) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); 5278c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank; 5288c2ecf20Sopenharmony_ci struct gpio_chip *gc; 5298c2ecf20Sopenharmony_ci u32 imask; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* disable non-wake interrupt */ 5328c2ecf20Sopenharmony_ci if (priv->parent_irq >= 0) 5338c2ecf20Sopenharmony_ci disable_irq(priv->parent_irq); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) { 5368c2ecf20Sopenharmony_ci gc = &bank->gc; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (save) 5398c2ecf20Sopenharmony_ci brcmstb_gpio_bank_save(priv, bank); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* Unmask GPIOs which have been flagged as wake-up sources */ 5428c2ecf20Sopenharmony_ci if (priv->parent_wake_irq) 5438c2ecf20Sopenharmony_ci imask = bank->wake_active; 5448c2ecf20Sopenharmony_ci else 5458c2ecf20Sopenharmony_ci imask = 0; 5468c2ecf20Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_MASK(bank->id), 5478c2ecf20Sopenharmony_ci imask); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic void brcmstb_gpio_shutdown(struct platform_device *pdev) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci /* Enable GPIO for S5 cold boot */ 5548c2ecf20Sopenharmony_ci brcmstb_gpio_quiesce(&pdev->dev, false); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5588c2ecf20Sopenharmony_cistatic void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv, 5598c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct gpio_chip *gc = &bank->gc; 5628c2ecf20Sopenharmony_ci unsigned int i; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci for (i = 0; i < GIO_REG_STAT; i++) 5658c2ecf20Sopenharmony_ci gc->write_reg(priv->reg_base + GIO_BANK_OFF(bank->id, i), 5668c2ecf20Sopenharmony_ci bank->saved_regs[i]); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic int brcmstb_gpio_suspend(struct device *dev) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci brcmstb_gpio_quiesce(dev, true); 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int brcmstb_gpio_resume(struct device *dev) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev); 5788c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank; 5798c2ecf20Sopenharmony_ci bool need_wakeup_event = false; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci list_for_each_entry(bank, &priv->bank_list, node) { 5828c2ecf20Sopenharmony_ci need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank); 5838c2ecf20Sopenharmony_ci brcmstb_gpio_bank_restore(priv, bank); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (priv->parent_wake_irq && need_wakeup_event) 5878c2ecf20Sopenharmony_ci pm_wakeup_event(dev, 0); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* enable non-wake interrupt */ 5908c2ecf20Sopenharmony_ci if (priv->parent_irq >= 0) 5918c2ecf20Sopenharmony_ci enable_irq(priv->parent_irq); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci#else 5978c2ecf20Sopenharmony_ci#define brcmstb_gpio_suspend NULL 5988c2ecf20Sopenharmony_ci#define brcmstb_gpio_resume NULL 5998c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic const struct dev_pm_ops brcmstb_gpio_pm_ops = { 6028c2ecf20Sopenharmony_ci .suspend_noirq = brcmstb_gpio_suspend, 6038c2ecf20Sopenharmony_ci .resume_noirq = brcmstb_gpio_resume, 6048c2ecf20Sopenharmony_ci}; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic void brcmstb_gpio_set_names(struct device *dev, 6078c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 6108c2ecf20Sopenharmony_ci const char **names; 6118c2ecf20Sopenharmony_ci int nstrings, base; 6128c2ecf20Sopenharmony_ci unsigned int i; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci base = bank->id * MAX_GPIO_PER_BANK; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci nstrings = of_property_count_strings(np, "gpio-line-names"); 6178c2ecf20Sopenharmony_ci if (nstrings <= base) 6188c2ecf20Sopenharmony_ci /* Line names not present */ 6198c2ecf20Sopenharmony_ci return; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci names = devm_kcalloc(dev, MAX_GPIO_PER_BANK, sizeof(*names), 6228c2ecf20Sopenharmony_ci GFP_KERNEL); 6238c2ecf20Sopenharmony_ci if (!names) 6248c2ecf20Sopenharmony_ci return; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* 6278c2ecf20Sopenharmony_ci * Make sure to not index beyond the end of the number of descriptors 6288c2ecf20Sopenharmony_ci * of the GPIO device. 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci for (i = 0; i < bank->width; i++) { 6318c2ecf20Sopenharmony_ci const char *name; 6328c2ecf20Sopenharmony_ci int ret; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci ret = of_property_read_string_index(np, "gpio-line-names", 6358c2ecf20Sopenharmony_ci base + i, &name); 6368c2ecf20Sopenharmony_ci if (ret) { 6378c2ecf20Sopenharmony_ci if (ret != -ENODATA) 6388c2ecf20Sopenharmony_ci dev_err(dev, "unable to name line %d: %d\n", 6398c2ecf20Sopenharmony_ci base + i, ret); 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci if (*name) 6438c2ecf20Sopenharmony_ci names[i] = name; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci bank->gc.names = names; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic int brcmstb_gpio_probe(struct platform_device *pdev) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 6528c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 6538c2ecf20Sopenharmony_ci void __iomem *reg_base; 6548c2ecf20Sopenharmony_ci struct brcmstb_gpio_priv *priv; 6558c2ecf20Sopenharmony_ci struct resource *res; 6568c2ecf20Sopenharmony_ci struct property *prop; 6578c2ecf20Sopenharmony_ci const __be32 *p; 6588c2ecf20Sopenharmony_ci u32 bank_width; 6598c2ecf20Sopenharmony_ci int num_banks = 0; 6608c2ecf20Sopenharmony_ci int err; 6618c2ecf20Sopenharmony_ci static int gpio_base; 6628c2ecf20Sopenharmony_ci unsigned long flags = 0; 6638c2ecf20Sopenharmony_ci bool need_wakeup_event = false; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 6668c2ecf20Sopenharmony_ci if (!priv) 6678c2ecf20Sopenharmony_ci return -ENOMEM; 6688c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 6698c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->bank_list); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6728c2ecf20Sopenharmony_ci reg_base = devm_ioremap_resource(dev, res); 6738c2ecf20Sopenharmony_ci if (IS_ERR(reg_base)) 6748c2ecf20Sopenharmony_ci return PTR_ERR(reg_base); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci priv->gpio_base = gpio_base; 6778c2ecf20Sopenharmony_ci priv->reg_base = reg_base; 6788c2ecf20Sopenharmony_ci priv->pdev = pdev; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "interrupt-controller")) { 6818c2ecf20Sopenharmony_ci priv->parent_irq = platform_get_irq(pdev, 0); 6828c2ecf20Sopenharmony_ci if (priv->parent_irq <= 0) 6838c2ecf20Sopenharmony_ci return -ENOENT; 6848c2ecf20Sopenharmony_ci } else { 6858c2ecf20Sopenharmony_ci priv->parent_irq = -ENOENT; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (brcmstb_gpio_sanity_check_banks(dev, np, res)) 6898c2ecf20Sopenharmony_ci return -EINVAL; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* 6928c2ecf20Sopenharmony_ci * MIPS endianness is configured by boot strap, which also reverses all 6938c2ecf20Sopenharmony_ci * bus endianness (i.e., big-endian CPU + big endian bus ==> native 6948c2ecf20Sopenharmony_ci * endian I/O). 6958c2ecf20Sopenharmony_ci * 6968c2ecf20Sopenharmony_ci * Other architectures (e.g., ARM) either do not support big endian, or 6978c2ecf20Sopenharmony_ci * else leave I/O in little endian mode. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci#if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN) 7008c2ecf20Sopenharmony_ci flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER; 7018c2ecf20Sopenharmony_ci#endif 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p, 7048c2ecf20Sopenharmony_ci bank_width) { 7058c2ecf20Sopenharmony_ci struct brcmstb_gpio_bank *bank; 7068c2ecf20Sopenharmony_ci struct gpio_chip *gc; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* 7098c2ecf20Sopenharmony_ci * If bank_width is 0, then there is an empty bank in the 7108c2ecf20Sopenharmony_ci * register block. Special handling for this case. 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci if (bank_width == 0) { 7138c2ecf20Sopenharmony_ci dev_dbg(dev, "Width 0 found: Empty bank @ %d\n", 7148c2ecf20Sopenharmony_ci num_banks); 7158c2ecf20Sopenharmony_ci num_banks++; 7168c2ecf20Sopenharmony_ci gpio_base += MAX_GPIO_PER_BANK; 7178c2ecf20Sopenharmony_ci continue; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL); 7218c2ecf20Sopenharmony_ci if (!bank) { 7228c2ecf20Sopenharmony_ci err = -ENOMEM; 7238c2ecf20Sopenharmony_ci goto fail; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci bank->parent_priv = priv; 7278c2ecf20Sopenharmony_ci bank->id = num_banks; 7288c2ecf20Sopenharmony_ci if (bank_width <= 0 || bank_width > MAX_GPIO_PER_BANK) { 7298c2ecf20Sopenharmony_ci dev_err(dev, "Invalid bank width %d\n", bank_width); 7308c2ecf20Sopenharmony_ci err = -EINVAL; 7318c2ecf20Sopenharmony_ci goto fail; 7328c2ecf20Sopenharmony_ci } else { 7338c2ecf20Sopenharmony_ci bank->width = bank_width; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* 7378c2ecf20Sopenharmony_ci * Regs are 4 bytes wide, have data reg, no set/clear regs, 7388c2ecf20Sopenharmony_ci * and direction bits have 0 = output and 1 = input 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_ci gc = &bank->gc; 7418c2ecf20Sopenharmony_ci err = bgpio_init(gc, dev, 4, 7428c2ecf20Sopenharmony_ci reg_base + GIO_DATA(bank->id), 7438c2ecf20Sopenharmony_ci NULL, NULL, NULL, 7448c2ecf20Sopenharmony_ci reg_base + GIO_IODIR(bank->id), flags); 7458c2ecf20Sopenharmony_ci if (err) { 7468c2ecf20Sopenharmony_ci dev_err(dev, "bgpio_init() failed\n"); 7478c2ecf20Sopenharmony_ci goto fail; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci gc->of_node = np; 7518c2ecf20Sopenharmony_ci gc->owner = THIS_MODULE; 7528c2ecf20Sopenharmony_ci gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", dev->of_node); 7538c2ecf20Sopenharmony_ci if (!gc->label) { 7548c2ecf20Sopenharmony_ci err = -ENOMEM; 7558c2ecf20Sopenharmony_ci goto fail; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci gc->base = gpio_base; 7588c2ecf20Sopenharmony_ci gc->of_gpio_n_cells = 2; 7598c2ecf20Sopenharmony_ci gc->of_xlate = brcmstb_gpio_of_xlate; 7608c2ecf20Sopenharmony_ci /* not all ngpio lines are valid, will use bank width later */ 7618c2ecf20Sopenharmony_ci gc->ngpio = MAX_GPIO_PER_BANK; 7628c2ecf20Sopenharmony_ci if (priv->parent_irq > 0) 7638c2ecf20Sopenharmony_ci gc->to_irq = brcmstb_gpio_to_irq; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* 7668c2ecf20Sopenharmony_ci * Mask all interrupts by default, since wakeup interrupts may 7678c2ecf20Sopenharmony_ci * be retained from S5 cold boot 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_ci need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank); 7708c2ecf20Sopenharmony_ci gc->write_reg(reg_base + GIO_MASK(bank->id), 0); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci brcmstb_gpio_set_names(dev, bank); 7738c2ecf20Sopenharmony_ci err = gpiochip_add_data(gc, bank); 7748c2ecf20Sopenharmony_ci if (err) { 7758c2ecf20Sopenharmony_ci dev_err(dev, "Could not add gpiochip for bank %d\n", 7768c2ecf20Sopenharmony_ci bank->id); 7778c2ecf20Sopenharmony_ci goto fail; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci gpio_base += gc->ngpio; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id, 7828c2ecf20Sopenharmony_ci gc->base, gc->ngpio, bank->width); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* Everything looks good, so add bank to list */ 7858c2ecf20Sopenharmony_ci list_add(&bank->node, &priv->bank_list); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci num_banks++; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci priv->num_gpios = gpio_base - priv->gpio_base; 7918c2ecf20Sopenharmony_ci if (priv->parent_irq > 0) { 7928c2ecf20Sopenharmony_ci err = brcmstb_gpio_irq_setup(pdev, priv); 7938c2ecf20Sopenharmony_ci if (err) 7948c2ecf20Sopenharmony_ci goto fail; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (priv->parent_wake_irq && need_wakeup_event) 7988c2ecf20Sopenharmony_ci pm_wakeup_event(dev, 0); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cifail: 8038c2ecf20Sopenharmony_ci (void) brcmstb_gpio_remove(pdev); 8048c2ecf20Sopenharmony_ci return err; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic const struct of_device_id brcmstb_gpio_of_match[] = { 8088c2ecf20Sopenharmony_ci { .compatible = "brcm,brcmstb-gpio" }, 8098c2ecf20Sopenharmony_ci {}, 8108c2ecf20Sopenharmony_ci}; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcmstb_gpio_of_match); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic struct platform_driver brcmstb_gpio_driver = { 8158c2ecf20Sopenharmony_ci .driver = { 8168c2ecf20Sopenharmony_ci .name = "brcmstb-gpio", 8178c2ecf20Sopenharmony_ci .of_match_table = brcmstb_gpio_of_match, 8188c2ecf20Sopenharmony_ci .pm = &brcmstb_gpio_pm_ops, 8198c2ecf20Sopenharmony_ci }, 8208c2ecf20Sopenharmony_ci .probe = brcmstb_gpio_probe, 8218c2ecf20Sopenharmony_ci .remove = brcmstb_gpio_remove, 8228c2ecf20Sopenharmony_ci .shutdown = brcmstb_gpio_shutdown, 8238c2ecf20Sopenharmony_ci}; 8248c2ecf20Sopenharmony_cimodule_platform_driver(brcmstb_gpio_driver); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gregory Fong"); 8278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Broadcom BRCMSTB SoC UPG GPIO"); 8288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 829