162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * RDA8810PL SoC irqchip driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright RDA Microelectronics Company Limited 662306a36Sopenharmony_ci * Copyright (c) 2017 Andreas Färber 762306a36Sopenharmony_ci * Copyright (c) 2018 Manivannan Sadhasivam 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/irqchip.h> 1462306a36Sopenharmony_ci#include <linux/irqdomain.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/exception.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define RDA_INTC_FINALSTATUS 0x00 2062306a36Sopenharmony_ci#define RDA_INTC_MASK_SET 0x08 2162306a36Sopenharmony_ci#define RDA_INTC_MASK_CLR 0x0c 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define RDA_IRQ_MASK_ALL 0xFFFFFFFF 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define RDA_NR_IRQS 32 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void __iomem *rda_intc_base; 2862306a36Sopenharmony_cistatic struct irq_domain *rda_irq_domain; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void rda_intc_mask_irq(struct irq_data *d) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_CLR); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void rda_intc_unmask_irq(struct irq_data *d) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci writel_relaxed(BIT(d->hwirq), rda_intc_base + RDA_INTC_MASK_SET); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int rda_intc_set_type(struct irq_data *data, unsigned int flow_type) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci /* Hardware supports only level triggered interrupts */ 4362306a36Sopenharmony_ci if ((flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) == flow_type) 4462306a36Sopenharmony_ci return 0; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return -EINVAL; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void __exception_irq_entry rda_handle_irq(struct pt_regs *regs) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci u32 stat = readl_relaxed(rda_intc_base + RDA_INTC_FINALSTATUS); 5262306a36Sopenharmony_ci u32 hwirq; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci while (stat) { 5562306a36Sopenharmony_ci hwirq = __fls(stat); 5662306a36Sopenharmony_ci generic_handle_domain_irq(rda_irq_domain, hwirq); 5762306a36Sopenharmony_ci stat &= ~BIT(hwirq); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct irq_chip rda_irq_chip = { 6262306a36Sopenharmony_ci .name = "rda-intc", 6362306a36Sopenharmony_ci .irq_mask = rda_intc_mask_irq, 6462306a36Sopenharmony_ci .irq_unmask = rda_intc_unmask_irq, 6562306a36Sopenharmony_ci .irq_set_type = rda_intc_set_type, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int rda_irq_map(struct irq_domain *d, 6962306a36Sopenharmony_ci unsigned int virq, irq_hw_number_t hw) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci irq_set_status_flags(virq, IRQ_LEVEL); 7262306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &rda_irq_chip, handle_level_irq); 7362306a36Sopenharmony_ci irq_set_chip_data(virq, d->host_data); 7462306a36Sopenharmony_ci irq_set_probe(virq); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const struct irq_domain_ops rda_irq_domain_ops = { 8062306a36Sopenharmony_ci .map = rda_irq_map, 8162306a36Sopenharmony_ci .xlate = irq_domain_xlate_onecell, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int __init rda8810_intc_init(struct device_node *node, 8562306a36Sopenharmony_ci struct device_node *parent) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci rda_intc_base = of_io_request_and_map(node, 0, "rda-intc"); 8862306a36Sopenharmony_ci if (IS_ERR(rda_intc_base)) 8962306a36Sopenharmony_ci return PTR_ERR(rda_intc_base); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Mask all interrupt sources */ 9262306a36Sopenharmony_ci writel_relaxed(RDA_IRQ_MASK_ALL, rda_intc_base + RDA_INTC_MASK_CLR); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci rda_irq_domain = irq_domain_create_linear(&node->fwnode, RDA_NR_IRQS, 9562306a36Sopenharmony_ci &rda_irq_domain_ops, 9662306a36Sopenharmony_ci rda_intc_base); 9762306a36Sopenharmony_ci if (!rda_irq_domain) { 9862306a36Sopenharmony_ci iounmap(rda_intc_base); 9962306a36Sopenharmony_ci return -ENOMEM; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci set_handle_irq(rda_handle_irq); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ciIRQCHIP_DECLARE(rda_intc, "rda,8810pl-intc", rda8810_intc_init); 108