18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2008, 2009 Provigent Ltd. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Baruch Siach <baruch@tkos.co.il> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Data sheet: ARM DDI 0190B, September 2000 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/ioport.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/irq.h> 188c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/bitops.h> 218c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 228c2ecf20Sopenharmony_ci#include <linux/device.h> 238c2ecf20Sopenharmony_ci#include <linux/amba/bus.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 268c2ecf20Sopenharmony_ci#include <linux/pm.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define GPIODIR 0x400 298c2ecf20Sopenharmony_ci#define GPIOIS 0x404 308c2ecf20Sopenharmony_ci#define GPIOIBE 0x408 318c2ecf20Sopenharmony_ci#define GPIOIEV 0x40C 328c2ecf20Sopenharmony_ci#define GPIOIE 0x410 338c2ecf20Sopenharmony_ci#define GPIORIS 0x414 348c2ecf20Sopenharmony_ci#define GPIOMIS 0x418 358c2ecf20Sopenharmony_ci#define GPIOIC 0x41C 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define PL061_GPIO_NR 8 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 408c2ecf20Sopenharmony_cistruct pl061_context_save_regs { 418c2ecf20Sopenharmony_ci u8 gpio_data; 428c2ecf20Sopenharmony_ci u8 gpio_dir; 438c2ecf20Sopenharmony_ci u8 gpio_is; 448c2ecf20Sopenharmony_ci u8 gpio_ibe; 458c2ecf20Sopenharmony_ci u8 gpio_iev; 468c2ecf20Sopenharmony_ci u8 gpio_ie; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct pl061 { 518c2ecf20Sopenharmony_ci raw_spinlock_t lock; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci void __iomem *base; 548c2ecf20Sopenharmony_ci struct gpio_chip gc; 558c2ecf20Sopenharmony_ci struct irq_chip irq_chip; 568c2ecf20Sopenharmony_ci int parent_irq; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 598c2ecf20Sopenharmony_ci struct pl061_context_save_regs csave_regs; 608c2ecf20Sopenharmony_ci#endif 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int pl061_get_direction(struct gpio_chip *gc, unsigned offset) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (readb(pl061->base + GPIODIR) & BIT(offset)) 688c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_OUT; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return GPIO_LINE_DIRECTION_IN; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int pl061_direction_input(struct gpio_chip *gc, unsigned offset) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci unsigned char gpiodir; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pl061->lock, flags); 808c2ecf20Sopenharmony_ci gpiodir = readb(pl061->base + GPIODIR); 818c2ecf20Sopenharmony_ci gpiodir &= ~(BIT(offset)); 828c2ecf20Sopenharmony_ci writeb(gpiodir, pl061->base + GPIODIR); 838c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pl061->lock, flags); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int pl061_direction_output(struct gpio_chip *gc, unsigned offset, 898c2ecf20Sopenharmony_ci int value) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 928c2ecf20Sopenharmony_ci unsigned long flags; 938c2ecf20Sopenharmony_ci unsigned char gpiodir; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pl061->lock, flags); 968c2ecf20Sopenharmony_ci writeb(!!value << offset, pl061->base + (BIT(offset + 2))); 978c2ecf20Sopenharmony_ci gpiodir = readb(pl061->base + GPIODIR); 988c2ecf20Sopenharmony_ci gpiodir |= BIT(offset); 998c2ecf20Sopenharmony_ci writeb(gpiodir, pl061->base + GPIODIR); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * gpio value is set again, because pl061 doesn't allow to set value of 1038c2ecf20Sopenharmony_ci * a gpio pin before configuring it in OUT mode. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci writeb(!!value << offset, pl061->base + (BIT(offset + 2))); 1068c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pl061->lock, flags); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int pl061_get_value(struct gpio_chip *gc, unsigned offset) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return !!readb(pl061->base + (BIT(offset + 2))); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci writeb(!!value << offset, pl061->base + (BIT(offset + 2))); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int pl061_irq_type(struct irq_data *d, unsigned trigger) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 1288c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 1298c2ecf20Sopenharmony_ci int offset = irqd_to_hwirq(d); 1308c2ecf20Sopenharmony_ci unsigned long flags; 1318c2ecf20Sopenharmony_ci u8 gpiois, gpioibe, gpioiev; 1328c2ecf20Sopenharmony_ci u8 bit = BIT(offset); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (offset < 0 || offset >= PL061_GPIO_NR) 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) && 1388c2ecf20Sopenharmony_ci (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))) 1398c2ecf20Sopenharmony_ci { 1408c2ecf20Sopenharmony_ci dev_err(gc->parent, 1418c2ecf20Sopenharmony_ci "trying to configure line %d for both level and edge " 1428c2ecf20Sopenharmony_ci "detection, choose one!\n", 1438c2ecf20Sopenharmony_ci offset); 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&pl061->lock, flags); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci gpioiev = readb(pl061->base + GPIOIEV); 1518c2ecf20Sopenharmony_ci gpiois = readb(pl061->base + GPIOIS); 1528c2ecf20Sopenharmony_ci gpioibe = readb(pl061->base + GPIOIBE); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { 1558c2ecf20Sopenharmony_ci bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Disable edge detection */ 1588c2ecf20Sopenharmony_ci gpioibe &= ~bit; 1598c2ecf20Sopenharmony_ci /* Enable level detection */ 1608c2ecf20Sopenharmony_ci gpiois |= bit; 1618c2ecf20Sopenharmony_ci /* Select polarity */ 1628c2ecf20Sopenharmony_ci if (polarity) 1638c2ecf20Sopenharmony_ci gpioiev |= bit; 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci gpioiev &= ~bit; 1668c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 1678c2ecf20Sopenharmony_ci dev_dbg(gc->parent, "line %d: IRQ on %s level\n", 1688c2ecf20Sopenharmony_ci offset, 1698c2ecf20Sopenharmony_ci polarity ? "HIGH" : "LOW"); 1708c2ecf20Sopenharmony_ci } else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { 1718c2ecf20Sopenharmony_ci /* Disable level detection */ 1728c2ecf20Sopenharmony_ci gpiois &= ~bit; 1738c2ecf20Sopenharmony_ci /* Select both edges, setting this makes GPIOEV be ignored */ 1748c2ecf20Sopenharmony_ci gpioibe |= bit; 1758c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 1768c2ecf20Sopenharmony_ci dev_dbg(gc->parent, "line %d: IRQ on both edges\n", offset); 1778c2ecf20Sopenharmony_ci } else if ((trigger & IRQ_TYPE_EDGE_RISING) || 1788c2ecf20Sopenharmony_ci (trigger & IRQ_TYPE_EDGE_FALLING)) { 1798c2ecf20Sopenharmony_ci bool rising = trigger & IRQ_TYPE_EDGE_RISING; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* Disable level detection */ 1828c2ecf20Sopenharmony_ci gpiois &= ~bit; 1838c2ecf20Sopenharmony_ci /* Clear detection on both edges */ 1848c2ecf20Sopenharmony_ci gpioibe &= ~bit; 1858c2ecf20Sopenharmony_ci /* Select edge */ 1868c2ecf20Sopenharmony_ci if (rising) 1878c2ecf20Sopenharmony_ci gpioiev |= bit; 1888c2ecf20Sopenharmony_ci else 1898c2ecf20Sopenharmony_ci gpioiev &= ~bit; 1908c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_edge_irq); 1918c2ecf20Sopenharmony_ci dev_dbg(gc->parent, "line %d: IRQ on %s edge\n", 1928c2ecf20Sopenharmony_ci offset, 1938c2ecf20Sopenharmony_ci rising ? "RISING" : "FALLING"); 1948c2ecf20Sopenharmony_ci } else { 1958c2ecf20Sopenharmony_ci /* No trigger: disable everything */ 1968c2ecf20Sopenharmony_ci gpiois &= ~bit; 1978c2ecf20Sopenharmony_ci gpioibe &= ~bit; 1988c2ecf20Sopenharmony_ci gpioiev &= ~bit; 1998c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_bad_irq); 2008c2ecf20Sopenharmony_ci dev_warn(gc->parent, "no trigger selected for line %d\n", 2018c2ecf20Sopenharmony_ci offset); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci writeb(gpiois, pl061->base + GPIOIS); 2058c2ecf20Sopenharmony_ci writeb(gpioibe, pl061->base + GPIOIBE); 2068c2ecf20Sopenharmony_ci writeb(gpioiev, pl061->base + GPIOIEV); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&pl061->lock, flags); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void pl061_irq_handler(struct irq_desc *desc) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci unsigned long pending; 2168c2ecf20Sopenharmony_ci int offset; 2178c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_desc_get_handler_data(desc); 2188c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 2198c2ecf20Sopenharmony_ci struct irq_chip *irqchip = irq_desc_get_chip(desc); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci chained_irq_enter(irqchip, desc); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci pending = readb(pl061->base + GPIOMIS); 2248c2ecf20Sopenharmony_ci if (pending) { 2258c2ecf20Sopenharmony_ci for_each_set_bit(offset, &pending, PL061_GPIO_NR) 2268c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(gc->irq.domain, 2278c2ecf20Sopenharmony_ci offset)); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci chained_irq_exit(irqchip, desc); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void pl061_irq_mask(struct irq_data *d) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2368c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 2378c2ecf20Sopenharmony_ci u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); 2388c2ecf20Sopenharmony_ci u8 gpioie; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci raw_spin_lock(&pl061->lock); 2418c2ecf20Sopenharmony_ci gpioie = readb(pl061->base + GPIOIE) & ~mask; 2428c2ecf20Sopenharmony_ci writeb(gpioie, pl061->base + GPIOIE); 2438c2ecf20Sopenharmony_ci raw_spin_unlock(&pl061->lock); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void pl061_irq_unmask(struct irq_data *d) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2498c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 2508c2ecf20Sopenharmony_ci u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); 2518c2ecf20Sopenharmony_ci u8 gpioie; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci raw_spin_lock(&pl061->lock); 2548c2ecf20Sopenharmony_ci gpioie = readb(pl061->base + GPIOIE) | mask; 2558c2ecf20Sopenharmony_ci writeb(gpioie, pl061->base + GPIOIE); 2568c2ecf20Sopenharmony_ci raw_spin_unlock(&pl061->lock); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/** 2608c2ecf20Sopenharmony_ci * pl061_irq_ack() - ACK an edge IRQ 2618c2ecf20Sopenharmony_ci * @d: IRQ data for this IRQ 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * This gets called from the edge IRQ handler to ACK the edge IRQ 2648c2ecf20Sopenharmony_ci * in the GPIOIC (interrupt-clear) register. For level IRQs this is 2658c2ecf20Sopenharmony_ci * not needed: these go away when the level signal goes away. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_cistatic void pl061_irq_ack(struct irq_data *d) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2708c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 2718c2ecf20Sopenharmony_ci u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci raw_spin_lock(&pl061->lock); 2748c2ecf20Sopenharmony_ci writeb(mask, pl061->base + GPIOIC); 2758c2ecf20Sopenharmony_ci raw_spin_unlock(&pl061->lock); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int pl061_irq_set_wake(struct irq_data *d, unsigned int state) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct gpio_chip *gc = irq_data_get_irq_chip_data(d); 2818c2ecf20Sopenharmony_ci struct pl061 *pl061 = gpiochip_get_data(gc); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return irq_set_irq_wake(pl061->parent_irq, state); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int pl061_probe(struct amba_device *adev, const struct amba_id *id) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct device *dev = &adev->dev; 2898c2ecf20Sopenharmony_ci struct pl061 *pl061; 2908c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 2918c2ecf20Sopenharmony_ci int ret, irq; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL); 2948c2ecf20Sopenharmony_ci if (pl061 == NULL) 2958c2ecf20Sopenharmony_ci return -ENOMEM; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci pl061->base = devm_ioremap_resource(dev, &adev->res); 2988c2ecf20Sopenharmony_ci if (IS_ERR(pl061->base)) 2998c2ecf20Sopenharmony_ci return PTR_ERR(pl061->base); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci raw_spin_lock_init(&pl061->lock); 3028c2ecf20Sopenharmony_ci pl061->gc.request = gpiochip_generic_request; 3038c2ecf20Sopenharmony_ci pl061->gc.free = gpiochip_generic_free; 3048c2ecf20Sopenharmony_ci pl061->gc.base = -1; 3058c2ecf20Sopenharmony_ci pl061->gc.get_direction = pl061_get_direction; 3068c2ecf20Sopenharmony_ci pl061->gc.direction_input = pl061_direction_input; 3078c2ecf20Sopenharmony_ci pl061->gc.direction_output = pl061_direction_output; 3088c2ecf20Sopenharmony_ci pl061->gc.get = pl061_get_value; 3098c2ecf20Sopenharmony_ci pl061->gc.set = pl061_set_value; 3108c2ecf20Sopenharmony_ci pl061->gc.ngpio = PL061_GPIO_NR; 3118c2ecf20Sopenharmony_ci pl061->gc.label = dev_name(dev); 3128c2ecf20Sopenharmony_ci pl061->gc.parent = dev; 3138c2ecf20Sopenharmony_ci pl061->gc.owner = THIS_MODULE; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* 3168c2ecf20Sopenharmony_ci * irq_chip support 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci pl061->irq_chip.name = dev_name(dev); 3198c2ecf20Sopenharmony_ci pl061->irq_chip.irq_ack = pl061_irq_ack; 3208c2ecf20Sopenharmony_ci pl061->irq_chip.irq_mask = pl061_irq_mask; 3218c2ecf20Sopenharmony_ci pl061->irq_chip.irq_unmask = pl061_irq_unmask; 3228c2ecf20Sopenharmony_ci pl061->irq_chip.irq_set_type = pl061_irq_type; 3238c2ecf20Sopenharmony_ci pl061->irq_chip.irq_set_wake = pl061_irq_set_wake; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci writeb(0, pl061->base + GPIOIE); /* disable irqs */ 3268c2ecf20Sopenharmony_ci irq = adev->irq[0]; 3278c2ecf20Sopenharmony_ci if (!irq) 3288c2ecf20Sopenharmony_ci dev_warn(&adev->dev, "IRQ support disabled\n"); 3298c2ecf20Sopenharmony_ci pl061->parent_irq = irq; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci girq = &pl061->gc.irq; 3328c2ecf20Sopenharmony_ci girq->chip = &pl061->irq_chip; 3338c2ecf20Sopenharmony_ci girq->parent_handler = pl061_irq_handler; 3348c2ecf20Sopenharmony_ci girq->num_parents = 1; 3358c2ecf20Sopenharmony_ci girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), 3368c2ecf20Sopenharmony_ci GFP_KERNEL); 3378c2ecf20Sopenharmony_ci if (!girq->parents) 3388c2ecf20Sopenharmony_ci return -ENOMEM; 3398c2ecf20Sopenharmony_ci girq->parents[0] = irq; 3408c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 3418c2ecf20Sopenharmony_ci girq->handler = handle_bad_irq; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061); 3448c2ecf20Sopenharmony_ci if (ret) 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci amba_set_drvdata(adev, pl061); 3488c2ecf20Sopenharmony_ci dev_info(dev, "PL061 GPIO chip registered\n"); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3548c2ecf20Sopenharmony_cistatic int pl061_suspend(struct device *dev) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct pl061 *pl061 = dev_get_drvdata(dev); 3578c2ecf20Sopenharmony_ci int offset; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_data = 0; 3608c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR); 3618c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS); 3628c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE); 3638c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV); 3648c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (offset = 0; offset < PL061_GPIO_NR; offset++) { 3678c2ecf20Sopenharmony_ci if (pl061->csave_regs.gpio_dir & (BIT(offset))) 3688c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_data |= 3698c2ecf20Sopenharmony_ci pl061_get_value(&pl061->gc, offset) << offset; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int pl061_resume(struct device *dev) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct pl061 *pl061 = dev_get_drvdata(dev); 3788c2ecf20Sopenharmony_ci int offset; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci for (offset = 0; offset < PL061_GPIO_NR; offset++) { 3818c2ecf20Sopenharmony_ci if (pl061->csave_regs.gpio_dir & (BIT(offset))) 3828c2ecf20Sopenharmony_ci pl061_direction_output(&pl061->gc, offset, 3838c2ecf20Sopenharmony_ci pl061->csave_regs.gpio_data & 3848c2ecf20Sopenharmony_ci (BIT(offset))); 3858c2ecf20Sopenharmony_ci else 3868c2ecf20Sopenharmony_ci pl061_direction_input(&pl061->gc, offset); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS); 3908c2ecf20Sopenharmony_ci writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE); 3918c2ecf20Sopenharmony_ci writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV); 3928c2ecf20Sopenharmony_ci writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pl061_dev_pm_ops = { 3988c2ecf20Sopenharmony_ci .suspend = pl061_suspend, 3998c2ecf20Sopenharmony_ci .resume = pl061_resume, 4008c2ecf20Sopenharmony_ci .freeze = pl061_suspend, 4018c2ecf20Sopenharmony_ci .restore = pl061_resume, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci#endif 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic const struct amba_id pl061_ids[] = { 4068c2ecf20Sopenharmony_ci { 4078c2ecf20Sopenharmony_ci .id = 0x00041061, 4088c2ecf20Sopenharmony_ci .mask = 0x000fffff, 4098c2ecf20Sopenharmony_ci }, 4108c2ecf20Sopenharmony_ci { 0, 0 }, 4118c2ecf20Sopenharmony_ci}; 4128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl061_ids); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic struct amba_driver pl061_gpio_driver = { 4158c2ecf20Sopenharmony_ci .drv = { 4168c2ecf20Sopenharmony_ci .name = "pl061_gpio", 4178c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4188c2ecf20Sopenharmony_ci .pm = &pl061_dev_pm_ops, 4198c2ecf20Sopenharmony_ci#endif 4208c2ecf20Sopenharmony_ci }, 4218c2ecf20Sopenharmony_ci .id_table = pl061_ids, 4228c2ecf20Sopenharmony_ci .probe = pl061_probe, 4238c2ecf20Sopenharmony_ci}; 4248c2ecf20Sopenharmony_cimodule_amba_driver(pl061_gpio_driver); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 427