162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Varun Sethi <varun.sethi@freescale.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/irq.h> 962306a36Sopenharmony_ci#include <linux/smp.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/irqdomain.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/io.h> 1462306a36Sopenharmony_ci#include <asm/irq.h> 1562306a36Sopenharmony_ci#include <asm/mpic.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "mpic.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define MPIC_ERR_INT_BASE 0x3900 2062306a36Sopenharmony_ci#define MPIC_ERR_INT_EISR 0x0000 2162306a36Sopenharmony_ci#define MPIC_ERR_INT_EIMR 0x0010 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic inline u32 mpic_fsl_err_read(u32 __iomem *base, unsigned int err_reg) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci return in_be32(base + (err_reg >> 2)); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic inline void mpic_fsl_err_write(u32 __iomem *base, u32 value) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci out_be32(base + (MPIC_ERR_INT_EIMR >> 2), value); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void fsl_mpic_mask_err(struct irq_data *d) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci u32 eimr; 3662306a36Sopenharmony_ci struct mpic *mpic = irq_data_get_irq_chip_data(d); 3762306a36Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0]; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); 4062306a36Sopenharmony_ci eimr |= (1 << (31 - src)); 4162306a36Sopenharmony_ci mpic_fsl_err_write(mpic->err_regs, eimr); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void fsl_mpic_unmask_err(struct irq_data *d) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci u32 eimr; 4762306a36Sopenharmony_ci struct mpic *mpic = irq_data_get_irq_chip_data(d); 4862306a36Sopenharmony_ci unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0]; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); 5162306a36Sopenharmony_ci eimr &= ~(1 << (31 - src)); 5262306a36Sopenharmony_ci mpic_fsl_err_write(mpic->err_regs, eimr); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct irq_chip fsl_mpic_err_chip = { 5662306a36Sopenharmony_ci .irq_disable = fsl_mpic_mask_err, 5762306a36Sopenharmony_ci .irq_mask = fsl_mpic_mask_err, 5862306a36Sopenharmony_ci .irq_unmask = fsl_mpic_unmask_err, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciint __init mpic_setup_error_int(struct mpic *mpic, int intvec) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int i; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci mpic->err_regs = ioremap(mpic->paddr + MPIC_ERR_INT_BASE, 0x1000); 6662306a36Sopenharmony_ci if (!mpic->err_regs) { 6762306a36Sopenharmony_ci pr_err("could not map mpic error registers\n"); 6862306a36Sopenharmony_ci return -ENOMEM; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci mpic->hc_err = fsl_mpic_err_chip; 7162306a36Sopenharmony_ci mpic->hc_err.name = mpic->name; 7262306a36Sopenharmony_ci mpic->flags |= MPIC_FSL_HAS_EIMR; 7362306a36Sopenharmony_ci /* allocate interrupt vectors for error interrupts */ 7462306a36Sopenharmony_ci for (i = MPIC_MAX_ERR - 1; i >= 0; i--) 7562306a36Sopenharmony_ci mpic->err_int_vecs[i] = intvec--; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci if ((mpic->flags & MPIC_FSL_HAS_EIMR) && 8362306a36Sopenharmony_ci (hw >= mpic->err_int_vecs[0] && 8462306a36Sopenharmony_ci hw <= mpic->err_int_vecs[MPIC_MAX_ERR - 1])) { 8562306a36Sopenharmony_ci WARN_ON(mpic->flags & MPIC_SECONDARY); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci pr_debug("mpic: mapping as Error Interrupt\n"); 8862306a36Sopenharmony_ci irq_set_chip_data(virq, mpic); 8962306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &mpic->hc_err, 9062306a36Sopenharmony_ci handle_level_irq); 9162306a36Sopenharmony_ci return 1; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic irqreturn_t fsl_error_int_handler(int irq, void *data) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct mpic *mpic = (struct mpic *) data; 10062306a36Sopenharmony_ci u32 eisr, eimr; 10162306a36Sopenharmony_ci int errint; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci eisr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EISR); 10462306a36Sopenharmony_ci eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (!(eisr & ~eimr)) 10762306a36Sopenharmony_ci return IRQ_NONE; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci while (eisr) { 11062306a36Sopenharmony_ci int ret; 11162306a36Sopenharmony_ci errint = __builtin_clz(eisr); 11262306a36Sopenharmony_ci ret = generic_handle_domain_irq(mpic->irqhost, 11362306a36Sopenharmony_ci mpic->err_int_vecs[errint]); 11462306a36Sopenharmony_ci if (WARN_ON(ret)) { 11562306a36Sopenharmony_ci eimr |= 1 << (31 - errint); 11662306a36Sopenharmony_ci mpic_fsl_err_write(mpic->err_regs, eimr); 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci eisr &= ~(1 << (31 - errint)); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return IRQ_HANDLED; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civoid __init mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci unsigned int virq; 12762306a36Sopenharmony_ci int ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci virq = irq_create_mapping(mpic->irqhost, irqnum); 13062306a36Sopenharmony_ci if (!virq) { 13162306a36Sopenharmony_ci pr_err("Error interrupt setup failed\n"); 13262306a36Sopenharmony_ci return; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Mask all error interrupts */ 13662306a36Sopenharmony_ci mpic_fsl_err_write(mpic->err_regs, ~0); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci ret = request_irq(virq, fsl_error_int_handler, IRQF_NO_THREAD, 13962306a36Sopenharmony_ci "mpic-error-int", mpic); 14062306a36Sopenharmony_ci if (ret) 14162306a36Sopenharmony_ci pr_err("Failed to register error interrupt handler\n"); 14262306a36Sopenharmony_ci} 143