11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2c5abbba9STien Hock Loh /* 3c5abbba9STien Hock Loh * Copyright (C) 2013 Altera Corporation 4c5abbba9STien Hock Loh * Based on gpio-mpc8xxx.c 5c5abbba9STien Hock Loh */ 6c5abbba9STien Hock Loh 7c5abbba9STien Hock Loh #include <linux/io.h> 87b5409eeSPaul Gortmaker #include <linux/module.h> 940a1f9b2SLinus Walleij #include <linux/gpio/driver.h> 10*a99cc668SArnd Bergmann #include <linux/gpio/legacy-of-mm-gpiochip.h> 11c5abbba9STien Hock Loh #include <linux/platform_device.h> 12c5abbba9STien Hock Loh 13c5abbba9STien Hock Loh #define ALTERA_GPIO_MAX_NGPIO 32 14c5abbba9STien Hock Loh #define ALTERA_GPIO_DATA 0x0 15c5abbba9STien Hock Loh #define ALTERA_GPIO_DIR 0x4 16c5abbba9STien Hock Loh #define ALTERA_GPIO_IRQ_MASK 0x8 17c5abbba9STien Hock Loh #define ALTERA_GPIO_EDGE_CAP 0xc 18c5abbba9STien Hock Loh 19c5abbba9STien Hock Loh /** 20c5abbba9STien Hock Loh * struct altera_gpio_chip 21c5abbba9STien Hock Loh * @mmchip : memory mapped chip structure. 22c5abbba9STien Hock Loh * @gpio_lock : synchronization lock so that new irq/set/get requests 239ce01efeSPhil Reid * will be blocked until the current one completes. 24c5abbba9STien Hock Loh * @interrupt_trigger : specifies the hardware configured IRQ trigger type 259ce01efeSPhil Reid * (rising, falling, both, high) 26c5abbba9STien Hock Loh * @mapped_irq : kernel mapped irq number. 27670647d7SLee Jones * @irq_chip : IRQ chip configuration 28c5abbba9STien Hock Loh */ 29c5abbba9STien Hock Loh struct altera_gpio_chip { 30c5abbba9STien Hock Loh struct of_mm_gpio_chip mmchip; 3121d01c9cSJulia Cartwright raw_spinlock_t gpio_lock; 32c5abbba9STien Hock Loh int interrupt_trigger; 33c5abbba9STien Hock Loh int mapped_irq; 349d373acaSPhil Reid struct irq_chip irq_chip; 35c5abbba9STien Hock Loh }; 36c5abbba9STien Hock Loh 37c5abbba9STien Hock Loh static void altera_gpio_irq_unmask(struct irq_data *d) 38c5abbba9STien Hock Loh { 39c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 40c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 41c5abbba9STien Hock Loh unsigned long flags; 42c5abbba9STien Hock Loh u32 intmask; 43c5abbba9STien Hock Loh 44397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 45c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 46c5abbba9STien Hock Loh 4721d01c9cSJulia Cartwright raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 48c5abbba9STien Hock Loh intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 49c5abbba9STien Hock Loh /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ 50c5abbba9STien Hock Loh intmask |= BIT(irqd_to_hwirq(d)); 51c5abbba9STien Hock Loh writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 5221d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 53c5abbba9STien Hock Loh } 54c5abbba9STien Hock Loh 55c5abbba9STien Hock Loh static void altera_gpio_irq_mask(struct irq_data *d) 56c5abbba9STien Hock Loh { 57c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 58c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 59c5abbba9STien Hock Loh unsigned long flags; 60c5abbba9STien Hock Loh u32 intmask; 61c5abbba9STien Hock Loh 62397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 63c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 64c5abbba9STien Hock Loh 6521d01c9cSJulia Cartwright raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 66c5abbba9STien Hock Loh intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 67c5abbba9STien Hock Loh /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ 68c5abbba9STien Hock Loh intmask &= ~BIT(irqd_to_hwirq(d)); 69c5abbba9STien Hock Loh writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 7021d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 71c5abbba9STien Hock Loh } 72c5abbba9STien Hock Loh 73670647d7SLee Jones /* 74c5abbba9STien Hock Loh * This controller's IRQ type is synthesized in hardware, so this function 75c5abbba9STien Hock Loh * just checks if the requested set_type matches the synthesized IRQ type 76c5abbba9STien Hock Loh */ 77c5abbba9STien Hock Loh static int altera_gpio_irq_set_type(struct irq_data *d, 78c5abbba9STien Hock Loh unsigned int type) 79c5abbba9STien Hock Loh { 80c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 81c5abbba9STien Hock Loh 82397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 83c5abbba9STien Hock Loh 84f759921cSPhil Reid if (type == IRQ_TYPE_NONE) { 85f759921cSPhil Reid irq_set_handler_locked(d, handle_bad_irq); 86c5abbba9STien Hock Loh return 0; 87f759921cSPhil Reid } 88f759921cSPhil Reid if (type == altera_gc->interrupt_trigger) { 89f759921cSPhil Reid if (type == IRQ_TYPE_LEVEL_HIGH) 90f759921cSPhil Reid irq_set_handler_locked(d, handle_level_irq); 91f759921cSPhil Reid else 92f759921cSPhil Reid irq_set_handler_locked(d, handle_simple_irq); 93c5abbba9STien Hock Loh return 0; 94f759921cSPhil Reid } 95f759921cSPhil Reid irq_set_handler_locked(d, handle_bad_irq); 96c5abbba9STien Hock Loh return -EINVAL; 97c5abbba9STien Hock Loh } 98c5abbba9STien Hock Loh 9938e003f4SDaniel Lockyer static unsigned int altera_gpio_irq_startup(struct irq_data *d) 10038e003f4SDaniel Lockyer { 101c5abbba9STien Hock Loh altera_gpio_irq_unmask(d); 102c5abbba9STien Hock Loh 103c5abbba9STien Hock Loh return 0; 104c5abbba9STien Hock Loh } 105c5abbba9STien Hock Loh 106c5abbba9STien Hock Loh static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) 107c5abbba9STien Hock Loh { 108c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 109c5abbba9STien Hock Loh 110c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 111c5abbba9STien Hock Loh 112c5abbba9STien Hock Loh return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); 113c5abbba9STien Hock Loh } 114c5abbba9STien Hock Loh 115c5abbba9STien Hock Loh static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 116c5abbba9STien Hock Loh { 117c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 118c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 119c5abbba9STien Hock Loh unsigned long flags; 120c5abbba9STien Hock Loh unsigned int data_reg; 121c5abbba9STien Hock Loh 122c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 123397d0773SLinus Walleij chip = gpiochip_get_data(gc); 124c5abbba9STien Hock Loh 12521d01c9cSJulia Cartwright raw_spin_lock_irqsave(&chip->gpio_lock, flags); 126c5abbba9STien Hock Loh data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 127c5abbba9STien Hock Loh if (value) 128c5abbba9STien Hock Loh data_reg |= BIT(offset); 129c5abbba9STien Hock Loh else 130c5abbba9STien Hock Loh data_reg &= ~BIT(offset); 131c5abbba9STien Hock Loh writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 13221d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 133c5abbba9STien Hock Loh } 134c5abbba9STien Hock Loh 135c5abbba9STien Hock Loh static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 136c5abbba9STien Hock Loh { 137c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 138c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 139c5abbba9STien Hock Loh unsigned long flags; 140c5abbba9STien Hock Loh unsigned int gpio_ddr; 141c5abbba9STien Hock Loh 142c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 143397d0773SLinus Walleij chip = gpiochip_get_data(gc); 144c5abbba9STien Hock Loh 14521d01c9cSJulia Cartwright raw_spin_lock_irqsave(&chip->gpio_lock, flags); 146c5abbba9STien Hock Loh /* Set pin as input, assumes software controlled IP */ 147c5abbba9STien Hock Loh gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 148c5abbba9STien Hock Loh gpio_ddr &= ~BIT(offset); 149c5abbba9STien Hock Loh writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 15021d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 151c5abbba9STien Hock Loh 152c5abbba9STien Hock Loh return 0; 153c5abbba9STien Hock Loh } 154c5abbba9STien Hock Loh 155c5abbba9STien Hock Loh static int altera_gpio_direction_output(struct gpio_chip *gc, 156c5abbba9STien Hock Loh unsigned offset, int value) 157c5abbba9STien Hock Loh { 158c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 159c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 160c5abbba9STien Hock Loh unsigned long flags; 161c5abbba9STien Hock Loh unsigned int data_reg, gpio_ddr; 162c5abbba9STien Hock Loh 163c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 164397d0773SLinus Walleij chip = gpiochip_get_data(gc); 165c5abbba9STien Hock Loh 16621d01c9cSJulia Cartwright raw_spin_lock_irqsave(&chip->gpio_lock, flags); 167c5abbba9STien Hock Loh /* Sets the GPIO value */ 168c5abbba9STien Hock Loh data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 169c5abbba9STien Hock Loh if (value) 170c5abbba9STien Hock Loh data_reg |= BIT(offset); 171c5abbba9STien Hock Loh else 172c5abbba9STien Hock Loh data_reg &= ~BIT(offset); 173c5abbba9STien Hock Loh writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 174c5abbba9STien Hock Loh 175c5abbba9STien Hock Loh /* Set pin as output, assumes software controlled IP */ 176c5abbba9STien Hock Loh gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 177c5abbba9STien Hock Loh gpio_ddr |= BIT(offset); 178c5abbba9STien Hock Loh writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 17921d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 180c5abbba9STien Hock Loh 181c5abbba9STien Hock Loh return 0; 182c5abbba9STien Hock Loh } 183c5abbba9STien Hock Loh 184bd0b9ac4SThomas Gleixner static void altera_gpio_irq_edge_handler(struct irq_desc *desc) 185c5abbba9STien Hock Loh { 186c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 187c5abbba9STien Hock Loh struct irq_chip *chip; 188c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 189c5abbba9STien Hock Loh struct irq_domain *irqdomain; 190c5abbba9STien Hock Loh unsigned long status; 191c5abbba9STien Hock Loh int i; 192c5abbba9STien Hock Loh 193397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 194c5abbba9STien Hock Loh chip = irq_desc_get_chip(desc); 195c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 196f0fbe7bcSThierry Reding irqdomain = altera_gc->mmchip.gc.irq.domain; 197c5abbba9STien Hock Loh 198c5abbba9STien Hock Loh chained_irq_enter(chip, desc); 199c5abbba9STien Hock Loh 200c5abbba9STien Hock Loh while ((status = 201c5abbba9STien Hock Loh (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & 202c5abbba9STien Hock Loh readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { 203c5abbba9STien Hock Loh writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); 204dbd1c54fSMarc Zyngier for_each_set_bit(i, &status, mm_gc->gc.ngpio) 205dbd1c54fSMarc Zyngier generic_handle_domain_irq(irqdomain, i); 206c5abbba9STien Hock Loh } 207c5abbba9STien Hock Loh 208c5abbba9STien Hock Loh chained_irq_exit(chip, desc); 209c5abbba9STien Hock Loh } 210c5abbba9STien Hock Loh 211bd0b9ac4SThomas Gleixner static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) 212c5abbba9STien Hock Loh { 213c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 214c5abbba9STien Hock Loh struct irq_chip *chip; 215c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 216c5abbba9STien Hock Loh struct irq_domain *irqdomain; 217c5abbba9STien Hock Loh unsigned long status; 218c5abbba9STien Hock Loh int i; 219c5abbba9STien Hock Loh 220397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 221c5abbba9STien Hock Loh chip = irq_desc_get_chip(desc); 222c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 223f0fbe7bcSThierry Reding irqdomain = altera_gc->mmchip.gc.irq.domain; 224c5abbba9STien Hock Loh 225c5abbba9STien Hock Loh chained_irq_enter(chip, desc); 226c5abbba9STien Hock Loh 227c5abbba9STien Hock Loh status = readl(mm_gc->regs + ALTERA_GPIO_DATA); 228c5abbba9STien Hock Loh status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 229c5abbba9STien Hock Loh 230dbd1c54fSMarc Zyngier for_each_set_bit(i, &status, mm_gc->gc.ngpio) 231dbd1c54fSMarc Zyngier generic_handle_domain_irq(irqdomain, i); 232dbd1c54fSMarc Zyngier 233c5abbba9STien Hock Loh chained_irq_exit(chip, desc); 234c5abbba9STien Hock Loh } 235c5abbba9STien Hock Loh 236c4b40493Skbuild test robot static int altera_gpio_probe(struct platform_device *pdev) 237c5abbba9STien Hock Loh { 238c5abbba9STien Hock Loh struct device_node *node = pdev->dev.of_node; 239c5abbba9STien Hock Loh int reg, ret; 240c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 2412617790fSLinus Walleij struct gpio_irq_chip *girq; 242c5abbba9STien Hock Loh 243c5abbba9STien Hock Loh altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); 244c5abbba9STien Hock Loh if (!altera_gc) 245c5abbba9STien Hock Loh return -ENOMEM; 246c5abbba9STien Hock Loh 24721d01c9cSJulia Cartwright raw_spin_lock_init(&altera_gc->gpio_lock); 248c5abbba9STien Hock Loh 249c5abbba9STien Hock Loh if (of_property_read_u32(node, "altr,ngpio", ®)) 250c5abbba9STien Hock Loh /* By default assume maximum ngpio */ 251c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 252c5abbba9STien Hock Loh else 253c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = reg; 254c5abbba9STien Hock Loh 255c5abbba9STien Hock Loh if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { 256c5abbba9STien Hock Loh dev_warn(&pdev->dev, 257c5abbba9STien Hock Loh "ngpio is greater than %d, defaulting to %d\n", 258c5abbba9STien Hock Loh ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); 259c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 260c5abbba9STien Hock Loh } 261c5abbba9STien Hock Loh 262c5abbba9STien Hock Loh altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input; 263c5abbba9STien Hock Loh altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output; 264c5abbba9STien Hock Loh altera_gc->mmchip.gc.get = altera_gpio_get; 265c5abbba9STien Hock Loh altera_gc->mmchip.gc.set = altera_gpio_set; 266c5abbba9STien Hock Loh altera_gc->mmchip.gc.owner = THIS_MODULE; 26758383c78SLinus Walleij altera_gc->mmchip.gc.parent = &pdev->dev; 268c5abbba9STien Hock Loh 2691e4d149eSOoi, Joyce altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); 270c5abbba9STien Hock Loh 271c5abbba9STien Hock Loh if (altera_gc->mapped_irq < 0) 272c5abbba9STien Hock Loh goto skip_irq; 273c5abbba9STien Hock Loh 274c5abbba9STien Hock Loh if (of_property_read_u32(node, "altr,interrupt-type", ®)) { 275c5abbba9STien Hock Loh dev_err(&pdev->dev, 276c5abbba9STien Hock Loh "altr,interrupt-type value not set in device tree\n"); 2772617790fSLinus Walleij return -EINVAL; 278c5abbba9STien Hock Loh } 279c5abbba9STien Hock Loh altera_gc->interrupt_trigger = reg; 280c5abbba9STien Hock Loh 2819d373acaSPhil Reid altera_gc->irq_chip.name = "altera-gpio"; 2829d373acaSPhil Reid altera_gc->irq_chip.irq_mask = altera_gpio_irq_mask; 2839d373acaSPhil Reid altera_gc->irq_chip.irq_unmask = altera_gpio_irq_unmask; 2849d373acaSPhil Reid altera_gc->irq_chip.irq_set_type = altera_gpio_irq_set_type; 2859d373acaSPhil Reid altera_gc->irq_chip.irq_startup = altera_gpio_irq_startup; 2869d373acaSPhil Reid altera_gc->irq_chip.irq_shutdown = altera_gpio_irq_mask; 2879d373acaSPhil Reid 2882617790fSLinus Walleij girq = &altera_gc->mmchip.gc.irq; 2892617790fSLinus Walleij girq->chip = &altera_gc->irq_chip; 2902617790fSLinus Walleij if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) 2912617790fSLinus Walleij girq->parent_handler = altera_gpio_irq_leveL_high_handler; 2922617790fSLinus Walleij else 2932617790fSLinus Walleij girq->parent_handler = altera_gpio_irq_edge_handler; 2942617790fSLinus Walleij girq->num_parents = 1; 2952617790fSLinus Walleij girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents), 2962617790fSLinus Walleij GFP_KERNEL); 2972617790fSLinus Walleij if (!girq->parents) 2982617790fSLinus Walleij return -ENOMEM; 2992617790fSLinus Walleij girq->default_type = IRQ_TYPE_NONE; 3002617790fSLinus Walleij girq->handler = handle_bad_irq; 3012617790fSLinus Walleij girq->parents[0] = altera_gc->mapped_irq; 302c5abbba9STien Hock Loh 303c5abbba9STien Hock Loh skip_irq: 3042617790fSLinus Walleij ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc); 3052617790fSLinus Walleij if (ret) { 3062617790fSLinus Walleij dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); 307c5abbba9STien Hock Loh return ret; 308c5abbba9STien Hock Loh } 309c5abbba9STien Hock Loh 3102617790fSLinus Walleij platform_set_drvdata(pdev, altera_gc); 3112617790fSLinus Walleij 3122617790fSLinus Walleij return 0; 3132617790fSLinus Walleij } 3142617790fSLinus Walleij 315c5abbba9STien Hock Loh static int altera_gpio_remove(struct platform_device *pdev) 316c5abbba9STien Hock Loh { 317c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); 318c5abbba9STien Hock Loh 31941ec66c9SMasahiro Yamada of_mm_gpiochip_remove(&altera_gc->mmchip); 320c5abbba9STien Hock Loh 3211c8b5d68SMasahiro Yamada return 0; 322c5abbba9STien Hock Loh } 323c5abbba9STien Hock Loh 324c5abbba9STien Hock Loh static const struct of_device_id altera_gpio_of_match[] = { 325c5abbba9STien Hock Loh { .compatible = "altr,pio-1.0", }, 326c5abbba9STien Hock Loh {}, 327c5abbba9STien Hock Loh }; 328c5abbba9STien Hock Loh MODULE_DEVICE_TABLE(of, altera_gpio_of_match); 329c5abbba9STien Hock Loh 330c5abbba9STien Hock Loh static struct platform_driver altera_gpio_driver = { 331c5abbba9STien Hock Loh .driver = { 332c5abbba9STien Hock Loh .name = "altera_gpio", 333c5abbba9STien Hock Loh .of_match_table = of_match_ptr(altera_gpio_of_match), 334c5abbba9STien Hock Loh }, 335c5abbba9STien Hock Loh .probe = altera_gpio_probe, 336c5abbba9STien Hock Loh .remove = altera_gpio_remove, 337c5abbba9STien Hock Loh }; 338c5abbba9STien Hock Loh 339c5abbba9STien Hock Loh static int __init altera_gpio_init(void) 340c5abbba9STien Hock Loh { 341c5abbba9STien Hock Loh return platform_driver_register(&altera_gpio_driver); 342c5abbba9STien Hock Loh } 343c5abbba9STien Hock Loh subsys_initcall(altera_gpio_init); 344c5abbba9STien Hock Loh 345c5abbba9STien Hock Loh static void __exit altera_gpio_exit(void) 346c5abbba9STien Hock Loh { 347c5abbba9STien Hock Loh platform_driver_unregister(&altera_gpio_driver); 348c5abbba9STien Hock Loh } 349c5abbba9STien Hock Loh module_exit(altera_gpio_exit); 350c5abbba9STien Hock Loh 351c5abbba9STien Hock Loh MODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>"); 352c5abbba9STien Hock Loh MODULE_DESCRIPTION("Altera GPIO driver"); 353c5abbba9STien Hock Loh MODULE_LICENSE("GPL"); 354