irq-vt8500.c (5967B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * arch/arm/mach-vt8500/irq.c 4 * 5 * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> 6 * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> 7 */ 8 9/* 10 * This file is copied and modified from the original irq.c provided by 11 * Alexey Charkov. Minor changes have been made for Device Tree Support. 12 */ 13 14#include <linux/slab.h> 15#include <linux/io.h> 16#include <linux/irq.h> 17#include <linux/irqchip.h> 18#include <linux/irqdomain.h> 19#include <linux/interrupt.h> 20#include <linux/bitops.h> 21 22#include <linux/of.h> 23#include <linux/of_irq.h> 24#include <linux/of_address.h> 25 26#include <asm/irq.h> 27#include <asm/exception.h> 28#include <asm/mach/irq.h> 29 30#define VT8500_ICPC_IRQ 0x20 31#define VT8500_ICPC_FIQ 0x24 32#define VT8500_ICDC 0x40 /* Destination Control 64*u32 */ 33#define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */ 34 35/* ICPC */ 36#define ICPC_MASK 0x3F 37#define ICPC_ROTATE BIT(6) 38 39/* IC_DCTR */ 40#define ICDC_IRQ 0x00 41#define ICDC_FIQ 0x01 42#define ICDC_DSS0 0x02 43#define ICDC_DSS1 0x03 44#define ICDC_DSS2 0x04 45#define ICDC_DSS3 0x05 46#define ICDC_DSS4 0x06 47#define ICDC_DSS5 0x07 48 49#define VT8500_INT_DISABLE 0 50#define VT8500_INT_ENABLE BIT(3) 51 52#define VT8500_TRIGGER_HIGH 0 53#define VT8500_TRIGGER_RISING BIT(5) 54#define VT8500_TRIGGER_FALLING BIT(6) 55#define VT8500_EDGE ( VT8500_TRIGGER_RISING \ 56 | VT8500_TRIGGER_FALLING) 57 58/* vt8500 has 1 intc, wm8505 and wm8650 have 2 */ 59#define VT8500_INTC_MAX 2 60 61struct vt8500_irq_data { 62 void __iomem *base; /* IO Memory base address */ 63 struct irq_domain *domain; /* Domain for this controller */ 64}; 65 66/* Global variable for accessing io-mem addresses */ 67static struct vt8500_irq_data intc[VT8500_INTC_MAX]; 68static u32 active_cnt = 0; 69 70static void vt8500_irq_mask(struct irq_data *d) 71{ 72 struct vt8500_irq_data *priv = d->domain->host_data; 73 void __iomem *base = priv->base; 74 void __iomem *stat_reg = base + VT8500_ICIS + (d->hwirq < 32 ? 0 : 4); 75 u8 edge, dctr; 76 u32 status; 77 78 edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE; 79 if (edge) { 80 status = readl(stat_reg); 81 82 status |= (1 << (d->hwirq & 0x1f)); 83 writel(status, stat_reg); 84 } else { 85 dctr = readb(base + VT8500_ICDC + d->hwirq); 86 dctr &= ~VT8500_INT_ENABLE; 87 writeb(dctr, base + VT8500_ICDC + d->hwirq); 88 } 89} 90 91static void vt8500_irq_unmask(struct irq_data *d) 92{ 93 struct vt8500_irq_data *priv = d->domain->host_data; 94 void __iomem *base = priv->base; 95 u8 dctr; 96 97 dctr = readb(base + VT8500_ICDC + d->hwirq); 98 dctr |= VT8500_INT_ENABLE; 99 writeb(dctr, base + VT8500_ICDC + d->hwirq); 100} 101 102static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type) 103{ 104 struct vt8500_irq_data *priv = d->domain->host_data; 105 void __iomem *base = priv->base; 106 u8 dctr; 107 108 dctr = readb(base + VT8500_ICDC + d->hwirq); 109 dctr &= ~VT8500_EDGE; 110 111 switch (flow_type) { 112 case IRQF_TRIGGER_LOW: 113 return -EINVAL; 114 case IRQF_TRIGGER_HIGH: 115 dctr |= VT8500_TRIGGER_HIGH; 116 irq_set_handler_locked(d, handle_level_irq); 117 break; 118 case IRQF_TRIGGER_FALLING: 119 dctr |= VT8500_TRIGGER_FALLING; 120 irq_set_handler_locked(d, handle_edge_irq); 121 break; 122 case IRQF_TRIGGER_RISING: 123 dctr |= VT8500_TRIGGER_RISING; 124 irq_set_handler_locked(d, handle_edge_irq); 125 break; 126 } 127 writeb(dctr, base + VT8500_ICDC + d->hwirq); 128 129 return 0; 130} 131 132static struct irq_chip vt8500_irq_chip = { 133 .name = "vt8500", 134 .irq_ack = vt8500_irq_mask, 135 .irq_mask = vt8500_irq_mask, 136 .irq_unmask = vt8500_irq_unmask, 137 .irq_set_type = vt8500_irq_set_type, 138}; 139 140static void __init vt8500_init_irq_hw(void __iomem *base) 141{ 142 u32 i; 143 144 /* Enable rotating priority for IRQ */ 145 writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ); 146 writel(0x00, base + VT8500_ICPC_FIQ); 147 148 /* Disable all interrupts and route them to IRQ */ 149 for (i = 0; i < 64; i++) 150 writeb(VT8500_INT_DISABLE | ICDC_IRQ, base + VT8500_ICDC + i); 151} 152 153static int vt8500_irq_map(struct irq_domain *h, unsigned int virq, 154 irq_hw_number_t hw) 155{ 156 irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq); 157 158 return 0; 159} 160 161static const struct irq_domain_ops vt8500_irq_domain_ops = { 162 .map = vt8500_irq_map, 163 .xlate = irq_domain_xlate_onecell, 164}; 165 166static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) 167{ 168 u32 stat, i; 169 int irqnr; 170 void __iomem *base; 171 172 /* Loop through each active controller */ 173 for (i=0; i<active_cnt; i++) { 174 base = intc[i].base; 175 irqnr = readl_relaxed(base) & 0x3F; 176 /* 177 Highest Priority register default = 63, so check that this 178 is a real interrupt by checking the status register 179 */ 180 if (irqnr == 63) { 181 stat = readl_relaxed(base + VT8500_ICIS + 4); 182 if (!(stat & BIT(31))) 183 continue; 184 } 185 186 generic_handle_domain_irq(intc[i].domain, irqnr); 187 } 188} 189 190static int __init vt8500_irq_init(struct device_node *node, 191 struct device_node *parent) 192{ 193 int irq, i; 194 struct device_node *np = node; 195 196 if (active_cnt == VT8500_INTC_MAX) { 197 pr_err("%s: Interrupt controllers > VT8500_INTC_MAX\n", 198 __func__); 199 goto out; 200 } 201 202 intc[active_cnt].base = of_iomap(np, 0); 203 intc[active_cnt].domain = irq_domain_add_linear(node, 64, 204 &vt8500_irq_domain_ops, &intc[active_cnt]); 205 206 if (!intc[active_cnt].base) { 207 pr_err("%s: Unable to map IO memory\n", __func__); 208 goto out; 209 } 210 211 if (!intc[active_cnt].domain) { 212 pr_err("%s: Unable to add irq domain!\n", __func__); 213 goto out; 214 } 215 216 set_handle_irq(vt8500_handle_irq); 217 218 vt8500_init_irq_hw(intc[active_cnt].base); 219 220 pr_info("vt8500-irq: Added interrupt controller\n"); 221 222 active_cnt++; 223 224 /* check if this is a slaved controller */ 225 if (of_irq_count(np) != 0) { 226 /* check that we have the correct number of interrupts */ 227 if (of_irq_count(np) != 8) { 228 pr_err("%s: Incorrect IRQ map for slaved controller\n", 229 __func__); 230 return -EINVAL; 231 } 232 233 for (i = 0; i < 8; i++) { 234 irq = irq_of_parse_and_map(np, i); 235 enable_irq(irq); 236 } 237 238 pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); 239 } 240out: 241 return 0; 242} 243 244IRQCHIP_DECLARE(vt8500_irq, "via,vt8500-intc", vt8500_irq_init);