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