18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013 Altera Corporation 48c2ecf20Sopenharmony_ci * Based on gpio-mpc8xxx.c 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 108c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> /* For of_mm_gpio_chip */ 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define ALTERA_GPIO_MAX_NGPIO 32 148c2ecf20Sopenharmony_ci#define ALTERA_GPIO_DATA 0x0 158c2ecf20Sopenharmony_ci#define ALTERA_GPIO_DIR 0x4 168c2ecf20Sopenharmony_ci#define ALTERA_GPIO_IRQ_MASK 0x8 178c2ecf20Sopenharmony_ci#define ALTERA_GPIO_EDGE_CAP 0xc 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci* struct altera_gpio_chip 218c2ecf20Sopenharmony_ci* @mmchip : memory mapped chip structure. 228c2ecf20Sopenharmony_ci* @gpio_lock : synchronization lock so that new irq/set/get requests 238c2ecf20Sopenharmony_ci* will be blocked until the current one completes. 248c2ecf20Sopenharmony_ci* @interrupt_trigger : specifies the hardware configured IRQ trigger type 258c2ecf20Sopenharmony_ci* (rising, falling, both, high) 268c2ecf20Sopenharmony_ci* @mapped_irq : kernel mapped irq number. 278c2ecf20Sopenharmony_ci* @irq_chip : IRQ chip configuration 288c2ecf20Sopenharmony_ci*/ 298c2ecf20Sopenharmony_cistruct altera_gpio_chip { 308c2ecf20Sopenharmony_ci struct of_mm_gpio_chip mmchip; 318c2ecf20Sopenharmony_ci raw_spinlock_t gpio_lock; 328c2ecf20Sopenharmony_ci int interrupt_trigger; 338c2ecf20Sopenharmony_ci int mapped_irq; 348c2ecf20Sopenharmony_ci struct irq_chip irq_chip; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void altera_gpio_irq_unmask(struct irq_data *d) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct altera_gpio_chip *altera_gc; 408c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 418c2ecf20Sopenharmony_ci unsigned long flags; 428c2ecf20Sopenharmony_ci u32 intmask; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 458c2ecf20Sopenharmony_ci mm_gc = &altera_gc->mmchip; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 488c2ecf20Sopenharmony_ci intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 498c2ecf20Sopenharmony_ci /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ 508c2ecf20Sopenharmony_ci intmask |= BIT(irqd_to_hwirq(d)); 518c2ecf20Sopenharmony_ci writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 528c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void altera_gpio_irq_mask(struct irq_data *d) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct altera_gpio_chip *altera_gc; 588c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 598c2ecf20Sopenharmony_ci unsigned long flags; 608c2ecf20Sopenharmony_ci u32 intmask; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 638c2ecf20Sopenharmony_ci mm_gc = &altera_gc->mmchip; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 668c2ecf20Sopenharmony_ci intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 678c2ecf20Sopenharmony_ci /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ 688c2ecf20Sopenharmony_ci intmask &= ~BIT(irqd_to_hwirq(d)); 698c2ecf20Sopenharmony_ci writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 708c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* 748c2ecf20Sopenharmony_ci * This controller's IRQ type is synthesized in hardware, so this function 758c2ecf20Sopenharmony_ci * just checks if the requested set_type matches the synthesized IRQ type 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic int altera_gpio_irq_set_type(struct irq_data *d, 788c2ecf20Sopenharmony_ci unsigned int type) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct altera_gpio_chip *altera_gc; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_NONE) { 858c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_bad_irq); 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci if (type == altera_gc->interrupt_trigger) { 898c2ecf20Sopenharmony_ci if (type == IRQ_TYPE_LEVEL_HIGH) 908c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 918c2ecf20Sopenharmony_ci else 928c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_simple_irq); 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci irq_set_handler_locked(d, handle_bad_irq); 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic unsigned int altera_gpio_irq_startup(struct irq_data *d) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci altera_gpio_irq_unmask(d); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int altera_gpio_get(struct gpio_chip *gc, unsigned offset) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 1188c2ecf20Sopenharmony_ci struct altera_gpio_chip *chip; 1198c2ecf20Sopenharmony_ci unsigned long flags; 1208c2ecf20Sopenharmony_ci unsigned int data_reg; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 1238c2ecf20Sopenharmony_ci chip = gpiochip_get_data(gc); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->gpio_lock, flags); 1268c2ecf20Sopenharmony_ci data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 1278c2ecf20Sopenharmony_ci if (value) 1288c2ecf20Sopenharmony_ci data_reg |= BIT(offset); 1298c2ecf20Sopenharmony_ci else 1308c2ecf20Sopenharmony_ci data_reg &= ~BIT(offset); 1318c2ecf20Sopenharmony_ci writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 1328c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 1388c2ecf20Sopenharmony_ci struct altera_gpio_chip *chip; 1398c2ecf20Sopenharmony_ci unsigned long flags; 1408c2ecf20Sopenharmony_ci unsigned int gpio_ddr; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 1438c2ecf20Sopenharmony_ci chip = gpiochip_get_data(gc); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->gpio_lock, flags); 1468c2ecf20Sopenharmony_ci /* Set pin as input, assumes software controlled IP */ 1478c2ecf20Sopenharmony_ci gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 1488c2ecf20Sopenharmony_ci gpio_ddr &= ~BIT(offset); 1498c2ecf20Sopenharmony_ci writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 1508c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int altera_gpio_direction_output(struct gpio_chip *gc, 1568c2ecf20Sopenharmony_ci unsigned offset, int value) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 1598c2ecf20Sopenharmony_ci struct altera_gpio_chip *chip; 1608c2ecf20Sopenharmony_ci unsigned long flags; 1618c2ecf20Sopenharmony_ci unsigned int data_reg, gpio_ddr; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 1648c2ecf20Sopenharmony_ci chip = gpiochip_get_data(gc); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&chip->gpio_lock, flags); 1678c2ecf20Sopenharmony_ci /* Sets the GPIO value */ 1688c2ecf20Sopenharmony_ci data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 1698c2ecf20Sopenharmony_ci if (value) 1708c2ecf20Sopenharmony_ci data_reg |= BIT(offset); 1718c2ecf20Sopenharmony_ci else 1728c2ecf20Sopenharmony_ci data_reg &= ~BIT(offset); 1738c2ecf20Sopenharmony_ci writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Set pin as output, assumes software controlled IP */ 1768c2ecf20Sopenharmony_ci gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 1778c2ecf20Sopenharmony_ci gpio_ddr |= BIT(offset); 1788c2ecf20Sopenharmony_ci writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 1798c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void altera_gpio_irq_edge_handler(struct irq_desc *desc) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct altera_gpio_chip *altera_gc; 1878c2ecf20Sopenharmony_ci struct irq_chip *chip; 1888c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 1898c2ecf20Sopenharmony_ci struct irq_domain *irqdomain; 1908c2ecf20Sopenharmony_ci unsigned long status; 1918c2ecf20Sopenharmony_ci int i; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 1948c2ecf20Sopenharmony_ci chip = irq_desc_get_chip(desc); 1958c2ecf20Sopenharmony_ci mm_gc = &altera_gc->mmchip; 1968c2ecf20Sopenharmony_ci irqdomain = altera_gc->mmchip.gc.irq.domain; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci while ((status = 2018c2ecf20Sopenharmony_ci (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & 2028c2ecf20Sopenharmony_ci readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { 2038c2ecf20Sopenharmony_ci writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); 2048c2ecf20Sopenharmony_ci for_each_set_bit(i, &status, mm_gc->gc.ngpio) { 2058c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(irqdomain, i)); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct altera_gpio_chip *altera_gc; 2158c2ecf20Sopenharmony_ci struct irq_chip *chip; 2168c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 2178c2ecf20Sopenharmony_ci struct irq_domain *irqdomain; 2188c2ecf20Sopenharmony_ci unsigned long status; 2198c2ecf20Sopenharmony_ci int i; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 2228c2ecf20Sopenharmony_ci chip = irq_desc_get_chip(desc); 2238c2ecf20Sopenharmony_ci mm_gc = &altera_gc->mmchip; 2248c2ecf20Sopenharmony_ci irqdomain = altera_gc->mmchip.gc.irq.domain; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci status = readl(mm_gc->regs + ALTERA_GPIO_DATA); 2298c2ecf20Sopenharmony_ci status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci for_each_set_bit(i, &status, mm_gc->gc.ngpio) { 2328c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(irqdomain, i)); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int altera_gpio_probe(struct platform_device *pdev) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 2408c2ecf20Sopenharmony_ci int reg, ret; 2418c2ecf20Sopenharmony_ci struct altera_gpio_chip *altera_gc; 2428c2ecf20Sopenharmony_ci struct gpio_irq_chip *girq; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); 2458c2ecf20Sopenharmony_ci if (!altera_gc) 2468c2ecf20Sopenharmony_ci return -ENOMEM; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci raw_spin_lock_init(&altera_gc->gpio_lock); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "altr,ngpio", ®)) 2518c2ecf20Sopenharmony_ci /* By default assume maximum ngpio */ 2528c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 2538c2ecf20Sopenharmony_ci else 2548c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.ngpio = reg; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { 2578c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 2588c2ecf20Sopenharmony_ci "ngpio is greater than %d, defaulting to %d\n", 2598c2ecf20Sopenharmony_ci ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); 2608c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input; 2648c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output; 2658c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.get = altera_gpio_get; 2668c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.set = altera_gpio_set; 2678c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.owner = THIS_MODULE; 2688c2ecf20Sopenharmony_ci altera_gc->mmchip.gc.parent = &pdev->dev; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (altera_gc->mapped_irq < 0) 2738c2ecf20Sopenharmony_ci goto skip_irq; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "altr,interrupt-type", ®)) { 2768c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2778c2ecf20Sopenharmony_ci "altr,interrupt-type value not set in device tree\n"); 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci altera_gc->interrupt_trigger = reg; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci altera_gc->irq_chip.name = "altera-gpio"; 2838c2ecf20Sopenharmony_ci altera_gc->irq_chip.irq_mask = altera_gpio_irq_mask; 2848c2ecf20Sopenharmony_ci altera_gc->irq_chip.irq_unmask = altera_gpio_irq_unmask; 2858c2ecf20Sopenharmony_ci altera_gc->irq_chip.irq_set_type = altera_gpio_irq_set_type; 2868c2ecf20Sopenharmony_ci altera_gc->irq_chip.irq_startup = altera_gpio_irq_startup; 2878c2ecf20Sopenharmony_ci altera_gc->irq_chip.irq_shutdown = altera_gpio_irq_mask; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci girq = &altera_gc->mmchip.gc.irq; 2908c2ecf20Sopenharmony_ci girq->chip = &altera_gc->irq_chip; 2918c2ecf20Sopenharmony_ci if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) 2928c2ecf20Sopenharmony_ci girq->parent_handler = altera_gpio_irq_leveL_high_handler; 2938c2ecf20Sopenharmony_ci else 2948c2ecf20Sopenharmony_ci girq->parent_handler = altera_gpio_irq_edge_handler; 2958c2ecf20Sopenharmony_ci girq->num_parents = 1; 2968c2ecf20Sopenharmony_ci girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents), 2978c2ecf20Sopenharmony_ci GFP_KERNEL); 2988c2ecf20Sopenharmony_ci if (!girq->parents) 2998c2ecf20Sopenharmony_ci return -ENOMEM; 3008c2ecf20Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 3018c2ecf20Sopenharmony_ci girq->handler = handle_bad_irq; 3028c2ecf20Sopenharmony_ci girq->parents[0] = altera_gc->mapped_irq; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ciskip_irq: 3058c2ecf20Sopenharmony_ci ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc); 3068c2ecf20Sopenharmony_ci if (ret) { 3078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, altera_gc); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci return 0; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int altera_gpio_remove(struct platform_device *pdev) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci of_mm_gpiochip_remove(&altera_gc->mmchip); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic const struct of_device_id altera_gpio_of_match[] = { 3268c2ecf20Sopenharmony_ci { .compatible = "altr,pio-1.0", }, 3278c2ecf20Sopenharmony_ci {}, 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, altera_gpio_of_match); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic struct platform_driver altera_gpio_driver = { 3328c2ecf20Sopenharmony_ci .driver = { 3338c2ecf20Sopenharmony_ci .name = "altera_gpio", 3348c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(altera_gpio_of_match), 3358c2ecf20Sopenharmony_ci }, 3368c2ecf20Sopenharmony_ci .probe = altera_gpio_probe, 3378c2ecf20Sopenharmony_ci .remove = altera_gpio_remove, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int __init altera_gpio_init(void) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci return platform_driver_register(&altera_gpio_driver); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_cisubsys_initcall(altera_gpio_init); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void __exit altera_gpio_exit(void) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci platform_driver_unregister(&altera_gpio_driver); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_cimodule_exit(altera_gpio_exit); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>"); 3538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Altera GPIO driver"); 3548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 355