1/* 2 * Copyright (c) 2016, Mellanox Technologies. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 */ 32 33#include <linux/interrupt.h> 34#include <linux/module.h> 35#include <linux/of.h> 36#include <linux/irq.h> 37#include <linux/irqdomain.h> 38#include <linux/irqchip.h> 39#include <soc/nps/common.h> 40 41#define NPS_NR_CPU_IRQS 8 /* number of interrupt lines of NPS400 CPU */ 42#define NPS_TIMER0_IRQ 3 43 44/* 45 * NPS400 core includes an Interrupt Controller (IC) support. 46 * All cores can deactivate level irqs at first level control 47 * at cores mesh layer called MTM. 48 * For devices out side chip e.g. uart, network there is another 49 * level called Global Interrupt Manager (GIM). 50 * This second level can control level and edge interrupt. 51 * 52 * NOTE: AUX_IENABLE and CTOP_AUX_IACK are auxiliary registers 53 * with private HW copy per CPU. 54 */ 55 56static void nps400_irq_mask(struct irq_data *irqd) 57{ 58 unsigned int ienb; 59 unsigned int irq = irqd_to_hwirq(irqd); 60 61 ienb = read_aux_reg(AUX_IENABLE); 62 ienb &= ~(1 << irq); 63 write_aux_reg(AUX_IENABLE, ienb); 64} 65 66static void nps400_irq_unmask(struct irq_data *irqd) 67{ 68 unsigned int ienb; 69 unsigned int irq = irqd_to_hwirq(irqd); 70 71 ienb = read_aux_reg(AUX_IENABLE); 72 ienb |= (1 << irq); 73 write_aux_reg(AUX_IENABLE, ienb); 74} 75 76static void nps400_irq_eoi_global(struct irq_data *irqd) 77{ 78 unsigned int __maybe_unused irq = irqd_to_hwirq(irqd); 79 80 write_aux_reg(CTOP_AUX_IACK, 1 << irq); 81 82 /* Don't ack GIC before all device access attempts are done */ 83 mb(); 84 85 nps_ack_gic(); 86} 87 88static void nps400_irq_ack(struct irq_data *irqd) 89{ 90 unsigned int __maybe_unused irq = irqd_to_hwirq(irqd); 91 92 write_aux_reg(CTOP_AUX_IACK, 1 << irq); 93} 94 95static struct irq_chip nps400_irq_chip_fasteoi = { 96 .name = "NPS400 IC Global", 97 .irq_mask = nps400_irq_mask, 98 .irq_unmask = nps400_irq_unmask, 99 .irq_eoi = nps400_irq_eoi_global, 100}; 101 102static struct irq_chip nps400_irq_chip_percpu = { 103 .name = "NPS400 IC", 104 .irq_mask = nps400_irq_mask, 105 .irq_unmask = nps400_irq_unmask, 106 .irq_ack = nps400_irq_ack, 107}; 108 109static int nps400_irq_map(struct irq_domain *d, unsigned int virq, 110 irq_hw_number_t hw) 111{ 112 switch (hw) { 113 case NPS_TIMER0_IRQ: 114#ifdef CONFIG_SMP 115 case NPS_IPI_IRQ: 116#endif 117 irq_set_percpu_devid(virq); 118 irq_set_chip_and_handler(virq, &nps400_irq_chip_percpu, 119 handle_percpu_devid_irq); 120 break; 121 default: 122 irq_set_chip_and_handler(virq, &nps400_irq_chip_fasteoi, 123 handle_fasteoi_irq); 124 break; 125 } 126 127 return 0; 128} 129 130static const struct irq_domain_ops nps400_irq_ops = { 131 .xlate = irq_domain_xlate_onecell, 132 .map = nps400_irq_map, 133}; 134 135static int __init nps400_of_init(struct device_node *node, 136 struct device_node *parent) 137{ 138 struct irq_domain *nps400_root_domain; 139 140 if (parent) { 141 pr_err("DeviceTree incore ic not a root irq controller\n"); 142 return -EINVAL; 143 } 144 145 nps400_root_domain = irq_domain_add_linear(node, NPS_NR_CPU_IRQS, 146 &nps400_irq_ops, NULL); 147 148 if (!nps400_root_domain) { 149 pr_err("nps400 root irq domain not avail\n"); 150 return -ENOMEM; 151 } 152 153 /* 154 * Needed for primary domain lookup to succeed 155 * This is a primary irqchip, and can never have a parent 156 */ 157 irq_set_default_host(nps400_root_domain); 158 159#ifdef CONFIG_SMP 160 irq_create_mapping(nps400_root_domain, NPS_IPI_IRQ); 161#endif 162 163 return 0; 164} 165IRQCHIP_DECLARE(ezchip_nps400_ic, "ezchip,nps400-ic", nps400_of_init); 166