162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
462306a36Sopenharmony_ci * Copyright (C) 2014 Stefan Kristansson <stefan.kristiansson@saunalahti.fi>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/irq.h>
862306a36Sopenharmony_ci#include <linux/irqchip.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/of_irq.h>
1162306a36Sopenharmony_ci#include <linux/of_address.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* OR1K PIC implementation */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct or1k_pic_dev {
1662306a36Sopenharmony_ci	struct irq_chip chip;
1762306a36Sopenharmony_ci	irq_flow_handler_t handle;
1862306a36Sopenharmony_ci	unsigned long flags;
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * We're a couple of cycles faster than the generic implementations with
2362306a36Sopenharmony_ci * these 'fast' versions.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic void or1k_pic_mask(struct irq_data *data)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void or1k_pic_unmask(struct irq_data *data)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq));
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void or1k_pic_ack(struct irq_data *data)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	mtspr(SPR_PICSR, (1UL << data->hwirq));
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void or1k_pic_mask_ack(struct irq_data *data)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
4462306a36Sopenharmony_ci	mtspr(SPR_PICSR, (1UL << data->hwirq));
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * There are two oddities with the OR1200 PIC implementation:
4962306a36Sopenharmony_ci * i)  LEVEL-triggered interrupts are latched and need to be cleared
5062306a36Sopenharmony_ci * ii) the interrupt latch is cleared by writing a 0 to the bit,
5162306a36Sopenharmony_ci *     as opposed to a 1 as mandated by the spec
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_cistatic void or1k_pic_or1200_ack(struct irq_data *data)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void or1k_pic_or1200_mask_ack(struct irq_data *data)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq));
6162306a36Sopenharmony_ci	mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq));
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct or1k_pic_dev or1k_pic_level = {
6562306a36Sopenharmony_ci	.chip = {
6662306a36Sopenharmony_ci		.name = "or1k-PIC-level",
6762306a36Sopenharmony_ci		.irq_unmask = or1k_pic_unmask,
6862306a36Sopenharmony_ci		.irq_mask = or1k_pic_mask,
6962306a36Sopenharmony_ci	},
7062306a36Sopenharmony_ci	.handle = handle_level_irq,
7162306a36Sopenharmony_ci	.flags = IRQ_LEVEL | IRQ_NOPROBE,
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic struct or1k_pic_dev or1k_pic_edge = {
7562306a36Sopenharmony_ci	.chip = {
7662306a36Sopenharmony_ci		.name = "or1k-PIC-edge",
7762306a36Sopenharmony_ci		.irq_unmask = or1k_pic_unmask,
7862306a36Sopenharmony_ci		.irq_mask = or1k_pic_mask,
7962306a36Sopenharmony_ci		.irq_ack = or1k_pic_ack,
8062306a36Sopenharmony_ci		.irq_mask_ack = or1k_pic_mask_ack,
8162306a36Sopenharmony_ci	},
8262306a36Sopenharmony_ci	.handle = handle_edge_irq,
8362306a36Sopenharmony_ci	.flags = IRQ_LEVEL | IRQ_NOPROBE,
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic struct or1k_pic_dev or1k_pic_or1200 = {
8762306a36Sopenharmony_ci	.chip = {
8862306a36Sopenharmony_ci		.name = "or1200-PIC",
8962306a36Sopenharmony_ci		.irq_unmask = or1k_pic_unmask,
9062306a36Sopenharmony_ci		.irq_mask = or1k_pic_mask,
9162306a36Sopenharmony_ci		.irq_ack = or1k_pic_or1200_ack,
9262306a36Sopenharmony_ci		.irq_mask_ack = or1k_pic_or1200_mask_ack,
9362306a36Sopenharmony_ci	},
9462306a36Sopenharmony_ci	.handle = handle_level_irq,
9562306a36Sopenharmony_ci	.flags = IRQ_LEVEL | IRQ_NOPROBE,
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic struct irq_domain *root_domain;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic inline int pic_get_irq(int first)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int hwirq;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	hwirq = ffs(mfspr(SPR_PICSR) >> first);
10562306a36Sopenharmony_ci	if (!hwirq)
10662306a36Sopenharmony_ci		return NO_IRQ;
10762306a36Sopenharmony_ci	else
10862306a36Sopenharmony_ci		hwirq = hwirq + first - 1;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return hwirq;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic void or1k_pic_handle_irq(struct pt_regs *regs)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int irq = -1;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	while ((irq = pic_get_irq(irq + 1)) != NO_IRQ)
11862306a36Sopenharmony_ci		generic_handle_domain_irq(root_domain, irq);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct or1k_pic_dev *pic = d->host_data;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	irq_set_chip_and_handler(irq, &pic->chip, pic->handle);
12662306a36Sopenharmony_ci	irq_set_status_flags(irq, pic->flags);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return 0;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic const struct irq_domain_ops or1k_irq_domain_ops = {
13262306a36Sopenharmony_ci	.xlate = irq_domain_xlate_onecell,
13362306a36Sopenharmony_ci	.map = or1k_map,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/*
13762306a36Sopenharmony_ci * This sets up the IRQ domain for the PIC built in to the OpenRISC
13862306a36Sopenharmony_ci * 1000 CPU.  This is the "root" domain as these are the interrupts
13962306a36Sopenharmony_ci * that directly trigger an exception in the CPU.
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_cistatic int __init or1k_pic_init(struct device_node *node,
14262306a36Sopenharmony_ci				 struct or1k_pic_dev *pic)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	/* Disable all interrupts until explicitly requested */
14562306a36Sopenharmony_ci	mtspr(SPR_PICMR, (0UL));
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops,
14862306a36Sopenharmony_ci					    pic);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	set_handle_irq(or1k_pic_handle_irq);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int __init or1k_pic_or1200_init(struct device_node *node,
15662306a36Sopenharmony_ci				       struct device_node *parent)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return or1k_pic_init(node, &or1k_pic_or1200);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ciIRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init);
16162306a36Sopenharmony_ciIRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int __init or1k_pic_level_init(struct device_node *node,
16462306a36Sopenharmony_ci				      struct device_node *parent)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	return or1k_pic_init(node, &or1k_pic_level);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ciIRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level",
16962306a36Sopenharmony_ci		or1k_pic_level_init);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int __init or1k_pic_edge_init(struct device_node *node,
17262306a36Sopenharmony_ci				     struct device_node *parent)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	return or1k_pic_init(node, &or1k_pic_edge);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ciIRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init);
177