| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2013 Altera Corporation |
|---|
| 3 | 4 | * Based on gpio-mpc8xxx.c |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 7 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 8 | | - * (at your option) any later version. |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 11 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | | - * GNU General Public License for more details. |
|---|
| 14 | | - * |
|---|
| 15 | | - * You should have received a copy of the GNU General Public License |
|---|
| 16 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | 5 | */ |
|---|
| 18 | 6 | |
|---|
| 19 | 7 | #include <linux/io.h> |
|---|
| .. | .. |
|---|
| 32 | 20 | * struct altera_gpio_chip |
|---|
| 33 | 21 | * @mmchip : memory mapped chip structure. |
|---|
| 34 | 22 | * @gpio_lock : synchronization lock so that new irq/set/get requests |
|---|
| 35 | | - will be blocked until the current one completes. |
|---|
| 23 | +* will be blocked until the current one completes. |
|---|
| 36 | 24 | * @interrupt_trigger : specifies the hardware configured IRQ trigger type |
|---|
| 37 | | - (rising, falling, both, high) |
|---|
| 25 | +* (rising, falling, both, high) |
|---|
| 38 | 26 | * @mapped_irq : kernel mapped irq number. |
|---|
| 27 | +* @irq_chip : IRQ chip configuration |
|---|
| 39 | 28 | */ |
|---|
| 40 | 29 | struct altera_gpio_chip { |
|---|
| 41 | 30 | struct of_mm_gpio_chip mmchip; |
|---|
| 42 | 31 | raw_spinlock_t gpio_lock; |
|---|
| 43 | 32 | int interrupt_trigger; |
|---|
| 44 | 33 | int mapped_irq; |
|---|
| 34 | + struct irq_chip irq_chip; |
|---|
| 45 | 35 | }; |
|---|
| 46 | 36 | |
|---|
| 47 | 37 | static void altera_gpio_irq_unmask(struct irq_data *d) |
|---|
| .. | .. |
|---|
| 80 | 70 | raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); |
|---|
| 81 | 71 | } |
|---|
| 82 | 72 | |
|---|
| 83 | | -/** |
|---|
| 73 | +/* |
|---|
| 84 | 74 | * This controller's IRQ type is synthesized in hardware, so this function |
|---|
| 85 | 75 | * just checks if the requested set_type matches the synthesized IRQ type |
|---|
| 86 | 76 | */ |
|---|
| .. | .. |
|---|
| 112 | 102 | |
|---|
| 113 | 103 | return 0; |
|---|
| 114 | 104 | } |
|---|
| 115 | | - |
|---|
| 116 | | -static struct irq_chip altera_irq_chip = { |
|---|
| 117 | | - .name = "altera-gpio", |
|---|
| 118 | | - .irq_mask = altera_gpio_irq_mask, |
|---|
| 119 | | - .irq_unmask = altera_gpio_irq_unmask, |
|---|
| 120 | | - .irq_set_type = altera_gpio_irq_set_type, |
|---|
| 121 | | - .irq_startup = altera_gpio_irq_startup, |
|---|
| 122 | | - .irq_shutdown = altera_gpio_irq_mask, |
|---|
| 123 | | -}; |
|---|
| 124 | 105 | |
|---|
| 125 | 106 | static int altera_gpio_get(struct gpio_chip *gc, unsigned offset) |
|---|
| 126 | 107 | { |
|---|
| .. | .. |
|---|
| 258 | 239 | struct device_node *node = pdev->dev.of_node; |
|---|
| 259 | 240 | int reg, ret; |
|---|
| 260 | 241 | struct altera_gpio_chip *altera_gc; |
|---|
| 242 | + struct gpio_irq_chip *girq; |
|---|
| 261 | 243 | |
|---|
| 262 | 244 | altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL); |
|---|
| 263 | 245 | if (!altera_gc) |
|---|
| .. | .. |
|---|
| 285 | 267 | altera_gc->mmchip.gc.owner = THIS_MODULE; |
|---|
| 286 | 268 | altera_gc->mmchip.gc.parent = &pdev->dev; |
|---|
| 287 | 269 | |
|---|
| 270 | + altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0); |
|---|
| 271 | + |
|---|
| 272 | + if (altera_gc->mapped_irq < 0) |
|---|
| 273 | + goto skip_irq; |
|---|
| 274 | + |
|---|
| 275 | + if (of_property_read_u32(node, "altr,interrupt-type", ®)) { |
|---|
| 276 | + dev_err(&pdev->dev, |
|---|
| 277 | + "altr,interrupt-type value not set in device tree\n"); |
|---|
| 278 | + return -EINVAL; |
|---|
| 279 | + } |
|---|
| 280 | + altera_gc->interrupt_trigger = reg; |
|---|
| 281 | + |
|---|
| 282 | + altera_gc->irq_chip.name = "altera-gpio"; |
|---|
| 283 | + altera_gc->irq_chip.irq_mask = altera_gpio_irq_mask; |
|---|
| 284 | + altera_gc->irq_chip.irq_unmask = altera_gpio_irq_unmask; |
|---|
| 285 | + altera_gc->irq_chip.irq_set_type = altera_gpio_irq_set_type; |
|---|
| 286 | + altera_gc->irq_chip.irq_startup = altera_gpio_irq_startup; |
|---|
| 287 | + altera_gc->irq_chip.irq_shutdown = altera_gpio_irq_mask; |
|---|
| 288 | + |
|---|
| 289 | + girq = &altera_gc->mmchip.gc.irq; |
|---|
| 290 | + girq->chip = &altera_gc->irq_chip; |
|---|
| 291 | + if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH) |
|---|
| 292 | + girq->parent_handler = altera_gpio_irq_leveL_high_handler; |
|---|
| 293 | + else |
|---|
| 294 | + girq->parent_handler = altera_gpio_irq_edge_handler; |
|---|
| 295 | + girq->num_parents = 1; |
|---|
| 296 | + girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents), |
|---|
| 297 | + GFP_KERNEL); |
|---|
| 298 | + if (!girq->parents) |
|---|
| 299 | + return -ENOMEM; |
|---|
| 300 | + girq->default_type = IRQ_TYPE_NONE; |
|---|
| 301 | + girq->handler = handle_bad_irq; |
|---|
| 302 | + girq->parents[0] = altera_gc->mapped_irq; |
|---|
| 303 | + |
|---|
| 304 | +skip_irq: |
|---|
| 288 | 305 | ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc); |
|---|
| 289 | 306 | if (ret) { |
|---|
| 290 | 307 | dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n"); |
|---|
| .. | .. |
|---|
| 293 | 310 | |
|---|
| 294 | 311 | platform_set_drvdata(pdev, altera_gc); |
|---|
| 295 | 312 | |
|---|
| 296 | | - altera_gc->mapped_irq = platform_get_irq(pdev, 0); |
|---|
| 297 | | - |
|---|
| 298 | | - if (altera_gc->mapped_irq < 0) |
|---|
| 299 | | - goto skip_irq; |
|---|
| 300 | | - |
|---|
| 301 | | - if (of_property_read_u32(node, "altr,interrupt-type", ®)) { |
|---|
| 302 | | - ret = -EINVAL; |
|---|
| 303 | | - dev_err(&pdev->dev, |
|---|
| 304 | | - "altr,interrupt-type value not set in device tree\n"); |
|---|
| 305 | | - goto teardown; |
|---|
| 306 | | - } |
|---|
| 307 | | - altera_gc->interrupt_trigger = reg; |
|---|
| 308 | | - |
|---|
| 309 | | - ret = gpiochip_irqchip_add(&altera_gc->mmchip.gc, &altera_irq_chip, 0, |
|---|
| 310 | | - handle_bad_irq, IRQ_TYPE_NONE); |
|---|
| 311 | | - |
|---|
| 312 | | - if (ret) { |
|---|
| 313 | | - dev_err(&pdev->dev, "could not add irqchip\n"); |
|---|
| 314 | | - goto teardown; |
|---|
| 315 | | - } |
|---|
| 316 | | - |
|---|
| 317 | | - gpiochip_set_chained_irqchip(&altera_gc->mmchip.gc, |
|---|
| 318 | | - &altera_irq_chip, |
|---|
| 319 | | - altera_gc->mapped_irq, |
|---|
| 320 | | - altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH ? |
|---|
| 321 | | - altera_gpio_irq_leveL_high_handler : |
|---|
| 322 | | - altera_gpio_irq_edge_handler); |
|---|
| 323 | | - |
|---|
| 324 | | -skip_irq: |
|---|
| 325 | 313 | return 0; |
|---|
| 326 | | -teardown: |
|---|
| 327 | | - of_mm_gpiochip_remove(&altera_gc->mmchip); |
|---|
| 328 | | - pr_err("%pOF: registration failed with status %d\n", |
|---|
| 329 | | - node, ret); |
|---|
| 330 | | - |
|---|
| 331 | | - return ret; |
|---|
| 332 | 314 | } |
|---|
| 333 | 315 | |
|---|
| 334 | 316 | static int altera_gpio_remove(struct platform_device *pdev) |
|---|