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>
22 #include <linux/seq_file.h>
25 #define IRQ_STATUS 0x8
27 struct ts4800_irq_data
{
29 struct platform_device
*pdev
;
30 struct irq_domain
*domain
;
33 static void ts4800_irq_mask(struct irq_data
*d
)
35 struct ts4800_irq_data
*data
= irq_data_get_irq_chip_data(d
);
36 u16 reg
= readw(data
->base
+ IRQ_MASK
);
37 u16 mask
= 1 << d
->hwirq
;
39 writew(reg
| mask
, data
->base
+ IRQ_MASK
);
42 static void ts4800_irq_unmask(struct irq_data
*d
)
44 struct ts4800_irq_data
*data
= irq_data_get_irq_chip_data(d
);
45 u16 reg
= readw(data
->base
+ IRQ_MASK
);
46 u16 mask
= 1 << d
->hwirq
;
48 writew(reg
& ~mask
, data
->base
+ IRQ_MASK
);
51 static void ts4800_irq_print_chip(struct irq_data
*d
, struct seq_file
*p
)
53 struct ts4800_irq_data
*data
= irq_data_get_irq_chip_data(d
);
55 seq_printf(p
, "%s", dev_name(&data
->pdev
->dev
));
58 static const struct irq_chip ts4800_chip
= {
59 .irq_mask
= ts4800_irq_mask
,
60 .irq_unmask
= ts4800_irq_unmask
,
61 .irq_print_chip
= ts4800_irq_print_chip
,
64 static int ts4800_irqdomain_map(struct irq_domain
*d
, unsigned int irq
,
65 irq_hw_number_t hwirq
)
67 struct ts4800_irq_data
*data
= d
->host_data
;
69 irq_set_chip_and_handler(irq
, &ts4800_chip
, handle_simple_irq
);
70 irq_set_chip_data(irq
, data
);
76 static const struct irq_domain_ops ts4800_ic_ops
= {
77 .map
= ts4800_irqdomain_map
,
78 .xlate
= irq_domain_xlate_onecell
,
81 static void ts4800_ic_chained_handle_irq(struct irq_desc
*desc
)
83 struct ts4800_irq_data
*data
= irq_desc_get_handler_data(desc
);
84 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
85 u16 status
= readw(data
->base
+ IRQ_STATUS
);
87 chained_irq_enter(chip
, desc
);
89 if (unlikely(status
== 0)) {
95 unsigned int bit
= __ffs(status
);
97 generic_handle_domain_irq(data
->domain
, bit
);
98 status
&= ~(1 << bit
);
102 chained_irq_exit(chip
, desc
);
105 static int ts4800_ic_probe(struct platform_device
*pdev
)
107 struct device_node
*node
= pdev
->dev
.of_node
;
108 struct ts4800_irq_data
*data
;
111 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
116 data
->base
= devm_platform_ioremap_resource(pdev
, 0);
117 if (IS_ERR(data
->base
))
118 return PTR_ERR(data
->base
);
120 writew(0xFFFF, data
->base
+ IRQ_MASK
);
122 parent_irq
= irq_of_parse_and_map(node
, 0);
124 dev_err(&pdev
->dev
, "failed to get parent IRQ\n");
128 data
->domain
= irq_domain_add_linear(node
, 8, &ts4800_ic_ops
, data
);
130 dev_err(&pdev
->dev
, "cannot add IRQ domain\n");
134 irq_set_chained_handler_and_data(parent_irq
,
135 ts4800_ic_chained_handle_irq
, data
);
137 platform_set_drvdata(pdev
, data
);
142 static void ts4800_ic_remove(struct platform_device
*pdev
)
144 struct ts4800_irq_data
*data
= platform_get_drvdata(pdev
);
146 irq_domain_remove(data
->domain
);
149 static const struct of_device_id ts4800_ic_of_match
[] = {
150 { .compatible
= "technologic,ts4800-irqc", },
153 MODULE_DEVICE_TABLE(of
, ts4800_ic_of_match
);
155 static struct platform_driver ts4800_ic_driver
= {
156 .probe
= ts4800_ic_probe
,
157 .remove
= ts4800_ic_remove
,
159 .name
= "ts4800-irqc",
160 .of_match_table
= ts4800_ic_of_match
,
163 module_platform_driver(ts4800_ic_driver
);
165 MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
166 MODULE_DESCRIPTION("Multiplexed-IRQs driver for TS-4800's FPGA");
167 MODULE_LICENSE("GPL v2");
168 MODULE_ALIAS("platform:ts4800_irqc");