1/* 2 * Copyright (C) 2012 Freescale Semiconductor, Inc. 3 * 4 * Author: Varun Sethi <varun.sethi@freescale.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; version 2 of the 9 * License. 10 * 11 */ 12 13#include <linux/irq.h> 14#include <linux/smp.h> 15#include <linux/interrupt.h> 16 17#include <asm/io.h> 18#include <asm/irq.h> 19#include <asm/mpic.h> 20 21#include "mpic.h" 22 23#define MPIC_ERR_INT_BASE 0x3900 24#define MPIC_ERR_INT_EISR 0x0000 25#define MPIC_ERR_INT_EIMR 0x0010 26 27static inline u32 mpic_fsl_err_read(u32 __iomem *base, unsigned int err_reg) 28{ 29 return in_be32(base + (err_reg >> 2)); 30} 31 32static inline void mpic_fsl_err_write(u32 __iomem *base, u32 value) 33{ 34 out_be32(base + (MPIC_ERR_INT_EIMR >> 2), value); 35} 36 37static void fsl_mpic_mask_err(struct irq_data *d) 38{ 39 u32 eimr; 40 struct mpic *mpic = irq_data_get_irq_chip_data(d); 41 unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0]; 42 43 eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); 44 eimr |= (1 << (31 - src)); 45 mpic_fsl_err_write(mpic->err_regs, eimr); 46} 47 48static void fsl_mpic_unmask_err(struct irq_data *d) 49{ 50 u32 eimr; 51 struct mpic *mpic = irq_data_get_irq_chip_data(d); 52 unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0]; 53 54 eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); 55 eimr &= ~(1 << (31 - src)); 56 mpic_fsl_err_write(mpic->err_regs, eimr); 57} 58 59static struct irq_chip fsl_mpic_err_chip = { 60 .irq_disable = fsl_mpic_mask_err, 61 .irq_mask = fsl_mpic_mask_err, 62 .irq_unmask = fsl_mpic_unmask_err, 63}; 64 65int mpic_setup_error_int(struct mpic *mpic, int intvec) 66{ 67 int i; 68 69 mpic->err_regs = ioremap(mpic->paddr + MPIC_ERR_INT_BASE, 0x1000); 70 if (!mpic->err_regs) { 71 pr_err("could not map mpic error registers\n"); 72 return -ENOMEM; 73 } 74 mpic->hc_err = fsl_mpic_err_chip; 75 mpic->hc_err.name = mpic->name; 76 mpic->flags |= MPIC_FSL_HAS_EIMR; 77 /* allocate interrupt vectors for error interrupts */ 78 for (i = MPIC_MAX_ERR - 1; i >= 0; i--) 79 mpic->err_int_vecs[i] = --intvec; 80 81 return 0; 82} 83 84int mpic_map_error_int(struct mpic *mpic, unsigned int virq, irq_hw_number_t hw) 85{ 86 if ((mpic->flags & MPIC_FSL_HAS_EIMR) && 87 (hw >= mpic->err_int_vecs[0] && 88 hw <= mpic->err_int_vecs[MPIC_MAX_ERR - 1])) { 89 WARN_ON(mpic->flags & MPIC_SECONDARY); 90 91 pr_debug("mpic: mapping as Error Interrupt\n"); 92 irq_set_chip_data(virq, mpic); 93 irq_set_chip_and_handler(virq, &mpic->hc_err, 94 handle_level_irq); 95 return 1; 96 } 97 98 return 0; 99} 100 101static irqreturn_t fsl_error_int_handler(int irq, void *data) 102{ 103 struct mpic *mpic = (struct mpic *) data; 104 u32 eisr, eimr; 105 int errint; 106 unsigned int cascade_irq; 107 108 eisr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EISR); 109 eimr = mpic_fsl_err_read(mpic->err_regs, MPIC_ERR_INT_EIMR); 110 111 if (!(eisr & ~eimr)) 112 return IRQ_NONE; 113 114 while (eisr) { 115 errint = __builtin_clz(eisr); 116 cascade_irq = irq_linear_revmap(mpic->irqhost, 117 mpic->err_int_vecs[errint]); 118 WARN_ON(cascade_irq == NO_IRQ); 119 if (cascade_irq != NO_IRQ) { 120 generic_handle_irq(cascade_irq); 121 } else { 122 eimr |= 1 << (31 - errint); 123 mpic_fsl_err_write(mpic->err_regs, eimr); 124 } 125 eisr &= ~(1 << (31 - errint)); 126 } 127 128 return IRQ_HANDLED; 129} 130 131void mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum) 132{ 133 unsigned int virq; 134 int ret; 135 136 virq = irq_create_mapping(mpic->irqhost, irqnum); 137 if (virq == NO_IRQ) { 138 pr_err("Error interrupt setup failed\n"); 139 return; 140 } 141 142 /* Mask all error interrupts */ 143 mpic_fsl_err_write(mpic->err_regs, ~0); 144 145 ret = request_irq(virq, fsl_error_int_handler, IRQF_NO_THREAD, 146 "mpic-error-int", mpic); 147 if (ret) 148 pr_err("Failed to register error interrupt handler\n"); 149} 150