root/drivers/irqchip/irq-ts4800.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ts4800_irq_mask
  2. ts4800_irq_unmask
  3. ts4800_irqdomain_map
  4. ts4800_ic_chained_handle_irq
  5. ts4800_ic_probe
  6. ts4800_ic_remove

   1 /*
   2  * Multiplexed-IRQs driver for TS-4800's FPGA
   3  *
   4  * Copyright (c) 2015 - Savoir-faire Linux
   5  *
   6  * This file is licensed under the terms of the GNU General Public
   7  * License version 2. This program is licensed "as is" without any
   8  * warranty of any kind, whether express or implied.
   9  */
  10 
  11 #include <linux/interrupt.h>
  12 #include <linux/io.h>
  13 #include <linux/irq.h>
  14 #include <linux/irqchip.h>
  15 #include <linux/irqchip/chained_irq.h>
  16 #include <linux/irqdomain.h>
  17 #include <linux/module.h>
  18 #include <linux/of.h>
  19 #include <linux/of_address.h>
  20 #include <linux/of_irq.h>
  21 #include <linux/platform_device.h>
  22 
  23 #define IRQ_MASK        0x4
  24 #define IRQ_STATUS      0x8
  25 
  26 struct ts4800_irq_data {
  27         void __iomem            *base;
  28         struct irq_domain       *domain;
  29         struct irq_chip         irq_chip;
  30 };
  31 
  32 static void ts4800_irq_mask(struct irq_data *d)
  33 {
  34         struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d);
  35         u16 reg = readw(data->base + IRQ_MASK);
  36         u16 mask = 1 << d->hwirq;
  37 
  38         writew(reg | mask, data->base + IRQ_MASK);
  39 }
  40 
  41 static void ts4800_irq_unmask(struct irq_data *d)
  42 {
  43         struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d);
  44         u16 reg = readw(data->base + IRQ_MASK);
  45         u16 mask = 1 << d->hwirq;
  46 
  47         writew(reg & ~mask, data->base + IRQ_MASK);
  48 }
  49 
  50 static int ts4800_irqdomain_map(struct irq_domain *d, unsigned int irq,
  51                                 irq_hw_number_t hwirq)
  52 {
  53         struct ts4800_irq_data *data = d->host_data;
  54 
  55         irq_set_chip_and_handler(irq, &data->irq_chip, handle_simple_irq);
  56         irq_set_chip_data(irq, data);
  57         irq_set_noprobe(irq);
  58 
  59         return 0;
  60 }
  61 
  62 static const struct irq_domain_ops ts4800_ic_ops = {
  63         .map = ts4800_irqdomain_map,
  64         .xlate = irq_domain_xlate_onecell,
  65 };
  66 
  67 static void ts4800_ic_chained_handle_irq(struct irq_desc *desc)
  68 {
  69         struct ts4800_irq_data *data = irq_desc_get_handler_data(desc);
  70         struct irq_chip *chip = irq_desc_get_chip(desc);
  71         u16 status = readw(data->base + IRQ_STATUS);
  72 
  73         chained_irq_enter(chip, desc);
  74 
  75         if (unlikely(status == 0)) {
  76                 handle_bad_irq(desc);
  77                 goto out;
  78         }
  79 
  80         do {
  81                 unsigned int bit = __ffs(status);
  82                 int irq = irq_find_mapping(data->domain, bit);
  83 
  84                 status &= ~(1 << bit);
  85                 generic_handle_irq(irq);
  86         } while (status);
  87 
  88 out:
  89         chained_irq_exit(chip, desc);
  90 }
  91 
  92 static int ts4800_ic_probe(struct platform_device *pdev)
  93 {
  94         struct device_node *node = pdev->dev.of_node;
  95         struct ts4800_irq_data *data;
  96         struct irq_chip *irq_chip;
  97         struct resource *res;
  98         int parent_irq;
  99 
 100         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 101         if (!data)
 102                 return -ENOMEM;
 103 
 104         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 105         data->base = devm_ioremap_resource(&pdev->dev, res);
 106         if (IS_ERR(data->base))
 107                 return PTR_ERR(data->base);
 108 
 109         writew(0xFFFF, data->base + IRQ_MASK);
 110 
 111         parent_irq = irq_of_parse_and_map(node, 0);
 112         if (!parent_irq) {
 113                 dev_err(&pdev->dev, "failed to get parent IRQ\n");
 114                 return -EINVAL;
 115         }
 116 
 117         irq_chip = &data->irq_chip;
 118         irq_chip->name = dev_name(&pdev->dev);
 119         irq_chip->irq_mask = ts4800_irq_mask;
 120         irq_chip->irq_unmask = ts4800_irq_unmask;
 121 
 122         data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data);
 123         if (!data->domain) {
 124                 dev_err(&pdev->dev, "cannot add IRQ domain\n");
 125                 return -ENOMEM;
 126         }
 127 
 128         irq_set_chained_handler_and_data(parent_irq,
 129                                          ts4800_ic_chained_handle_irq, data);
 130 
 131         platform_set_drvdata(pdev, data);
 132 
 133         return 0;
 134 }
 135 
 136 static int ts4800_ic_remove(struct platform_device *pdev)
 137 {
 138         struct ts4800_irq_data *data = platform_get_drvdata(pdev);
 139 
 140         irq_domain_remove(data->domain);
 141 
 142         return 0;
 143 }
 144 
 145 static const struct of_device_id ts4800_ic_of_match[] = {
 146         { .compatible = "technologic,ts4800-irqc", },
 147         {},
 148 };
 149 MODULE_DEVICE_TABLE(of, ts4800_ic_of_match);
 150 
 151 static struct platform_driver ts4800_ic_driver = {
 152         .probe  = ts4800_ic_probe,
 153         .remove = ts4800_ic_remove,
 154         .driver = {
 155                 .name = "ts4800-irqc",
 156                 .of_match_table = ts4800_ic_of_match,
 157         },
 158 };
 159 module_platform_driver(ts4800_ic_driver);
 160 
 161 MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
 162 MODULE_LICENSE("GPL v2");
 163 MODULE_ALIAS("platform:ts4800_irqc");

/* [<][>][^][v][top][bottom][index][help] */