1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Aspeed AST24XX, AST25XX, and AST26XX SCU Interrupt Controller
4 * Copyright 2019 IBM Corporation
6 * Eddie James <eajames@linux.ibm.com>
9 #include <linux/bitops.h>
10 #include <linux/irq.h>
11 #include <linux/irqchip.h>
12 #include <linux/irqchip/chained_irq.h>
13 #include <linux/irqdomain.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/of_irq.h>
16 #include <linux/regmap.h>
18 #define ASPEED_SCU_IC_REG 0x018
19 #define ASPEED_SCU_IC_SHIFT 0
20 #define ASPEED_SCU_IC_ENABLE GENMASK(6, ASPEED_SCU_IC_SHIFT)
21 #define ASPEED_SCU_IC_NUM_IRQS 7
22 #define ASPEED_SCU_IC_STATUS_SHIFT 16
24 #define ASPEED_AST2600_SCU_IC0_REG 0x560
25 #define ASPEED_AST2600_SCU_IC0_SHIFT 0
26 #define ASPEED_AST2600_SCU_IC0_ENABLE \
27 GENMASK(5, ASPEED_AST2600_SCU_IC0_SHIFT)
28 #define ASPEED_AST2600_SCU_IC0_NUM_IRQS 6
30 #define ASPEED_AST2600_SCU_IC1_REG 0x570
31 #define ASPEED_AST2600_SCU_IC1_SHIFT 4
32 #define ASPEED_AST2600_SCU_IC1_ENABLE \
33 GENMASK(5, ASPEED_AST2600_SCU_IC1_SHIFT)
34 #define ASPEED_AST2600_SCU_IC1_NUM_IRQS 2
36 struct aspeed_scu_ic
{
37 unsigned long irq_enable
;
38 unsigned long irq_shift
;
39 unsigned int num_irqs
;
42 struct irq_domain
*irq_domain
;
45 static void aspeed_scu_ic_irq_handler(struct irq_desc
*desc
)
50 unsigned long enabled
;
53 struct aspeed_scu_ic
*scu_ic
= irq_desc_get_handler_data(desc
);
54 struct irq_chip
*chip
= irq_desc_get_chip(desc
);
55 unsigned int mask
= scu_ic
->irq_enable
<< ASPEED_SCU_IC_STATUS_SHIFT
;
57 chained_irq_enter(chip
, desc
);
60 * The SCU IC has just one register to control its operation and read
61 * status. The interrupt enable bits occupy the lower 16 bits of the
62 * register, while the interrupt status bits occupy the upper 16 bits.
63 * The status bit for a given interrupt is always 16 bits shifted from
64 * the enable bit for the same interrupt.
65 * Therefore, perform the IRQ operations in the enable bit space by
66 * shifting the status down to get the mapping and then back up to
69 regmap_read(scu_ic
->scu
, scu_ic
->reg
, &sts
);
70 enabled
= sts
& scu_ic
->irq_enable
;
71 status
= (sts
>> ASPEED_SCU_IC_STATUS_SHIFT
) & enabled
;
73 bit
= scu_ic
->irq_shift
;
74 max
= scu_ic
->num_irqs
+ bit
;
76 for_each_set_bit_from(bit
, &status
, max
) {
77 irq
= irq_find_mapping(scu_ic
->irq_domain
,
78 bit
- scu_ic
->irq_shift
);
79 generic_handle_irq(irq
);
81 regmap_update_bits(scu_ic
->scu
, scu_ic
->reg
, mask
,
82 BIT(bit
+ ASPEED_SCU_IC_STATUS_SHIFT
));
85 chained_irq_exit(chip
, desc
);
88 static void aspeed_scu_ic_irq_mask(struct irq_data
*data
)
90 struct aspeed_scu_ic
*scu_ic
= irq_data_get_irq_chip_data(data
);
91 unsigned int mask
= BIT(data
->hwirq
+ scu_ic
->irq_shift
) |
92 (scu_ic
->irq_enable
<< ASPEED_SCU_IC_STATUS_SHIFT
);
95 * Status bits are cleared by writing 1. In order to prevent the mask
96 * operation from clearing the status bits, they should be under the
97 * mask and written with 0.
99 regmap_update_bits(scu_ic
->scu
, scu_ic
->reg
, mask
, 0);
102 static void aspeed_scu_ic_irq_unmask(struct irq_data
*data
)
104 struct aspeed_scu_ic
*scu_ic
= irq_data_get_irq_chip_data(data
);
105 unsigned int bit
= BIT(data
->hwirq
+ scu_ic
->irq_shift
);
106 unsigned int mask
= bit
|
107 (scu_ic
->irq_enable
<< ASPEED_SCU_IC_STATUS_SHIFT
);
110 * Status bits are cleared by writing 1. In order to prevent the unmask
111 * operation from clearing the status bits, they should be under the
112 * mask and written with 0.
114 regmap_update_bits(scu_ic
->scu
, scu_ic
->reg
, mask
, bit
);
117 static int aspeed_scu_ic_irq_set_affinity(struct irq_data
*data
,
118 const struct cpumask
*dest
,
124 static struct irq_chip aspeed_scu_ic_chip
= {
125 .name
= "aspeed-scu-ic",
126 .irq_mask
= aspeed_scu_ic_irq_mask
,
127 .irq_unmask
= aspeed_scu_ic_irq_unmask
,
128 .irq_set_affinity
= aspeed_scu_ic_irq_set_affinity
,
131 static int aspeed_scu_ic_map(struct irq_domain
*domain
, unsigned int irq
,
132 irq_hw_number_t hwirq
)
134 irq_set_chip_and_handler(irq
, &aspeed_scu_ic_chip
, handle_level_irq
);
135 irq_set_chip_data(irq
, domain
->host_data
);
140 static const struct irq_domain_ops aspeed_scu_ic_domain_ops
= {
141 .map
= aspeed_scu_ic_map
,
144 static int aspeed_scu_ic_of_init_common(struct aspeed_scu_ic
*scu_ic
,
145 struct device_node
*node
)
155 scu_ic
->scu
= syscon_node_to_regmap(node
->parent
);
156 if (IS_ERR(scu_ic
->scu
)) {
157 rc
= PTR_ERR(scu_ic
->scu
);
161 irq
= irq_of_parse_and_map(node
, 0);
167 scu_ic
->irq_domain
= irq_domain_add_linear(node
, scu_ic
->num_irqs
,
168 &aspeed_scu_ic_domain_ops
,
170 if (!scu_ic
->irq_domain
) {
175 irq_set_chained_handler_and_data(irq
, aspeed_scu_ic_irq_handler
,
186 static int __init
aspeed_scu_ic_of_init(struct device_node
*node
,
187 struct device_node
*parent
)
189 struct aspeed_scu_ic
*scu_ic
= kzalloc(sizeof(*scu_ic
), GFP_KERNEL
);
194 scu_ic
->irq_enable
= ASPEED_SCU_IC_ENABLE
;
195 scu_ic
->irq_shift
= ASPEED_SCU_IC_SHIFT
;
196 scu_ic
->num_irqs
= ASPEED_SCU_IC_NUM_IRQS
;
197 scu_ic
->reg
= ASPEED_SCU_IC_REG
;
199 return aspeed_scu_ic_of_init_common(scu_ic
, node
);
202 static int __init
aspeed_ast2600_scu_ic0_of_init(struct device_node
*node
,
203 struct device_node
*parent
)
205 struct aspeed_scu_ic
*scu_ic
= kzalloc(sizeof(*scu_ic
), GFP_KERNEL
);
210 scu_ic
->irq_enable
= ASPEED_AST2600_SCU_IC0_ENABLE
;
211 scu_ic
->irq_shift
= ASPEED_AST2600_SCU_IC0_SHIFT
;
212 scu_ic
->num_irqs
= ASPEED_AST2600_SCU_IC0_NUM_IRQS
;
213 scu_ic
->reg
= ASPEED_AST2600_SCU_IC0_REG
;
215 return aspeed_scu_ic_of_init_common(scu_ic
, node
);
218 static int __init
aspeed_ast2600_scu_ic1_of_init(struct device_node
*node
,
219 struct device_node
*parent
)
221 struct aspeed_scu_ic
*scu_ic
= kzalloc(sizeof(*scu_ic
), GFP_KERNEL
);
226 scu_ic
->irq_enable
= ASPEED_AST2600_SCU_IC1_ENABLE
;
227 scu_ic
->irq_shift
= ASPEED_AST2600_SCU_IC1_SHIFT
;
228 scu_ic
->num_irqs
= ASPEED_AST2600_SCU_IC1_NUM_IRQS
;
229 scu_ic
->reg
= ASPEED_AST2600_SCU_IC1_REG
;
231 return aspeed_scu_ic_of_init_common(scu_ic
, node
);
234 IRQCHIP_DECLARE(ast2400_scu_ic
, "aspeed,ast2400-scu-ic", aspeed_scu_ic_of_init
);
235 IRQCHIP_DECLARE(ast2500_scu_ic
, "aspeed,ast2500-scu-ic", aspeed_scu_ic_of_init
);
236 IRQCHIP_DECLARE(ast2600_scu_ic0
, "aspeed,ast2600-scu-ic0",
237 aspeed_ast2600_scu_ic0_of_init
);
238 IRQCHIP_DECLARE(ast2600_scu_ic1
, "aspeed,ast2600-scu-ic1",
239 aspeed_ast2600_scu_ic1_of_init
);