162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Altera Corporation 462306a36Sopenharmony_ci * Based on gpio-mpc8xxx.c 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/gpio/driver.h> 1062306a36Sopenharmony_ci#include <linux/gpio/legacy-of-mm-gpiochip.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define ALTERA_GPIO_MAX_NGPIO 32 1462306a36Sopenharmony_ci#define ALTERA_GPIO_DATA 0x0 1562306a36Sopenharmony_ci#define ALTERA_GPIO_DIR 0x4 1662306a36Sopenharmony_ci#define ALTERA_GPIO_IRQ_MASK 0x8 1762306a36Sopenharmony_ci#define ALTERA_GPIO_EDGE_CAP 0xc 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci* struct altera_gpio_chip 2162306a36Sopenharmony_ci* @mmchip : memory mapped chip structure. 2262306a36Sopenharmony_ci* @gpio_lock : synchronization lock so that new irq/set/get requests 2362306a36Sopenharmony_ci* will be blocked until the current one completes. 2462306a36Sopenharmony_ci* @interrupt_trigger : specifies the hardware configured IRQ trigger type 2562306a36Sopenharmony_ci* (rising, falling, both, high) 2662306a36Sopenharmony_ci* @mapped_irq : kernel mapped irq number. 2762306a36Sopenharmony_ci*/ 2862306a36Sopenharmony_cistruct altera_gpio_chip { 2962306a36Sopenharmony_ci struct of_mm_gpio_chip mmchip; 3062306a36Sopenharmony_ci raw_spinlock_t gpio_lock; 3162306a36Sopenharmony_ci int interrupt_trigger; 3262306a36Sopenharmony_ci int mapped_irq; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void altera_gpio_irq_unmask(struct irq_data *d) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct altera_gpio_chip *altera_gc; 3862306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 3962306a36Sopenharmony_ci unsigned long flags; 4062306a36Sopenharmony_ci u32 intmask; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 4362306a36Sopenharmony_ci mm_gc = &altera_gc->mmchip; 4462306a36Sopenharmony_ci gpiochip_enable_irq(&mm_gc->gc, irqd_to_hwirq(d)); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 4762306a36Sopenharmony_ci intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 4862306a36Sopenharmony_ci /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ 4962306a36Sopenharmony_ci intmask |= BIT(irqd_to_hwirq(d)); 5062306a36Sopenharmony_ci writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 5162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void altera_gpio_irq_mask(struct irq_data *d) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct altera_gpio_chip *altera_gc; 5762306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 5862306a36Sopenharmony_ci unsigned long flags; 5962306a36Sopenharmony_ci u32 intmask; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 6262306a36Sopenharmony_ci mm_gc = &altera_gc->mmchip; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags); 6562306a36Sopenharmony_ci intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 6662306a36Sopenharmony_ci /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ 6762306a36Sopenharmony_ci intmask &= ~BIT(irqd_to_hwirq(d)); 6862306a36Sopenharmony_ci writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 6962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 7062306a36Sopenharmony_ci gpiochip_disable_irq(&mm_gc->gc, irqd_to_hwirq(d)); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* 7462306a36Sopenharmony_ci * This controller's IRQ type is synthesized in hardware, so this function 7562306a36Sopenharmony_ci * just checks if the requested set_type matches the synthesized IRQ type 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic int altera_gpio_irq_set_type(struct irq_data *d, 7862306a36Sopenharmony_ci unsigned int type) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct altera_gpio_chip *altera_gc; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (type == IRQ_TYPE_NONE) { 8562306a36Sopenharmony_ci irq_set_handler_locked(d, handle_bad_irq); 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci if (type == altera_gc->interrupt_trigger) { 8962306a36Sopenharmony_ci if (type == IRQ_TYPE_LEVEL_HIGH) 9062306a36Sopenharmony_ci irq_set_handler_locked(d, handle_level_irq); 9162306a36Sopenharmony_ci else 9262306a36Sopenharmony_ci irq_set_handler_locked(d, handle_simple_irq); 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci irq_set_handler_locked(d, handle_bad_irq); 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic unsigned int altera_gpio_irq_startup(struct irq_data *d) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci altera_gpio_irq_unmask(d); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int altera_gpio_get(struct gpio_chip *gc, unsigned offset) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 11862306a36Sopenharmony_ci struct altera_gpio_chip *chip; 11962306a36Sopenharmony_ci unsigned long flags; 12062306a36Sopenharmony_ci unsigned int data_reg; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 12362306a36Sopenharmony_ci chip = gpiochip_get_data(gc); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->gpio_lock, flags); 12662306a36Sopenharmony_ci data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 12762306a36Sopenharmony_ci if (value) 12862306a36Sopenharmony_ci data_reg |= BIT(offset); 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci data_reg &= ~BIT(offset); 13162306a36Sopenharmony_ci writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 13262306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 13862306a36Sopenharmony_ci struct altera_gpio_chip *chip; 13962306a36Sopenharmony_ci unsigned long flags; 14062306a36Sopenharmony_ci unsigned int gpio_ddr; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 14362306a36Sopenharmony_ci chip = gpiochip_get_data(gc); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->gpio_lock, flags); 14662306a36Sopenharmony_ci /* Set pin as input, assumes software controlled IP */ 14762306a36Sopenharmony_ci gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 14862306a36Sopenharmony_ci gpio_ddr &= ~BIT(offset); 14962306a36Sopenharmony_ci writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 15062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int altera_gpio_direction_output(struct gpio_chip *gc, 15662306a36Sopenharmony_ci unsigned offset, int value) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 15962306a36Sopenharmony_ci struct altera_gpio_chip *chip; 16062306a36Sopenharmony_ci unsigned long flags; 16162306a36Sopenharmony_ci unsigned int data_reg, gpio_ddr; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci mm_gc = to_of_mm_gpio_chip(gc); 16462306a36Sopenharmony_ci chip = gpiochip_get_data(gc); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci raw_spin_lock_irqsave(&chip->gpio_lock, flags); 16762306a36Sopenharmony_ci /* Sets the GPIO value */ 16862306a36Sopenharmony_ci data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 16962306a36Sopenharmony_ci if (value) 17062306a36Sopenharmony_ci data_reg |= BIT(offset); 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci data_reg &= ~BIT(offset); 17362306a36Sopenharmony_ci writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Set pin as output, assumes software controlled IP */ 17662306a36Sopenharmony_ci gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 17762306a36Sopenharmony_ci gpio_ddr |= BIT(offset); 17862306a36Sopenharmony_ci writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 17962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&chip->gpio_lock, flags); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void altera_gpio_irq_edge_handler(struct irq_desc *desc) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct altera_gpio_chip *altera_gc; 18762306a36Sopenharmony_ci struct irq_chip *chip; 18862306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 18962306a36Sopenharmony_ci struct irq_domain *irqdomain; 19062306a36Sopenharmony_ci unsigned long status; 19162306a36Sopenharmony_ci int i; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 19462306a36Sopenharmony_ci chip = irq_desc_get_chip(desc); 19562306a36Sopenharmony_ci mm_gc = &altera_gc->mmchip; 19662306a36Sopenharmony_ci irqdomain = altera_gc->mmchip.gc.irq.domain; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci chained_irq_enter(chip, desc); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci while ((status = 20162306a36Sopenharmony_ci (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & 20262306a36Sopenharmony_ci readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { 20362306a36Sopenharmony_ci writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); 20462306a36Sopenharmony_ci for_each_set_bit(i, &status, mm_gc->gc.ngpio) 20562306a36Sopenharmony_ci generic_handle_domain_irq(irqdomain, i); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci chained_irq_exit(chip, desc); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct altera_gpio_chip *altera_gc; 21462306a36Sopenharmony_ci struct irq_chip *chip; 21562306a36Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 21662306a36Sopenharmony_ci struct irq_domain *irqdomain; 21762306a36Sopenharmony_ci unsigned long status; 21862306a36Sopenharmony_ci int i; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 22162306a36Sopenharmony_ci chip = irq_desc_get_chip(desc); 22262306a36Sopenharmony_ci mm_gc = &altera_gc->mmchip; 22362306a36Sopenharmony_ci irqdomain = altera_gc->mmchip.gc.irq.domain; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci chained_irq_enter(chip, desc); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci status = readl(mm_gc->regs + ALTERA_GPIO_DATA); 22862306a36Sopenharmony_ci status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci for_each_set_bit(i, &status, mm_gc->gc.ngpio) 23162306a36Sopenharmony_ci generic_handle_domain_irq(irqdomain, i); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci chained_irq_exit(chip, desc); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic const struct irq_chip altera_gpio_irq_chip = { 23762306a36Sopenharmony_ci .name = "altera-gpio", 23862306a36Sopenharmony_ci .irq_mask = altera_gpio_irq_mask, 23962306a36Sopenharmony_ci .irq_unmask = altera_gpio_irq_unmask, 24062306a36Sopenharmony_ci .irq_set_type = altera_gpio_irq_set_type, 24162306a36Sopenharmony_ci .irq_startup = altera_gpio_irq_startup, 24262306a36Sopenharmony_ci .irq_shutdown = altera_gpio_irq_mask, 24362306a36Sopenharmony_ci .flags = IRQCHIP_IMMUTABLE, 24462306a36Sopenharmony_ci GPIOCHIP_IRQ_RESOURCE_HELPERS, 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int altera_gpio_probe(struct platform_device *pdev) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 25062306a36Sopenharmony_ci int reg, ret; 25162306a36Sopenharmony_ci struct altera_gpio_chip *altera_gc; 25262306a36Sopenharmony_ci struct gpio_irq_chip *girq; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); 25562306a36Sopenharmony_ci if (!altera_gc) 25662306a36Sopenharmony_ci return -ENOMEM; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci raw_spin_lock_init(&altera_gc->gpio_lock); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (of_property_read_u32(node, "altr,ngpio", ®)) 26162306a36Sopenharmony_ci /* By default assume maximum ngpio */ 26262306a36Sopenharmony_ci altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 26362306a36Sopenharmony_ci else 26462306a36Sopenharmony_ci altera_gc->mmchip.gc.ngpio = reg; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { 26762306a36Sopenharmony_ci dev_warn(&pdev->dev, 26862306a36Sopenharmony_ci "ngpio is greater than %d, defaulting to %d\n", 26962306a36Sopenharmony_ci ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); 27062306a36Sopenharmony_ci altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input; 27462306a36Sopenharmony_ci altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output; 27562306a36Sopenharmony_ci altera_gc->mmchip.gc.get = altera_gpio_get; 27662306a36Sopenharmony_ci altera_gc->mmchip.gc.set = altera_gpio_set; 27762306a36Sopenharmony_ci altera_gc->mmchip.gc.owner = THIS_MODULE; 27862306a36Sopenharmony_ci altera_gc->mmchip.gc.parent = &pdev->dev; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (altera_gc->mapped_irq < 0) 28362306a36Sopenharmony_ci goto skip_irq; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (of_property_read_u32(node, "altr,interrupt-type", ®)) { 28662306a36Sopenharmony_ci dev_err(&pdev->dev, 28762306a36Sopenharmony_ci "altr,interrupt-type value not set in device tree\n"); 28862306a36Sopenharmony_ci return -EINVAL; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci altera_gc->interrupt_trigger = reg; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci girq = &altera_gc->mmchip.gc.irq; 29362306a36Sopenharmony_ci gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) 29662306a36Sopenharmony_ci girq->parent_handler = altera_gpio_irq_leveL_high_handler; 29762306a36Sopenharmony_ci else 29862306a36Sopenharmony_ci girq->parent_handler = altera_gpio_irq_edge_handler; 29962306a36Sopenharmony_ci girq->num_parents = 1; 30062306a36Sopenharmony_ci girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents), 30162306a36Sopenharmony_ci GFP_KERNEL); 30262306a36Sopenharmony_ci if (!girq->parents) 30362306a36Sopenharmony_ci return -ENOMEM; 30462306a36Sopenharmony_ci girq->default_type = IRQ_TYPE_NONE; 30562306a36Sopenharmony_ci girq->handler = handle_bad_irq; 30662306a36Sopenharmony_ci girq->parents[0] = altera_gc->mapped_irq; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciskip_irq: 30962306a36Sopenharmony_ci ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc); 31062306a36Sopenharmony_ci if (ret) { 31162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci platform_set_drvdata(pdev, altera_gc); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int altera_gpio_remove(struct platform_device *pdev) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci of_mm_gpiochip_remove(&altera_gc->mmchip); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic const struct of_device_id altera_gpio_of_match[] = { 33062306a36Sopenharmony_ci { .compatible = "altr,pio-1.0", }, 33162306a36Sopenharmony_ci {}, 33262306a36Sopenharmony_ci}; 33362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, altera_gpio_of_match); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic struct platform_driver altera_gpio_driver = { 33662306a36Sopenharmony_ci .driver = { 33762306a36Sopenharmony_ci .name = "altera_gpio", 33862306a36Sopenharmony_ci .of_match_table = altera_gpio_of_match, 33962306a36Sopenharmony_ci }, 34062306a36Sopenharmony_ci .probe = altera_gpio_probe, 34162306a36Sopenharmony_ci .remove = altera_gpio_remove, 34262306a36Sopenharmony_ci}; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int __init altera_gpio_init(void) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci return platform_driver_register(&altera_gpio_driver); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_cisubsys_initcall(altera_gpio_init); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void __exit altera_gpio_exit(void) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci platform_driver_unregister(&altera_gpio_driver); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_cimodule_exit(altera_gpio_exit); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciMODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>"); 35762306a36Sopenharmony_ciMODULE_DESCRIPTION("Altera GPIO driver"); 35862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 359