1 // SPDX-License-Identifier: GPL-2.0-only
3 * Driver for UniPhier AIDET (ARM Interrupt Detector)
5 * Copyright (C) 2017 Socionext Inc.
6 * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
9 #include <linux/bitops.h>
10 #include <linux/init.h>
11 #include <linux/irq.h>
12 #include <linux/irqdomain.h>
13 #include <linux/kernel.h>
15 #include <linux/of_device.h>
16 #include <linux/of_irq.h>
17 #include <linux/platform_device.h>
18 #include <linux/spinlock.h>
20 #define UNIPHIER_AIDET_NR_IRQS 256
22 #define UNIPHIER_AIDET_DETCONF 0x04 /* inverter register base */
24 struct uniphier_aidet_priv
{
25 struct irq_domain
*domain
;
26 void __iomem
*reg_base
;
28 u32 saved_vals
[UNIPHIER_AIDET_NR_IRQS
/ 32];
31 static void uniphier_aidet_reg_update(struct uniphier_aidet_priv
*priv
,
32 unsigned int reg
, u32 mask
, u32 val
)
37 spin_lock_irqsave(&priv
->lock
, flags
);
38 tmp
= readl_relaxed(priv
->reg_base
+ reg
);
41 writel_relaxed(tmp
, priv
->reg_base
+ reg
);
42 spin_unlock_irqrestore(&priv
->lock
, flags
);
45 static void uniphier_aidet_detconf_update(struct uniphier_aidet_priv
*priv
,
46 unsigned long index
, unsigned int val
)
51 reg
= UNIPHIER_AIDET_DETCONF
+ index
/ 32 * 4;
52 mask
= BIT(index
% 32);
54 uniphier_aidet_reg_update(priv
, reg
, mask
, val
? mask
: 0);
57 static int uniphier_aidet_irq_set_type(struct irq_data
*data
, unsigned int type
)
59 struct uniphier_aidet_priv
*priv
= data
->chip_data
;
62 /* enable inverter for active low triggers */
64 case IRQ_TYPE_EDGE_RISING
:
65 case IRQ_TYPE_LEVEL_HIGH
:
68 case IRQ_TYPE_EDGE_FALLING
:
70 type
= IRQ_TYPE_EDGE_RISING
;
72 case IRQ_TYPE_LEVEL_LOW
:
74 type
= IRQ_TYPE_LEVEL_HIGH
;
80 uniphier_aidet_detconf_update(priv
, data
->hwirq
, val
);
82 return irq_chip_set_type_parent(data
, type
);
85 static struct irq_chip uniphier_aidet_irq_chip
= {
87 .irq_mask
= irq_chip_mask_parent
,
88 .irq_unmask
= irq_chip_unmask_parent
,
89 .irq_eoi
= irq_chip_eoi_parent
,
90 .irq_set_affinity
= irq_chip_set_affinity_parent
,
91 .irq_set_type
= uniphier_aidet_irq_set_type
,
94 static int uniphier_aidet_domain_translate(struct irq_domain
*domain
,
95 struct irq_fwspec
*fwspec
,
96 unsigned long *out_hwirq
,
97 unsigned int *out_type
)
99 if (WARN_ON(fwspec
->param_count
< 2))
102 *out_hwirq
= fwspec
->param
[0];
103 *out_type
= fwspec
->param
[1] & IRQ_TYPE_SENSE_MASK
;
108 static int uniphier_aidet_domain_alloc(struct irq_domain
*domain
,
109 unsigned int virq
, unsigned int nr_irqs
,
112 struct irq_fwspec parent_fwspec
;
113 irq_hw_number_t hwirq
;
120 ret
= uniphier_aidet_domain_translate(domain
, arg
, &hwirq
, &type
);
125 case IRQ_TYPE_EDGE_RISING
:
126 case IRQ_TYPE_LEVEL_HIGH
:
128 case IRQ_TYPE_EDGE_FALLING
:
129 type
= IRQ_TYPE_EDGE_RISING
;
131 case IRQ_TYPE_LEVEL_LOW
:
132 type
= IRQ_TYPE_LEVEL_HIGH
;
138 if (hwirq
>= UNIPHIER_AIDET_NR_IRQS
)
141 ret
= irq_domain_set_hwirq_and_chip(domain
, virq
, hwirq
,
142 &uniphier_aidet_irq_chip
,
148 parent_fwspec
.fwnode
= domain
->parent
->fwnode
;
149 parent_fwspec
.param_count
= 3;
150 parent_fwspec
.param
[0] = 0; /* SPI */
151 parent_fwspec
.param
[1] = hwirq
;
152 parent_fwspec
.param
[2] = type
;
154 return irq_domain_alloc_irqs_parent(domain
, virq
, 1, &parent_fwspec
);
157 static const struct irq_domain_ops uniphier_aidet_domain_ops
= {
158 .alloc
= uniphier_aidet_domain_alloc
,
159 .free
= irq_domain_free_irqs_common
,
160 .translate
= uniphier_aidet_domain_translate
,
163 static int uniphier_aidet_probe(struct platform_device
*pdev
)
165 struct device
*dev
= &pdev
->dev
;
166 struct device_node
*parent_np
;
167 struct irq_domain
*parent_domain
;
168 struct uniphier_aidet_priv
*priv
;
170 parent_np
= of_irq_find_parent(dev
->of_node
);
174 parent_domain
= irq_find_host(parent_np
);
175 of_node_put(parent_np
);
177 return -EPROBE_DEFER
;
179 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
183 priv
->reg_base
= devm_platform_ioremap_resource(pdev
, 0);
184 if (IS_ERR(priv
->reg_base
))
185 return PTR_ERR(priv
->reg_base
);
187 spin_lock_init(&priv
->lock
);
189 priv
->domain
= irq_domain_create_hierarchy(
191 UNIPHIER_AIDET_NR_IRQS
,
192 of_node_to_fwnode(dev
->of_node
),
193 &uniphier_aidet_domain_ops
, priv
);
197 platform_set_drvdata(pdev
, priv
);
202 static int __maybe_unused
uniphier_aidet_suspend(struct device
*dev
)
204 struct uniphier_aidet_priv
*priv
= dev_get_drvdata(dev
);
207 for (i
= 0; i
< ARRAY_SIZE(priv
->saved_vals
); i
++)
208 priv
->saved_vals
[i
] = readl_relaxed(
209 priv
->reg_base
+ UNIPHIER_AIDET_DETCONF
+ i
* 4);
214 static int __maybe_unused
uniphier_aidet_resume(struct device
*dev
)
216 struct uniphier_aidet_priv
*priv
= dev_get_drvdata(dev
);
219 for (i
= 0; i
< ARRAY_SIZE(priv
->saved_vals
); i
++)
220 writel_relaxed(priv
->saved_vals
[i
],
221 priv
->reg_base
+ UNIPHIER_AIDET_DETCONF
+ i
* 4);
226 static const struct dev_pm_ops uniphier_aidet_pm_ops
= {
227 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(uniphier_aidet_suspend
,
228 uniphier_aidet_resume
)
231 static const struct of_device_id uniphier_aidet_match
[] = {
232 { .compatible
= "socionext,uniphier-ld4-aidet" },
233 { .compatible
= "socionext,uniphier-pro4-aidet" },
234 { .compatible
= "socionext,uniphier-sld8-aidet" },
235 { .compatible
= "socionext,uniphier-pro5-aidet" },
236 { .compatible
= "socionext,uniphier-pxs2-aidet" },
237 { .compatible
= "socionext,uniphier-ld11-aidet" },
238 { .compatible
= "socionext,uniphier-ld20-aidet" },
239 { .compatible
= "socionext,uniphier-pxs3-aidet" },
243 static struct platform_driver uniphier_aidet_driver
= {
244 .probe
= uniphier_aidet_probe
,
246 .name
= "uniphier-aidet",
247 .of_match_table
= uniphier_aidet_match
,
248 .pm
= &uniphier_aidet_pm_ops
,
251 builtin_platform_driver(uniphier_aidet_driver
);