1c5abbba9STien Hock Loh /* 2c5abbba9STien Hock Loh * Copyright (C) 2013 Altera Corporation 3c5abbba9STien Hock Loh * Based on gpio-mpc8xxx.c 4c5abbba9STien Hock Loh * 5c5abbba9STien Hock Loh * This program is free software; you can redistribute it and/or modify 6c5abbba9STien Hock Loh * it under the terms of the GNU General Public License as published by 7c5abbba9STien Hock Loh * the Free Software Foundation; either version 2 of the License, or 8c5abbba9STien Hock Loh * (at your option) any later version. 9c5abbba9STien Hock Loh * 10c5abbba9STien Hock Loh * This program is distributed in the hope that it will be useful, 11c5abbba9STien Hock Loh * but WITHOUT ANY WARRANTY; without even the implied warranty of 12c5abbba9STien Hock Loh * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13c5abbba9STien Hock Loh * GNU General Public License for more details. 14c5abbba9STien Hock Loh * 15c5abbba9STien Hock Loh * You should have received a copy of the GNU General Public License 16c5abbba9STien Hock Loh * along with this program. If not, see <http://www.gnu.org/licenses/>. 17c5abbba9STien Hock Loh */ 18c5abbba9STien Hock Loh 19c5abbba9STien Hock Loh #include <linux/io.h> 20c5abbba9STien Hock Loh #include <linux/of_gpio.h> 21c5abbba9STien Hock Loh #include <linux/platform_device.h> 22c5abbba9STien Hock Loh 23c5abbba9STien Hock Loh #define ALTERA_GPIO_MAX_NGPIO 32 24c5abbba9STien Hock Loh #define ALTERA_GPIO_DATA 0x0 25c5abbba9STien Hock Loh #define ALTERA_GPIO_DIR 0x4 26c5abbba9STien Hock Loh #define ALTERA_GPIO_IRQ_MASK 0x8 27c5abbba9STien Hock Loh #define ALTERA_GPIO_EDGE_CAP 0xc 28c5abbba9STien Hock Loh 29c5abbba9STien Hock Loh /** 30c5abbba9STien Hock Loh * struct altera_gpio_chip 31c5abbba9STien Hock Loh * @mmchip : memory mapped chip structure. 32c5abbba9STien Hock Loh * @gpio_lock : synchronization lock so that new irq/set/get requests 33c5abbba9STien Hock Loh will be blocked until the current one completes. 34c5abbba9STien Hock Loh * @interrupt_trigger : specifies the hardware configured IRQ trigger type 35c5abbba9STien Hock Loh (rising, falling, both, high) 36c5abbba9STien Hock Loh * @mapped_irq : kernel mapped irq number. 37c5abbba9STien Hock Loh */ 38c5abbba9STien Hock Loh struct altera_gpio_chip { 39c5abbba9STien Hock Loh struct of_mm_gpio_chip mmchip; 40c5abbba9STien Hock Loh spinlock_t gpio_lock; 41c5abbba9STien Hock Loh int interrupt_trigger; 42c5abbba9STien Hock Loh int mapped_irq; 43c5abbba9STien Hock Loh }; 44c5abbba9STien Hock Loh 45c5abbba9STien Hock Loh static void altera_gpio_irq_unmask(struct irq_data *d) 46c5abbba9STien Hock Loh { 47c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 48c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 49c5abbba9STien Hock Loh unsigned long flags; 50c5abbba9STien Hock Loh u32 intmask; 51c5abbba9STien Hock Loh 52*397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 53c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 54c5abbba9STien Hock Loh 55c5abbba9STien Hock Loh spin_lock_irqsave(&altera_gc->gpio_lock, flags); 56c5abbba9STien Hock Loh intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 57c5abbba9STien Hock Loh /* Set ALTERA_GPIO_IRQ_MASK bit to unmask */ 58c5abbba9STien Hock Loh intmask |= BIT(irqd_to_hwirq(d)); 59c5abbba9STien Hock Loh writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 60c5abbba9STien Hock Loh spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 61c5abbba9STien Hock Loh } 62c5abbba9STien Hock Loh 63c5abbba9STien Hock Loh static void altera_gpio_irq_mask(struct irq_data *d) 64c5abbba9STien Hock Loh { 65c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 66c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 67c5abbba9STien Hock Loh unsigned long flags; 68c5abbba9STien Hock Loh u32 intmask; 69c5abbba9STien Hock Loh 70*397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 71c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 72c5abbba9STien Hock Loh 73c5abbba9STien Hock Loh spin_lock_irqsave(&altera_gc->gpio_lock, flags); 74c5abbba9STien Hock Loh intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 75c5abbba9STien Hock Loh /* Clear ALTERA_GPIO_IRQ_MASK bit to mask */ 76c5abbba9STien Hock Loh intmask &= ~BIT(irqd_to_hwirq(d)); 77c5abbba9STien Hock Loh writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 78c5abbba9STien Hock Loh spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); 79c5abbba9STien Hock Loh } 80c5abbba9STien Hock Loh 81c5abbba9STien Hock Loh /** 82c5abbba9STien Hock Loh * This controller's IRQ type is synthesized in hardware, so this function 83c5abbba9STien Hock Loh * just checks if the requested set_type matches the synthesized IRQ type 84c5abbba9STien Hock Loh */ 85c5abbba9STien Hock Loh static int altera_gpio_irq_set_type(struct irq_data *d, 86c5abbba9STien Hock Loh unsigned int type) 87c5abbba9STien Hock Loh { 88c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 89c5abbba9STien Hock Loh 90*397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d)); 91c5abbba9STien Hock Loh 92c5abbba9STien Hock Loh if (type == IRQ_TYPE_NONE) 93c5abbba9STien Hock Loh return 0; 94c5abbba9STien Hock Loh if (type == IRQ_TYPE_LEVEL_HIGH && 95c5abbba9STien Hock Loh altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) 96c5abbba9STien Hock Loh return 0; 97c5abbba9STien Hock Loh if (type == IRQ_TYPE_EDGE_RISING && 98c5abbba9STien Hock Loh altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_RISING) 99c5abbba9STien Hock Loh return 0; 100c5abbba9STien Hock Loh if (type == IRQ_TYPE_EDGE_FALLING && 101c5abbba9STien Hock Loh altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_FALLING) 102c5abbba9STien Hock Loh return 0; 103c5abbba9STien Hock Loh if (type == IRQ_TYPE_EDGE_BOTH && 104c5abbba9STien Hock Loh altera_gc->interrupt_trigger == IRQ_TYPE_EDGE_BOTH) 105c5abbba9STien Hock Loh return 0; 106c5abbba9STien Hock Loh 107c5abbba9STien Hock Loh return -EINVAL; 108c5abbba9STien Hock Loh } 109c5abbba9STien Hock Loh 11038e003f4SDaniel Lockyer static unsigned int altera_gpio_irq_startup(struct irq_data *d) 11138e003f4SDaniel Lockyer { 112c5abbba9STien Hock Loh altera_gpio_irq_unmask(d); 113c5abbba9STien Hock Loh 114c5abbba9STien Hock Loh return 0; 115c5abbba9STien Hock Loh } 116c5abbba9STien Hock Loh 117c5abbba9STien Hock Loh static struct irq_chip altera_irq_chip = { 118c5abbba9STien Hock Loh .name = "altera-gpio", 119c5abbba9STien Hock Loh .irq_mask = altera_gpio_irq_mask, 120c5abbba9STien Hock Loh .irq_unmask = altera_gpio_irq_unmask, 121c5abbba9STien Hock Loh .irq_set_type = altera_gpio_irq_set_type, 122c5abbba9STien Hock Loh .irq_startup = altera_gpio_irq_startup, 123c5abbba9STien Hock Loh .irq_shutdown = altera_gpio_irq_mask, 124c5abbba9STien Hock Loh }; 125c5abbba9STien Hock Loh 126c5abbba9STien Hock Loh static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) 127c5abbba9STien Hock Loh { 128c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 129c5abbba9STien Hock Loh 130c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 131c5abbba9STien Hock Loh 132c5abbba9STien Hock Loh return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset)); 133c5abbba9STien Hock Loh } 134c5abbba9STien Hock Loh 135c5abbba9STien Hock Loh static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 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 data_reg; 141c5abbba9STien Hock Loh 142c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 143*397d0773SLinus Walleij chip = gpiochip_get_data(gc); 144c5abbba9STien Hock Loh 145c5abbba9STien Hock Loh spin_lock_irqsave(&chip->gpio_lock, flags); 146c5abbba9STien Hock Loh data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 147c5abbba9STien Hock Loh if (value) 148c5abbba9STien Hock Loh data_reg |= BIT(offset); 149c5abbba9STien Hock Loh else 150c5abbba9STien Hock Loh data_reg &= ~BIT(offset); 151c5abbba9STien Hock Loh writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 152c5abbba9STien Hock Loh spin_unlock_irqrestore(&chip->gpio_lock, flags); 153c5abbba9STien Hock Loh } 154c5abbba9STien Hock Loh 155c5abbba9STien Hock Loh static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset) 156c5abbba9STien Hock Loh { 157c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 158c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 159c5abbba9STien Hock Loh unsigned long flags; 160c5abbba9STien Hock Loh unsigned int gpio_ddr; 161c5abbba9STien Hock Loh 162c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 163*397d0773SLinus Walleij chip = gpiochip_get_data(gc); 164c5abbba9STien Hock Loh 165c5abbba9STien Hock Loh spin_lock_irqsave(&chip->gpio_lock, flags); 166c5abbba9STien Hock Loh /* Set pin as input, assumes software controlled IP */ 167c5abbba9STien Hock Loh gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 168c5abbba9STien Hock Loh gpio_ddr &= ~BIT(offset); 169c5abbba9STien Hock Loh writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 170c5abbba9STien Hock Loh spin_unlock_irqrestore(&chip->gpio_lock, flags); 171c5abbba9STien Hock Loh 172c5abbba9STien Hock Loh return 0; 173c5abbba9STien Hock Loh } 174c5abbba9STien Hock Loh 175c5abbba9STien Hock Loh static int altera_gpio_direction_output(struct gpio_chip *gc, 176c5abbba9STien Hock Loh unsigned offset, int value) 177c5abbba9STien Hock Loh { 178c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 179c5abbba9STien Hock Loh struct altera_gpio_chip *chip; 180c5abbba9STien Hock Loh unsigned long flags; 181c5abbba9STien Hock Loh unsigned int data_reg, gpio_ddr; 182c5abbba9STien Hock Loh 183c5abbba9STien Hock Loh mm_gc = to_of_mm_gpio_chip(gc); 184*397d0773SLinus Walleij chip = gpiochip_get_data(gc); 185c5abbba9STien Hock Loh 186c5abbba9STien Hock Loh spin_lock_irqsave(&chip->gpio_lock, flags); 187c5abbba9STien Hock Loh /* Sets the GPIO value */ 188c5abbba9STien Hock Loh data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA); 189c5abbba9STien Hock Loh if (value) 190c5abbba9STien Hock Loh data_reg |= BIT(offset); 191c5abbba9STien Hock Loh else 192c5abbba9STien Hock Loh data_reg &= ~BIT(offset); 193c5abbba9STien Hock Loh writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA); 194c5abbba9STien Hock Loh 195c5abbba9STien Hock Loh /* Set pin as output, assumes software controlled IP */ 196c5abbba9STien Hock Loh gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR); 197c5abbba9STien Hock Loh gpio_ddr |= BIT(offset); 198c5abbba9STien Hock Loh writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR); 199c5abbba9STien Hock Loh spin_unlock_irqrestore(&chip->gpio_lock, flags); 200c5abbba9STien Hock Loh 201c5abbba9STien Hock Loh return 0; 202c5abbba9STien Hock Loh } 203c5abbba9STien Hock Loh 204bd0b9ac4SThomas Gleixner static void altera_gpio_irq_edge_handler(struct irq_desc *desc) 205c5abbba9STien Hock Loh { 206c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 207c5abbba9STien Hock Loh struct irq_chip *chip; 208c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 209c5abbba9STien Hock Loh struct irq_domain *irqdomain; 210c5abbba9STien Hock Loh unsigned long status; 211c5abbba9STien Hock Loh int i; 212c5abbba9STien Hock Loh 213*397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 214c5abbba9STien Hock Loh chip = irq_desc_get_chip(desc); 215c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 216c5abbba9STien Hock Loh irqdomain = altera_gc->mmchip.gc.irqdomain; 217c5abbba9STien Hock Loh 218c5abbba9STien Hock Loh chained_irq_enter(chip, desc); 219c5abbba9STien Hock Loh 220c5abbba9STien Hock Loh while ((status = 221c5abbba9STien Hock Loh (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) & 222c5abbba9STien Hock Loh readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) { 223c5abbba9STien Hock Loh writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP); 224c5abbba9STien Hock Loh for_each_set_bit(i, &status, mm_gc->gc.ngpio) { 225c5abbba9STien Hock Loh generic_handle_irq(irq_find_mapping(irqdomain, i)); 226c5abbba9STien Hock Loh } 227c5abbba9STien Hock Loh } 228c5abbba9STien Hock Loh 229c5abbba9STien Hock Loh chained_irq_exit(chip, desc); 230c5abbba9STien Hock Loh } 231c5abbba9STien Hock Loh 232c5abbba9STien Hock Loh 233bd0b9ac4SThomas Gleixner static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) 234c5abbba9STien Hock Loh { 235c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 236c5abbba9STien Hock Loh struct irq_chip *chip; 237c5abbba9STien Hock Loh struct of_mm_gpio_chip *mm_gc; 238c5abbba9STien Hock Loh struct irq_domain *irqdomain; 239c5abbba9STien Hock Loh unsigned long status; 240c5abbba9STien Hock Loh int i; 241c5abbba9STien Hock Loh 242*397d0773SLinus Walleij altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); 243c5abbba9STien Hock Loh chip = irq_desc_get_chip(desc); 244c5abbba9STien Hock Loh mm_gc = &altera_gc->mmchip; 245c5abbba9STien Hock Loh irqdomain = altera_gc->mmchip.gc.irqdomain; 246c5abbba9STien Hock Loh 247c5abbba9STien Hock Loh chained_irq_enter(chip, desc); 248c5abbba9STien Hock Loh 249c5abbba9STien Hock Loh status = readl(mm_gc->regs + ALTERA_GPIO_DATA); 250c5abbba9STien Hock Loh status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK); 251c5abbba9STien Hock Loh 252c5abbba9STien Hock Loh for_each_set_bit(i, &status, mm_gc->gc.ngpio) { 253c5abbba9STien Hock Loh generic_handle_irq(irq_find_mapping(irqdomain, i)); 254c5abbba9STien Hock Loh } 255c5abbba9STien Hock Loh chained_irq_exit(chip, desc); 256c5abbba9STien Hock Loh } 257c5abbba9STien Hock Loh 258c4b40493Skbuild test robot static int altera_gpio_probe(struct platform_device *pdev) 259c5abbba9STien Hock Loh { 260c5abbba9STien Hock Loh struct device_node *node = pdev->dev.of_node; 261c5abbba9STien Hock Loh int reg, ret; 262c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc; 263c5abbba9STien Hock Loh 264c5abbba9STien Hock Loh altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); 265c5abbba9STien Hock Loh if (!altera_gc) 266c5abbba9STien Hock Loh return -ENOMEM; 267c5abbba9STien Hock Loh 268c5abbba9STien Hock Loh spin_lock_init(&altera_gc->gpio_lock); 269c5abbba9STien Hock Loh 270c5abbba9STien Hock Loh if (of_property_read_u32(node, "altr,ngpio", ®)) 271c5abbba9STien Hock Loh /* By default assume maximum ngpio */ 272c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 273c5abbba9STien Hock Loh else 274c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = reg; 275c5abbba9STien Hock Loh 276c5abbba9STien Hock Loh if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) { 277c5abbba9STien Hock Loh dev_warn(&pdev->dev, 278c5abbba9STien Hock Loh "ngpio is greater than %d, defaulting to %d\n", 279c5abbba9STien Hock Loh ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO); 280c5abbba9STien Hock Loh altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO; 281c5abbba9STien Hock Loh } 282c5abbba9STien Hock Loh 283c5abbba9STien Hock Loh altera_gc->mmchip.gc.direction_input = altera_gpio_direction_input; 284c5abbba9STien Hock Loh altera_gc->mmchip.gc.direction_output = altera_gpio_direction_output; 285c5abbba9STien Hock Loh altera_gc->mmchip.gc.get = altera_gpio_get; 286c5abbba9STien Hock Loh altera_gc->mmchip.gc.set = altera_gpio_set; 287c5abbba9STien Hock Loh altera_gc->mmchip.gc.owner = THIS_MODULE; 28858383c78SLinus Walleij altera_gc->mmchip.gc.parent = &pdev->dev; 289c5abbba9STien Hock Loh 290*397d0773SLinus Walleij ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc); 291c5abbba9STien Hock Loh if (ret) { 292c5abbba9STien Hock Loh dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); 293c5abbba9STien Hock Loh return ret; 294c5abbba9STien Hock Loh } 295c5abbba9STien Hock Loh 296c5abbba9STien Hock Loh platform_set_drvdata(pdev, altera_gc); 297c5abbba9STien Hock Loh 298c5abbba9STien Hock Loh altera_gc->mapped_irq = platform_get_irq(pdev, 0); 299c5abbba9STien Hock Loh 300c5abbba9STien Hock Loh if (altera_gc->mapped_irq < 0) 301c5abbba9STien Hock Loh goto skip_irq; 302c5abbba9STien Hock Loh 303c5abbba9STien Hock Loh if (of_property_read_u32(node, "altr,interrupt-type", ®)) { 304c5abbba9STien Hock Loh ret = -EINVAL; 305c5abbba9STien Hock Loh dev_err(&pdev->dev, 306c5abbba9STien Hock Loh "altr,interrupt-type value not set in device tree\n"); 307c5abbba9STien Hock Loh goto teardown; 308c5abbba9STien Hock Loh } 309c5abbba9STien Hock Loh altera_gc->interrupt_trigger = reg; 310c5abbba9STien Hock Loh 311c5abbba9STien Hock Loh ret = gpiochip_irqchip_add(&altera_gc->mmchip.gc, &altera_irq_chip, 0, 312c5abbba9STien Hock Loh handle_simple_irq, IRQ_TYPE_NONE); 313c5abbba9STien Hock Loh 314c5abbba9STien Hock Loh if (ret) { 315c5abbba9STien Hock Loh dev_info(&pdev->dev, "could not add irqchip\n"); 316c5abbba9STien Hock Loh return ret; 317c5abbba9STien Hock Loh } 318c5abbba9STien Hock Loh 319c5abbba9STien Hock Loh gpiochip_set_chained_irqchip(&altera_gc->mmchip.gc, 320c5abbba9STien Hock Loh &altera_irq_chip, 321c5abbba9STien Hock Loh altera_gc->mapped_irq, 322c5abbba9STien Hock Loh altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH ? 323c5abbba9STien Hock Loh altera_gpio_irq_leveL_high_handler : 324c5abbba9STien Hock Loh altera_gpio_irq_edge_handler); 325c5abbba9STien Hock Loh 326c5abbba9STien Hock Loh skip_irq: 327c5abbba9STien Hock Loh return 0; 328c5abbba9STien Hock Loh teardown: 329c5abbba9STien Hock Loh pr_err("%s: registration failed with status %d\n", 330c5abbba9STien Hock Loh node->full_name, ret); 331c5abbba9STien Hock Loh 332c5abbba9STien Hock Loh return ret; 333c5abbba9STien Hock Loh } 334c5abbba9STien Hock Loh 335c5abbba9STien Hock Loh static int altera_gpio_remove(struct platform_device *pdev) 336c5abbba9STien Hock Loh { 337c5abbba9STien Hock Loh struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev); 338c5abbba9STien Hock Loh 33941ec66c9SMasahiro Yamada of_mm_gpiochip_remove(&altera_gc->mmchip); 340c5abbba9STien Hock Loh 3411c8b5d68SMasahiro Yamada return 0; 342c5abbba9STien Hock Loh } 343c5abbba9STien Hock Loh 344c5abbba9STien Hock Loh static const struct of_device_id altera_gpio_of_match[] = { 345c5abbba9STien Hock Loh { .compatible = "altr,pio-1.0", }, 346c5abbba9STien Hock Loh {}, 347c5abbba9STien Hock Loh }; 348c5abbba9STien Hock Loh MODULE_DEVICE_TABLE(of, altera_gpio_of_match); 349c5abbba9STien Hock Loh 350c5abbba9STien Hock Loh static struct platform_driver altera_gpio_driver = { 351c5abbba9STien Hock Loh .driver = { 352c5abbba9STien Hock Loh .name = "altera_gpio", 353c5abbba9STien Hock Loh .of_match_table = of_match_ptr(altera_gpio_of_match), 354c5abbba9STien Hock Loh }, 355c5abbba9STien Hock Loh .probe = altera_gpio_probe, 356c5abbba9STien Hock Loh .remove = altera_gpio_remove, 357c5abbba9STien Hock Loh }; 358c5abbba9STien Hock Loh 359c5abbba9STien Hock Loh static int __init altera_gpio_init(void) 360c5abbba9STien Hock Loh { 361c5abbba9STien Hock Loh return platform_driver_register(&altera_gpio_driver); 362c5abbba9STien Hock Loh } 363c5abbba9STien Hock Loh subsys_initcall(altera_gpio_init); 364c5abbba9STien Hock Loh 365c5abbba9STien Hock Loh static void __exit altera_gpio_exit(void) 366c5abbba9STien Hock Loh { 367c5abbba9STien Hock Loh platform_driver_unregister(&altera_gpio_driver); 368c5abbba9STien Hock Loh } 369c5abbba9STien Hock Loh module_exit(altera_gpio_exit); 370c5abbba9STien Hock Loh 371c5abbba9STien Hock Loh MODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>"); 372c5abbba9STien Hock Loh MODULE_DESCRIPTION("Altera GPIO driver"); 373c5abbba9STien Hock Loh MODULE_LICENSE("GPL"); 374