2 * Multiplexed-IRQs driver for TS-4800's FPGA
4 * Copyright (c) 2015 - Savoir-faire Linux
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.
11 #include <linux/interrupt.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>
19 #include <linux/of_address.h>
20 #include <linux/of_irq.h>
21 #include <linux/platform_device.h>
24 #define IRQ_STATUS 0x8
26 struct ts4800_irq_data
{
28 struct irq_domain
*domain
;
29 struct irq_chip irq_chip
;
32 static void ts4800_irq_mask(struct irq_data
*d
)
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
;
38 writew(reg
| mask
, data
->base
+ IRQ_MASK
);
41 static void ts4800_irq_unmask(struct irq_data
*d
)
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
;
47 writew(reg
& ~mask
, data
->base
+ IRQ_MASK
);
50 static int ts4800_irqdomain_map(struct irq_domain
*d
, unsigned int irq
,
51 irq_hw_number_t hwirq
)
53 struct ts4800_irq_data
*data
= d
->host_data
;
55 irq_set_chip_and_handler(irq
, &data
->irq_chip
, handle_simple_irq
);
56 irq_set_chip_data(irq
, data
);
62 static const struct irq_domain_ops ts4800_ic_ops
= {
63 .map
= ts4800_irqdomain_map
,
64 .xlate
= irq_domain_xlate_onecell
,
67 static void ts4800_ic_chained_handle_irq(struct irq_desc
*desc
)
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
);
73 chained_irq_enter(chip
, desc
);
75 if (unlikely(status
== 0)) {
81 unsigned int bit
= __ffs(status
);
82 int irq
= irq_find_mapping(data
->domain
, bit
);
84 status
&= ~(1 << bit
);
85 generic_handle_irq(irq
);
89 chained_irq_exit(chip
, desc
);
92 static int ts4800_ic_probe(struct platform_device
*pdev
)
94 struct device_node
*node
= pdev
->dev
.of_node
;
95 struct ts4800_irq_data
*data
;
96 struct irq_chip
*irq_chip
;
100 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
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
);
109 writew(0xFFFF, data
->base
+ IRQ_MASK
);
111 parent_irq
= irq_of_parse_and_map(node
, 0);
113 dev_err(&pdev
->dev
, "failed to get parent IRQ\n");
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
;
122 data
->domain
= irq_domain_add_linear(node
, 8, &ts4800_ic_ops
, data
);
124 dev_err(&pdev
->dev
, "cannot add IRQ domain\n");
128 irq_set_chained_handler_and_data(parent_irq
,
129 ts4800_ic_chained_handle_irq
, data
);
131 platform_set_drvdata(pdev
, data
);
136 static int ts4800_ic_remove(struct platform_device
*pdev
)
138 struct ts4800_irq_data
*data
= platform_get_drvdata(pdev
);
140 irq_domain_remove(data
->domain
);
145 static const struct of_device_id ts4800_ic_of_match
[] = {
146 { .compatible
= "technologic,ts4800-irqc", },
149 MODULE_DEVICE_TABLE(of
, ts4800_ic_of_match
);
151 static struct platform_driver ts4800_ic_driver
= {
152 .probe
= ts4800_ic_probe
,
153 .remove
= ts4800_ic_remove
,
155 .name
= "ts4800-irqc",
156 .of_match_table
= ts4800_ic_of_match
,
159 module_platform_driver(ts4800_ic_driver
);
161 MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
162 MODULE_LICENSE("GPL v2");
163 MODULE_ALIAS("platform:ts4800_irqc");