1*1ccea77eSThomas 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> 1040a1f9b2SLinus Walleij #include <linux/of_gpio.h> /* For of_mm_gpio_chip */ 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. 27c5abbba9STien Hock Loh */ 28c5abbba9STien Hock Loh struct altera_gpio_chip { 29c5abbba9STien Hock Loh struct of_mm_gpio_chip mmchip; 3021d01c9cSJulia Cartwright raw_spinlock_t gpio_lock; 31c5abbba9STien Hock Loh int interrupt_trigger; 32c5abbba9STien Hock Loh int mapped_irq; 33c5abbba9STien Hock Loh }; 34c5abbba9STien Hock Loh 35c5abbba9STien Hock Loh static void altera_gpio_irq_unmask(struct irq_data *d) 36c5abbba9STien Hock Loh { 37c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 38c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 39c5abbba9STien Hock Loh unsigned long flags; 40c5abbba9STien Hock Loh u32 intmask; 41c5abbba9STien Hock Loh 42397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 43c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 44c5abbba9STien Hock Loh 4521d01c9cSJulia Cartwright raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 46c5abbba9STien Hock Loh intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 47c5abbba9STien Hock Loh /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ 48c5abbba9STien Hock Loh intmask |= BIT(irqd_to_hwirq(d)); 49c5abbba9STien Hock Loh writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 5021d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 51c5abbba9STien Hock Loh } 52c5abbba9STien Hock Loh 53c5abbba9STien Hock Loh static void altera_gpio_irq_mask(struct irq_data *d) 54c5abbba9STien Hock Loh { 55c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 56c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 57c5abbba9STien Hock Loh unsigned long flags; 58c5abbba9STien Hock Loh u32 intmask; 59c5abbba9STien Hock Loh 60397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 61c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 62c5abbba9STien Hock Loh 6321d01c9cSJulia Cartwright raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 64c5abbba9STien Hock Loh intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 65c5abbba9STien Hock Loh /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ 66c5abbba9STien Hock Loh intmask &= ~BIT(irqd_to_hwirq(d)); 67c5abbba9STien Hock Loh writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 6821d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 69c5abbba9STien Hock Loh } 70c5abbba9STien Hock Loh 71c5abbba9STien Hock Loh /** 72c5abbba9STien Hock Loh * This controller's IRQ type is synthesized in hardware, so this function 73c5abbba9STien Hock Loh * just checks if the requested set_type matches the synthesized IRQ type 74c5abbba9STien Hock Loh */ 75c5abbba9STien Hock Loh static int altera_gpio_irq_set_type(struct irq_data *d, 76c5abbba9STien Hock Loh unsigned int type) 77c5abbba9STien Hock Loh { 78c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 79c5abbba9STien Hock Loh 80397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 81c5abbba9STien Hock Loh 82f759921cSPhil Reid if (type == IRQ_TYPE_NONE) { 83f759921cSPhil Reid irq_set_handler_locked(d, handle_bad_irq); 84c5abbba9STien Hock Loh return 0; 85f759921cSPhil Reid } 86f759921cSPhil Reid if (type == altera_gc->interrupt_trigger) { 87f759921cSPhil Reid if (type == IRQ_TYPE_LEVEL_HIGH) 88f759921cSPhil Reid irq_set_handler_locked(d, handle_level_irq); 89f759921cSPhil Reid else 90f759921cSPhil Reid irq_set_handler_locked(d, handle_simple_irq); 91c5abbba9STien Hock Loh return 0; 92f759921cSPhil Reid } 93f759921cSPhil Reid irq_set_handler_locked(d, handle_bad_irq); 94c5abbba9STien Hock Loh return -EINVAL; 95c5abbba9STien Hock Loh } 96c5abbba9STien Hock Loh 9738e003f4SDaniel Lockyer static unsigned int altera_gpio_irq_startup(struct irq_data *d) 9838e003f4SDaniel Lockyer { 99c5abbba9STien Hock Loh altera_gpio_irq_unmask(d); 100c5abbba9STien Hock Loh 101c5abbba9STien Hock Loh return 0; 102c5abbba9STien Hock Loh } 103c5abbba9STien Hock Loh 104c5abbba9STien Hock Loh static struct irq_chip altera_irq_chip = { 105c5abbba9STien Hock Loh .name = "altera-gpio", 106c5abbba9STien Hock Loh .irq_mask = altera_gpio_irq_mask, 107c5abbba9STien Hock Loh .irq_unmask = altera_gpio_irq_unmask, 108c5abbba9STien Hock Loh .irq_set_type = altera_gpio_irq_set_type, 109c5abbba9STien Hock Loh .irq_startup = altera_gpio_irq_startup, 110c5abbba9STien Hock Loh .irq_shutdown = altera_gpio_irq_mask, 111c5abbba9STien Hock Loh }; 112c5abbba9STien Hock Loh 113c5abbba9STien Hock Loh static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) 114c5abbba9STien Hock Loh { 115c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 116c5abbba9STien Hock Loh 117c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 118c5abbba9STien Hock Loh 119c5abbba9STien Hock Loh return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); 120c5abbba9STien Hock Loh } 121c5abbba9STien Hock Loh 122c5abbba9STien Hock Loh static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 123c5abbba9STien Hock Loh { 124c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 125c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 126c5abbba9STien Hock Loh unsigned long flags; 127c5abbba9STien Hock Loh unsigned int data_reg; 128c5abbba9STien Hock Loh 129c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 130397d0773SLinus Walleij chip = gpiochip_get_data(gc); 131c5abbba9STien Hock Loh 13221d01c9cSJulia Cartwright raw_spin_lock_irqsave(&chip->gpio_lock, flags); 133c5abbba9STien Hock Loh data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 134c5abbba9STien Hock Loh if (value) 135c5abbba9STien Hock Loh data_reg |= BIT(offset); 136c5abbba9STien Hock Loh else 137c5abbba9STien Hock Loh data_reg &= ~BIT(offset); 138c5abbba9STien Hock Loh writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 13921d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 140c5abbba9STien Hock Loh } 141c5abbba9STien Hock Loh 142c5abbba9STien Hock Loh static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 143c5abbba9STien Hock Loh { 144c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 145c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 146c5abbba9STien Hock Loh unsigned long flags; 147c5abbba9STien Hock Loh unsigned int gpio_ddr; 148c5abbba9STien Hock Loh 149c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 150397d0773SLinus Walleij chip = gpiochip_get_data(gc); 151c5abbba9STien Hock Loh 15221d01c9cSJulia Cartwright raw_spin_lock_irqsave(&chip->gpio_lock, flags); 153c5abbba9STien Hock Loh /* Set pin as input, assumes software controlled IP */ 154c5abbba9STien Hock Loh gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 155c5abbba9STien Hock Loh gpio_ddr &= ~BIT(offset); 156c5abbba9STien Hock Loh writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 15721d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 158c5abbba9STien Hock Loh 159c5abbba9STien Hock Loh return 0; 160c5abbba9STien Hock Loh } 161c5abbba9STien Hock Loh 162c5abbba9STien Hock Loh static int altera_gpio_direction_output(struct gpio_chip *gc, 163c5abbba9STien Hock Loh unsigned offset, int value) 164c5abbba9STien Hock Loh { 165c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 166c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 167c5abbba9STien Hock Loh unsigned long flags; 168c5abbba9STien Hock Loh unsigned int data_reg, gpio_ddr; 169c5abbba9STien Hock Loh 170c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 171397d0773SLinus Walleij chip = gpiochip_get_data(gc); 172c5abbba9STien Hock Loh 17321d01c9cSJulia Cartwright raw_spin_lock_irqsave(&chip->gpio_lock, flags); 174c5abbba9STien Hock Loh /* Sets the GPIO value */ 175c5abbba9STien Hock Loh data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 176c5abbba9STien Hock Loh if (value) 177c5abbba9STien Hock Loh data_reg |= BIT(offset); 178c5abbba9STien Hock Loh else 179c5abbba9STien Hock Loh data_reg &= ~BIT(offset); 180c5abbba9STien Hock Loh writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 181c5abbba9STien Hock Loh 182c5abbba9STien Hock Loh /* Set pin as output, assumes software controlled IP */ 183c5abbba9STien Hock Loh gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 184c5abbba9STien Hock Loh gpio_ddr |= BIT(offset); 185c5abbba9STien Hock Loh writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 18621d01c9cSJulia Cartwright raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 187c5abbba9STien Hock Loh 188c5abbba9STien Hock Loh return 0; 189c5abbba9STien Hock Loh } 190c5abbba9STien Hock Loh 191bd0b9ac4SThomas Gleixner static void altera_gpio_irq_edge_handler(struct irq_desc *desc) 192c5abbba9STien Hock Loh { 193c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 194c5abbba9STien Hock Loh struct irq_chip *chip; 195c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 196c5abbba9STien Hock Loh struct irq_domain *irqdomain; 197c5abbba9STien Hock Loh unsigned long status; 198c5abbba9STien Hock Loh int i; 199c5abbba9STien Hock Loh 200397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 201c5abbba9STien Hock Loh chip = irq_desc_get_chip(desc); 202c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 203f0fbe7bcSThierry Reding irqdomain = altera_gc->mmchip.gc.irq.domain; 204c5abbba9STien Hock Loh 205c5abbba9STien Hock Loh chained_irq_enter(chip, desc); 206c5abbba9STien Hock Loh 207c5abbba9STien Hock Loh while ((status = 208c5abbba9STien Hock Loh (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & 209c5abbba9STien Hock Loh readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { 210c5abbba9STien Hock Loh writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); 211c5abbba9STien Hock Loh for_each_set_bit(i, &status, mm_gc->gc.ngpio) { 212c5abbba9STien Hock Loh generic_handle_irq(irq_find_mapping(irqdomain, i)); 213c5abbba9STien Hock Loh } 214c5abbba9STien Hock Loh } 215c5abbba9STien Hock Loh 216c5abbba9STien Hock Loh chained_irq_exit(chip, desc); 217c5abbba9STien Hock Loh } 218c5abbba9STien Hock Loh 219bd0b9ac4SThomas Gleixner static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) 220c5abbba9STien Hock Loh { 221c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 222c5abbba9STien Hock Loh struct irq_chip *chip; 223c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 224c5abbba9STien Hock Loh struct irq_domain *irqdomain; 225c5abbba9STien Hock Loh unsigned long status; 226c5abbba9STien Hock Loh int i; 227c5abbba9STien Hock Loh 228397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 229c5abbba9STien Hock Loh chip = irq_desc_get_chip(desc); 230c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 231f0fbe7bcSThierry Reding irqdomain = altera_gc->mmchip.gc.irq.domain; 232c5abbba9STien Hock Loh 233c5abbba9STien Hock Loh chained_irq_enter(chip, desc); 234c5abbba9STien Hock Loh 235c5abbba9STien Hock Loh status = readl(mm_gc->regs + ALTERA_GPIO_DATA); 236c5abbba9STien Hock Loh status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 237c5abbba9STien Hock Loh 238c5abbba9STien Hock Loh for_each_set_bit(i, &status, mm_gc->gc.ngpio) { 239c5abbba9STien Hock Loh generic_handle_irq(irq_find_mapping(irqdomain, i)); 240c5abbba9STien Hock Loh } 241c5abbba9STien Hock Loh chained_irq_exit(chip, desc); 242c5abbba9STien Hock Loh } 243c5abbba9STien Hock Loh 244c4b40493Skbuild test robot static int altera_gpio_probe(struct platform_device *pdev) 245c5abbba9STien Hock Loh { 246c5abbba9STien Hock Loh struct device_node *node = pdev->dev.of_node; 247c5abbba9STien Hock Loh int reg, ret; 248c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 249c5abbba9STien Hock Loh 250c5abbba9STien Hock Loh altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); 251c5abbba9STien Hock Loh if (!altera_gc) 252c5abbba9STien Hock Loh return -ENOMEM; 253c5abbba9STien Hock Loh 25421d01c9cSJulia Cartwright raw_spin_lock_init(&altera_gc->gpio_lock); 255c5abbba9STien Hock Loh 256c5abbba9STien Hock Loh if (of_property_read_u32(node, "altr,ngpio", ®)) 257c5abbba9STien Hock Loh /* By default assume maximum ngpio */ 258c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 259c5abbba9STien Hock Loh else 260c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = reg; 261c5abbba9STien Hock Loh 262c5abbba9STien Hock Loh if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { 263c5abbba9STien Hock Loh dev_warn(&pdev->dev, 264c5abbba9STien Hock Loh "ngpio is greater than %d, defaulting to %d\n", 265c5abbba9STien Hock Loh ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); 266c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 267c5abbba9STien Hock Loh } 268c5abbba9STien Hock Loh 269c5abbba9STien Hock Loh altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input; 270c5abbba9STien Hock Loh altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output; 271c5abbba9STien Hock Loh altera_gc->mmchip.gc.get = altera_gpio_get; 272c5abbba9STien Hock Loh altera_gc->mmchip.gc.set = altera_gpio_set; 273c5abbba9STien Hock Loh altera_gc->mmchip.gc.owner = THIS_MODULE; 27458383c78SLinus Walleij altera_gc->mmchip.gc.parent = &pdev->dev; 275c5abbba9STien Hock Loh 276397d0773SLinus Walleij ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc); 277c5abbba9STien Hock Loh if (ret) { 278c5abbba9STien Hock Loh dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); 279c5abbba9STien Hock Loh return ret; 280c5abbba9STien Hock Loh } 281c5abbba9STien Hock Loh 282c5abbba9STien Hock Loh platform_set_drvdata(pdev, altera_gc); 283c5abbba9STien Hock Loh 284c5abbba9STien Hock Loh altera_gc->mapped_irq = platform_get_irq(pdev, 0); 285c5abbba9STien Hock Loh 286c5abbba9STien Hock Loh if (altera_gc->mapped_irq < 0) 287c5abbba9STien Hock Loh goto skip_irq; 288c5abbba9STien Hock Loh 289c5abbba9STien Hock Loh if (of_property_read_u32(node, "altr,interrupt-type", ®)) { 290c5abbba9STien Hock Loh ret = -EINVAL; 291c5abbba9STien Hock Loh dev_err(&pdev->dev, 292c5abbba9STien Hock Loh "altr,interrupt-type value not set in device tree\n"); 293c5abbba9STien Hock Loh goto teardown; 294c5abbba9STien Hock Loh } 295c5abbba9STien Hock Loh altera_gc->interrupt_trigger = reg; 296c5abbba9STien Hock Loh 297c5abbba9STien Hock Loh ret = gpiochip_irqchip_add(&altera_gc->mmchip.gc, &altera_irq_chip, 0, 298f759921cSPhil Reid handle_bad_irq, IRQ_TYPE_NONE); 299c5abbba9STien Hock Loh 300c5abbba9STien Hock Loh if (ret) { 30173c13c83SPhil Reid dev_err(&pdev->dev, "could not add irqchip\n"); 30273c13c83SPhil Reid goto teardown; 303c5abbba9STien Hock Loh } 304c5abbba9STien Hock Loh 305c5abbba9STien Hock Loh gpiochip_set_chained_irqchip(&altera_gc->mmchip.gc, 306c5abbba9STien Hock Loh &altera_irq_chip, 307c5abbba9STien Hock Loh altera_gc->mapped_irq, 308c5abbba9STien Hock Loh altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH ? 309c5abbba9STien Hock Loh altera_gpio_irq_leveL_high_handler : 310c5abbba9STien Hock Loh altera_gpio_irq_edge_handler); 311c5abbba9STien Hock Loh 312c5abbba9STien Hock Loh skip_irq: 313c5abbba9STien Hock Loh return 0; 314c5abbba9STien Hock Loh teardown: 31573c13c83SPhil Reid of_mm_gpiochip_remove(&altera_gc->mmchip); 3167eb6ce2fSRob Herring pr_err("%pOF: registration failed with status %d\n", 3177eb6ce2fSRob Herring node, ret); 318c5abbba9STien Hock Loh 319c5abbba9STien Hock Loh return ret; 320c5abbba9STien Hock Loh } 321c5abbba9STien Hock Loh 322c5abbba9STien Hock Loh static int altera_gpio_remove(struct platform_device *pdev) 323c5abbba9STien Hock Loh { 324c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); 325c5abbba9STien Hock Loh 32641ec66c9SMasahiro Yamada of_mm_gpiochip_remove(&altera_gc->mmchip); 327c5abbba9STien Hock Loh 3281c8b5d68SMasahiro Yamada return 0; 329c5abbba9STien Hock Loh } 330c5abbba9STien Hock Loh 331c5abbba9STien Hock Loh static const struct of_device_id altera_gpio_of_match[] = { 332c5abbba9STien Hock Loh { .compatible = "altr,pio-1.0", }, 333c5abbba9STien Hock Loh {}, 334c5abbba9STien Hock Loh }; 335c5abbba9STien Hock Loh MODULE_DEVICE_TABLE(of, altera_gpio_of_match); 336c5abbba9STien Hock Loh 337c5abbba9STien Hock Loh static struct platform_driver altera_gpio_driver = { 338c5abbba9STien Hock Loh .driver = { 339c5abbba9STien Hock Loh .name = "altera_gpio", 340c5abbba9STien Hock Loh .of_match_table = of_match_ptr(altera_gpio_of_match), 341c5abbba9STien Hock Loh }, 342c5abbba9STien Hock Loh .probe = altera_gpio_probe, 343c5abbba9STien Hock Loh .remove = altera_gpio_remove, 344c5abbba9STien Hock Loh }; 345c5abbba9STien Hock Loh 346c5abbba9STien Hock Loh static int __init altera_gpio_init(void) 347c5abbba9STien Hock Loh { 348c5abbba9STien Hock Loh return platform_driver_register(&altera_gpio_driver); 349c5abbba9STien Hock Loh } 350c5abbba9STien Hock Loh subsys_initcall(altera_gpio_init); 351c5abbba9STien Hock Loh 352c5abbba9STien Hock Loh static void __exit altera_gpio_exit(void) 353c5abbba9STien Hock Loh { 354c5abbba9STien Hock Loh platform_driver_unregister(&altera_gpio_driver); 355c5abbba9STien Hock Loh } 356c5abbba9STien Hock Loh module_exit(altera_gpio_exit); 357c5abbba9STien Hock Loh 358c5abbba9STien Hock Loh MODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>"); 359c5abbba9STien Hock Loh MODULE_DESCRIPTION("Altera GPIO driver"); 360c5abbba9STien Hock Loh MODULE_LICENSE("GPL"); 361