104e2d1e0SGrzegorz Jaszczyk // SPDX-License-Identifier: GPL-2.0-only 204e2d1e0SGrzegorz Jaszczyk /* 304e2d1e0SGrzegorz Jaszczyk * PRU-ICSS INTC IRQChip driver for various TI SoCs 404e2d1e0SGrzegorz Jaszczyk * 504e2d1e0SGrzegorz Jaszczyk * Copyright (C) 2016-2020 Texas Instruments Incorporated - http://www.ti.com/ 604e2d1e0SGrzegorz Jaszczyk * 704e2d1e0SGrzegorz Jaszczyk * Author(s): 804e2d1e0SGrzegorz Jaszczyk * Andrew F. Davis <afd@ti.com> 904e2d1e0SGrzegorz Jaszczyk * Suman Anna <s-anna@ti.com> 1004e2d1e0SGrzegorz Jaszczyk * Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org> for Texas Instruments 1104e2d1e0SGrzegorz Jaszczyk * 1204e2d1e0SGrzegorz Jaszczyk * Copyright (C) 2019 David Lechner <david@lechnology.com> 1304e2d1e0SGrzegorz Jaszczyk */ 1404e2d1e0SGrzegorz Jaszczyk 15b1026e8aSDavid Lechner #include <linux/interrupt.h> 1604e2d1e0SGrzegorz Jaszczyk #include <linux/irq.h> 1704e2d1e0SGrzegorz Jaszczyk #include <linux/irqchip/chained_irq.h> 1804e2d1e0SGrzegorz Jaszczyk #include <linux/irqdomain.h> 1904e2d1e0SGrzegorz Jaszczyk #include <linux/module.h> 2004e2d1e0SGrzegorz Jaszczyk #include <linux/of_device.h> 2104e2d1e0SGrzegorz Jaszczyk #include <linux/platform_device.h> 2204e2d1e0SGrzegorz Jaszczyk 2304e2d1e0SGrzegorz Jaszczyk /* 2404e2d1e0SGrzegorz Jaszczyk * Number of host interrupts reaching the main MPU sub-system. Note that this 2504e2d1e0SGrzegorz Jaszczyk * is not the same as the total number of host interrupts supported by the PRUSS 2604e2d1e0SGrzegorz Jaszczyk * INTC instance 2704e2d1e0SGrzegorz Jaszczyk */ 2804e2d1e0SGrzegorz Jaszczyk #define MAX_NUM_HOST_IRQS 8 2904e2d1e0SGrzegorz Jaszczyk 3004e2d1e0SGrzegorz Jaszczyk /* minimum starting host interrupt number for MPU */ 3104e2d1e0SGrzegorz Jaszczyk #define FIRST_PRU_HOST_INT 2 3204e2d1e0SGrzegorz Jaszczyk 3304e2d1e0SGrzegorz Jaszczyk /* PRU_ICSS_INTC registers */ 3404e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_REVID 0x0000 3504e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_CR 0x0004 3604e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_GER 0x0010 3704e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_GNLR 0x001c 3804e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_SISR 0x0020 3904e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_SICR 0x0024 4004e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_EISR 0x0028 4104e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_EICR 0x002c 4204e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_HIEISR 0x0034 4304e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_HIDISR 0x0038 4404e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_GPIR 0x0080 4504e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_SRSR(x) (0x0200 + (x) * 4) 4604e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_SECR(x) (0x0280 + (x) * 4) 4704e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_ESR(x) (0x0300 + (x) * 4) 4804e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_ECR(x) (0x0380 + (x) * 4) 4904e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_CMR(x) (0x0400 + (x) * 4) 5004e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_HMR(x) (0x0800 + (x) * 4) 5104e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_HIPIR(x) (0x0900 + (x) * 4) 5204e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_SIPR(x) (0x0d00 + (x) * 4) 5304e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_SITR(x) (0x0d80 + (x) * 4) 5404e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_HINLR(x) (0x1100 + (x) * 4) 5504e2d1e0SGrzegorz Jaszczyk #define PRU_INTC_HIER 0x1500 5604e2d1e0SGrzegorz Jaszczyk 5704e2d1e0SGrzegorz Jaszczyk /* CMR register bit-field macros */ 5804e2d1e0SGrzegorz Jaszczyk #define CMR_EVT_MAP_MASK 0xf 5904e2d1e0SGrzegorz Jaszczyk #define CMR_EVT_MAP_BITS 8 6004e2d1e0SGrzegorz Jaszczyk #define CMR_EVT_PER_REG 4 6104e2d1e0SGrzegorz Jaszczyk 6204e2d1e0SGrzegorz Jaszczyk /* HMR register bit-field macros */ 6304e2d1e0SGrzegorz Jaszczyk #define HMR_CH_MAP_MASK 0xf 6404e2d1e0SGrzegorz Jaszczyk #define HMR_CH_MAP_BITS 8 6504e2d1e0SGrzegorz Jaszczyk #define HMR_CH_PER_REG 4 6604e2d1e0SGrzegorz Jaszczyk 6704e2d1e0SGrzegorz Jaszczyk /* HIPIR register bit-fields */ 6804e2d1e0SGrzegorz Jaszczyk #define INTC_HIPIR_NONE_HINT 0x80000000 6904e2d1e0SGrzegorz Jaszczyk 7004e2d1e0SGrzegorz Jaszczyk #define MAX_PRU_SYS_EVENTS 160 7104e2d1e0SGrzegorz Jaszczyk #define MAX_PRU_CHANNELS 20 7204e2d1e0SGrzegorz Jaszczyk 7304e2d1e0SGrzegorz Jaszczyk /** 7404e2d1e0SGrzegorz Jaszczyk * struct pruss_intc_map_record - keeps track of actual mapping state 7504e2d1e0SGrzegorz Jaszczyk * @value: The currently mapped value (channel or host) 7604e2d1e0SGrzegorz Jaszczyk * @ref_count: Keeps track of number of current users of this resource 7704e2d1e0SGrzegorz Jaszczyk */ 7804e2d1e0SGrzegorz Jaszczyk struct pruss_intc_map_record { 7904e2d1e0SGrzegorz Jaszczyk u8 value; 8004e2d1e0SGrzegorz Jaszczyk u8 ref_count; 8104e2d1e0SGrzegorz Jaszczyk }; 8204e2d1e0SGrzegorz Jaszczyk 8304e2d1e0SGrzegorz Jaszczyk /** 8404e2d1e0SGrzegorz Jaszczyk * struct pruss_intc_match_data - match data to handle SoC variations 8504e2d1e0SGrzegorz Jaszczyk * @num_system_events: number of input system events handled by the PRUSS INTC 8604e2d1e0SGrzegorz Jaszczyk * @num_host_events: number of host events (which is equal to number of 8704e2d1e0SGrzegorz Jaszczyk * channels) supported by the PRUSS INTC 8804e2d1e0SGrzegorz Jaszczyk */ 8904e2d1e0SGrzegorz Jaszczyk struct pruss_intc_match_data { 9004e2d1e0SGrzegorz Jaszczyk u8 num_system_events; 9104e2d1e0SGrzegorz Jaszczyk u8 num_host_events; 9204e2d1e0SGrzegorz Jaszczyk }; 9304e2d1e0SGrzegorz Jaszczyk 9404e2d1e0SGrzegorz Jaszczyk /** 9504e2d1e0SGrzegorz Jaszczyk * struct pruss_intc - PRUSS interrupt controller structure 9604e2d1e0SGrzegorz Jaszczyk * @event_channel: current state of system event to channel mappings 9704e2d1e0SGrzegorz Jaszczyk * @channel_host: current state of channel to host mappings 9804e2d1e0SGrzegorz Jaszczyk * @irqs: kernel irq numbers corresponding to PRUSS host interrupts 9904e2d1e0SGrzegorz Jaszczyk * @base: base virtual address of INTC register space 10004e2d1e0SGrzegorz Jaszczyk * @domain: irq domain for this interrupt controller 10104e2d1e0SGrzegorz Jaszczyk * @soc_config: cached PRUSS INTC IP configuration data 10204e2d1e0SGrzegorz Jaszczyk * @dev: PRUSS INTC device pointer 10304e2d1e0SGrzegorz Jaszczyk * @lock: mutex to serialize interrupts mapping 10404e2d1e0SGrzegorz Jaszczyk */ 10504e2d1e0SGrzegorz Jaszczyk struct pruss_intc { 10604e2d1e0SGrzegorz Jaszczyk struct pruss_intc_map_record event_channel[MAX_PRU_SYS_EVENTS]; 10704e2d1e0SGrzegorz Jaszczyk struct pruss_intc_map_record channel_host[MAX_PRU_CHANNELS]; 10804e2d1e0SGrzegorz Jaszczyk unsigned int irqs[MAX_NUM_HOST_IRQS]; 10904e2d1e0SGrzegorz Jaszczyk void __iomem *base; 11004e2d1e0SGrzegorz Jaszczyk struct irq_domain *domain; 11104e2d1e0SGrzegorz Jaszczyk const struct pruss_intc_match_data *soc_config; 11204e2d1e0SGrzegorz Jaszczyk struct device *dev; 11304e2d1e0SGrzegorz Jaszczyk struct mutex lock; /* PRUSS INTC lock */ 11404e2d1e0SGrzegorz Jaszczyk }; 11504e2d1e0SGrzegorz Jaszczyk 11604e2d1e0SGrzegorz Jaszczyk /** 11704e2d1e0SGrzegorz Jaszczyk * struct pruss_host_irq_data - PRUSS host irq data structure 11804e2d1e0SGrzegorz Jaszczyk * @intc: PRUSS interrupt controller pointer 11904e2d1e0SGrzegorz Jaszczyk * @host_irq: host irq number 12004e2d1e0SGrzegorz Jaszczyk */ 12104e2d1e0SGrzegorz Jaszczyk struct pruss_host_irq_data { 12204e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc; 12304e2d1e0SGrzegorz Jaszczyk u8 host_irq; 12404e2d1e0SGrzegorz Jaszczyk }; 12504e2d1e0SGrzegorz Jaszczyk 12604e2d1e0SGrzegorz Jaszczyk static inline u32 pruss_intc_read_reg(struct pruss_intc *intc, unsigned int reg) 12704e2d1e0SGrzegorz Jaszczyk { 12804e2d1e0SGrzegorz Jaszczyk return readl_relaxed(intc->base + reg); 12904e2d1e0SGrzegorz Jaszczyk } 13004e2d1e0SGrzegorz Jaszczyk 13104e2d1e0SGrzegorz Jaszczyk static inline void pruss_intc_write_reg(struct pruss_intc *intc, 13204e2d1e0SGrzegorz Jaszczyk unsigned int reg, u32 val) 13304e2d1e0SGrzegorz Jaszczyk { 13404e2d1e0SGrzegorz Jaszczyk writel_relaxed(val, intc->base + reg); 13504e2d1e0SGrzegorz Jaszczyk } 13604e2d1e0SGrzegorz Jaszczyk 13704e2d1e0SGrzegorz Jaszczyk static void pruss_intc_update_cmr(struct pruss_intc *intc, unsigned int evt, 13804e2d1e0SGrzegorz Jaszczyk u8 ch) 13904e2d1e0SGrzegorz Jaszczyk { 14004e2d1e0SGrzegorz Jaszczyk u32 idx, offset, val; 14104e2d1e0SGrzegorz Jaszczyk 14204e2d1e0SGrzegorz Jaszczyk idx = evt / CMR_EVT_PER_REG; 14304e2d1e0SGrzegorz Jaszczyk offset = (evt % CMR_EVT_PER_REG) * CMR_EVT_MAP_BITS; 14404e2d1e0SGrzegorz Jaszczyk 14504e2d1e0SGrzegorz Jaszczyk val = pruss_intc_read_reg(intc, PRU_INTC_CMR(idx)); 14604e2d1e0SGrzegorz Jaszczyk val &= ~(CMR_EVT_MAP_MASK << offset); 14704e2d1e0SGrzegorz Jaszczyk val |= ch << offset; 14804e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_CMR(idx), val); 14904e2d1e0SGrzegorz Jaszczyk 15004e2d1e0SGrzegorz Jaszczyk dev_dbg(intc->dev, "SYSEV%u -> CH%d (CMR%d 0x%08x)\n", evt, ch, 15104e2d1e0SGrzegorz Jaszczyk idx, pruss_intc_read_reg(intc, PRU_INTC_CMR(idx))); 15204e2d1e0SGrzegorz Jaszczyk } 15304e2d1e0SGrzegorz Jaszczyk 15404e2d1e0SGrzegorz Jaszczyk static void pruss_intc_update_hmr(struct pruss_intc *intc, u8 ch, u8 host) 15504e2d1e0SGrzegorz Jaszczyk { 15604e2d1e0SGrzegorz Jaszczyk u32 idx, offset, val; 15704e2d1e0SGrzegorz Jaszczyk 15804e2d1e0SGrzegorz Jaszczyk idx = ch / HMR_CH_PER_REG; 15904e2d1e0SGrzegorz Jaszczyk offset = (ch % HMR_CH_PER_REG) * HMR_CH_MAP_BITS; 16004e2d1e0SGrzegorz Jaszczyk 16104e2d1e0SGrzegorz Jaszczyk val = pruss_intc_read_reg(intc, PRU_INTC_HMR(idx)); 16204e2d1e0SGrzegorz Jaszczyk val &= ~(HMR_CH_MAP_MASK << offset); 16304e2d1e0SGrzegorz Jaszczyk val |= host << offset; 16404e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_HMR(idx), val); 16504e2d1e0SGrzegorz Jaszczyk 16604e2d1e0SGrzegorz Jaszczyk dev_dbg(intc->dev, "CH%d -> HOST%d (HMR%d 0x%08x)\n", ch, host, idx, 16704e2d1e0SGrzegorz Jaszczyk pruss_intc_read_reg(intc, PRU_INTC_HMR(idx))); 16804e2d1e0SGrzegorz Jaszczyk } 16904e2d1e0SGrzegorz Jaszczyk 17004e2d1e0SGrzegorz Jaszczyk /** 17104e2d1e0SGrzegorz Jaszczyk * pruss_intc_map() - configure the PRUSS INTC 17204e2d1e0SGrzegorz Jaszczyk * @intc: PRUSS interrupt controller pointer 17304e2d1e0SGrzegorz Jaszczyk * @hwirq: the system event number 17404e2d1e0SGrzegorz Jaszczyk * 17504e2d1e0SGrzegorz Jaszczyk * Configures the PRUSS INTC with the provided configuration from the one parsed 17604e2d1e0SGrzegorz Jaszczyk * in the xlate function. 17704e2d1e0SGrzegorz Jaszczyk */ 17804e2d1e0SGrzegorz Jaszczyk static void pruss_intc_map(struct pruss_intc *intc, unsigned long hwirq) 17904e2d1e0SGrzegorz Jaszczyk { 18004e2d1e0SGrzegorz Jaszczyk struct device *dev = intc->dev; 18104e2d1e0SGrzegorz Jaszczyk u8 ch, host, reg_idx; 18204e2d1e0SGrzegorz Jaszczyk u32 val; 18304e2d1e0SGrzegorz Jaszczyk 18404e2d1e0SGrzegorz Jaszczyk mutex_lock(&intc->lock); 18504e2d1e0SGrzegorz Jaszczyk 18604e2d1e0SGrzegorz Jaszczyk intc->event_channel[hwirq].ref_count++; 18704e2d1e0SGrzegorz Jaszczyk 18804e2d1e0SGrzegorz Jaszczyk ch = intc->event_channel[hwirq].value; 18904e2d1e0SGrzegorz Jaszczyk host = intc->channel_host[ch].value; 19004e2d1e0SGrzegorz Jaszczyk 19104e2d1e0SGrzegorz Jaszczyk pruss_intc_update_cmr(intc, hwirq, ch); 19204e2d1e0SGrzegorz Jaszczyk 19304e2d1e0SGrzegorz Jaszczyk reg_idx = hwirq / 32; 19404e2d1e0SGrzegorz Jaszczyk val = BIT(hwirq % 32); 19504e2d1e0SGrzegorz Jaszczyk 19604e2d1e0SGrzegorz Jaszczyk /* clear and enable system event */ 19704e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_ESR(reg_idx), val); 19804e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_SECR(reg_idx), val); 19904e2d1e0SGrzegorz Jaszczyk 20004e2d1e0SGrzegorz Jaszczyk if (++intc->channel_host[ch].ref_count == 1) { 20104e2d1e0SGrzegorz Jaszczyk pruss_intc_update_hmr(intc, ch, host); 20204e2d1e0SGrzegorz Jaszczyk 20304e2d1e0SGrzegorz Jaszczyk /* enable host interrupts */ 20404e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_HIEISR, host); 20504e2d1e0SGrzegorz Jaszczyk } 20604e2d1e0SGrzegorz Jaszczyk 20704e2d1e0SGrzegorz Jaszczyk dev_dbg(dev, "mapped system_event = %lu channel = %d host = %d", 20804e2d1e0SGrzegorz Jaszczyk hwirq, ch, host); 20904e2d1e0SGrzegorz Jaszczyk 21004e2d1e0SGrzegorz Jaszczyk mutex_unlock(&intc->lock); 21104e2d1e0SGrzegorz Jaszczyk } 21204e2d1e0SGrzegorz Jaszczyk 21304e2d1e0SGrzegorz Jaszczyk /** 21404e2d1e0SGrzegorz Jaszczyk * pruss_intc_unmap() - unconfigure the PRUSS INTC 21504e2d1e0SGrzegorz Jaszczyk * @intc: PRUSS interrupt controller pointer 21604e2d1e0SGrzegorz Jaszczyk * @hwirq: the system event number 21704e2d1e0SGrzegorz Jaszczyk * 21804e2d1e0SGrzegorz Jaszczyk * Undo whatever was done in pruss_intc_map() for a PRU core. 21904e2d1e0SGrzegorz Jaszczyk * Mappings are reference counted, so resources are only disabled when there 22004e2d1e0SGrzegorz Jaszczyk * are no longer any users. 22104e2d1e0SGrzegorz Jaszczyk */ 22204e2d1e0SGrzegorz Jaszczyk static void pruss_intc_unmap(struct pruss_intc *intc, unsigned long hwirq) 22304e2d1e0SGrzegorz Jaszczyk { 22404e2d1e0SGrzegorz Jaszczyk u8 ch, host, reg_idx; 22504e2d1e0SGrzegorz Jaszczyk u32 val; 22604e2d1e0SGrzegorz Jaszczyk 22704e2d1e0SGrzegorz Jaszczyk mutex_lock(&intc->lock); 22804e2d1e0SGrzegorz Jaszczyk 22904e2d1e0SGrzegorz Jaszczyk ch = intc->event_channel[hwirq].value; 23004e2d1e0SGrzegorz Jaszczyk host = intc->channel_host[ch].value; 23104e2d1e0SGrzegorz Jaszczyk 23204e2d1e0SGrzegorz Jaszczyk if (--intc->channel_host[ch].ref_count == 0) { 23304e2d1e0SGrzegorz Jaszczyk /* disable host interrupts */ 23404e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_HIDISR, host); 23504e2d1e0SGrzegorz Jaszczyk 23604e2d1e0SGrzegorz Jaszczyk /* clear the map using reset value 0 */ 23704e2d1e0SGrzegorz Jaszczyk pruss_intc_update_hmr(intc, ch, 0); 23804e2d1e0SGrzegorz Jaszczyk } 23904e2d1e0SGrzegorz Jaszczyk 24004e2d1e0SGrzegorz Jaszczyk intc->event_channel[hwirq].ref_count--; 24104e2d1e0SGrzegorz Jaszczyk reg_idx = hwirq / 32; 24204e2d1e0SGrzegorz Jaszczyk val = BIT(hwirq % 32); 24304e2d1e0SGrzegorz Jaszczyk 24404e2d1e0SGrzegorz Jaszczyk /* disable system events */ 24504e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_ECR(reg_idx), val); 24604e2d1e0SGrzegorz Jaszczyk /* clear any pending status */ 24704e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_SECR(reg_idx), val); 24804e2d1e0SGrzegorz Jaszczyk 24904e2d1e0SGrzegorz Jaszczyk /* clear the map using reset value 0 */ 25004e2d1e0SGrzegorz Jaszczyk pruss_intc_update_cmr(intc, hwirq, 0); 25104e2d1e0SGrzegorz Jaszczyk 25204e2d1e0SGrzegorz Jaszczyk dev_dbg(intc->dev, "unmapped system_event = %lu channel = %d host = %d\n", 25304e2d1e0SGrzegorz Jaszczyk hwirq, ch, host); 25404e2d1e0SGrzegorz Jaszczyk 25504e2d1e0SGrzegorz Jaszczyk mutex_unlock(&intc->lock); 25604e2d1e0SGrzegorz Jaszczyk } 25704e2d1e0SGrzegorz Jaszczyk 25804e2d1e0SGrzegorz Jaszczyk static void pruss_intc_init(struct pruss_intc *intc) 25904e2d1e0SGrzegorz Jaszczyk { 26004e2d1e0SGrzegorz Jaszczyk const struct pruss_intc_match_data *soc_config = intc->soc_config; 26104e2d1e0SGrzegorz Jaszczyk int num_chnl_map_regs, num_host_intr_regs, num_event_type_regs, i; 26204e2d1e0SGrzegorz Jaszczyk 26304e2d1e0SGrzegorz Jaszczyk num_chnl_map_regs = DIV_ROUND_UP(soc_config->num_system_events, 26404e2d1e0SGrzegorz Jaszczyk CMR_EVT_PER_REG); 26504e2d1e0SGrzegorz Jaszczyk num_host_intr_regs = DIV_ROUND_UP(soc_config->num_host_events, 26604e2d1e0SGrzegorz Jaszczyk HMR_CH_PER_REG); 26704e2d1e0SGrzegorz Jaszczyk num_event_type_regs = DIV_ROUND_UP(soc_config->num_system_events, 32); 26804e2d1e0SGrzegorz Jaszczyk 26904e2d1e0SGrzegorz Jaszczyk /* 27004e2d1e0SGrzegorz Jaszczyk * configure polarity (SIPR register) to active high and 27104e2d1e0SGrzegorz Jaszczyk * type (SITR register) to level interrupt for all system events 27204e2d1e0SGrzegorz Jaszczyk */ 27304e2d1e0SGrzegorz Jaszczyk for (i = 0; i < num_event_type_regs; i++) { 27404e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_SIPR(i), 0xffffffff); 27504e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_SITR(i), 0); 27604e2d1e0SGrzegorz Jaszczyk } 27704e2d1e0SGrzegorz Jaszczyk 27804e2d1e0SGrzegorz Jaszczyk /* clear all interrupt channel map registers, 4 events per register */ 27904e2d1e0SGrzegorz Jaszczyk for (i = 0; i < num_chnl_map_regs; i++) 28004e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_CMR(i), 0); 28104e2d1e0SGrzegorz Jaszczyk 28204e2d1e0SGrzegorz Jaszczyk /* clear all host interrupt map registers, 4 channels per register */ 28304e2d1e0SGrzegorz Jaszczyk for (i = 0; i < num_host_intr_regs; i++) 28404e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_HMR(i), 0); 28504e2d1e0SGrzegorz Jaszczyk 28604e2d1e0SGrzegorz Jaszczyk /* global interrupt enable */ 28704e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_GER, 1); 28804e2d1e0SGrzegorz Jaszczyk } 28904e2d1e0SGrzegorz Jaszczyk 29004e2d1e0SGrzegorz Jaszczyk static void pruss_intc_irq_ack(struct irq_data *data) 29104e2d1e0SGrzegorz Jaszczyk { 29204e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = irq_data_get_irq_chip_data(data); 29304e2d1e0SGrzegorz Jaszczyk unsigned int hwirq = data->hwirq; 29404e2d1e0SGrzegorz Jaszczyk 29504e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq); 29604e2d1e0SGrzegorz Jaszczyk } 29704e2d1e0SGrzegorz Jaszczyk 29804e2d1e0SGrzegorz Jaszczyk static void pruss_intc_irq_mask(struct irq_data *data) 29904e2d1e0SGrzegorz Jaszczyk { 30004e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = irq_data_get_irq_chip_data(data); 30104e2d1e0SGrzegorz Jaszczyk unsigned int hwirq = data->hwirq; 30204e2d1e0SGrzegorz Jaszczyk 30304e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_EICR, hwirq); 30404e2d1e0SGrzegorz Jaszczyk } 30504e2d1e0SGrzegorz Jaszczyk 30604e2d1e0SGrzegorz Jaszczyk static void pruss_intc_irq_unmask(struct irq_data *data) 30704e2d1e0SGrzegorz Jaszczyk { 30804e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = irq_data_get_irq_chip_data(data); 30904e2d1e0SGrzegorz Jaszczyk unsigned int hwirq = data->hwirq; 31004e2d1e0SGrzegorz Jaszczyk 31104e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_EISR, hwirq); 31204e2d1e0SGrzegorz Jaszczyk } 31304e2d1e0SGrzegorz Jaszczyk 31404e2d1e0SGrzegorz Jaszczyk static int pruss_intc_irq_reqres(struct irq_data *data) 31504e2d1e0SGrzegorz Jaszczyk { 31604e2d1e0SGrzegorz Jaszczyk if (!try_module_get(THIS_MODULE)) 31704e2d1e0SGrzegorz Jaszczyk return -ENODEV; 31804e2d1e0SGrzegorz Jaszczyk 31904e2d1e0SGrzegorz Jaszczyk return 0; 32004e2d1e0SGrzegorz Jaszczyk } 32104e2d1e0SGrzegorz Jaszczyk 32204e2d1e0SGrzegorz Jaszczyk static void pruss_intc_irq_relres(struct irq_data *data) 32304e2d1e0SGrzegorz Jaszczyk { 32404e2d1e0SGrzegorz Jaszczyk module_put(THIS_MODULE); 32504e2d1e0SGrzegorz Jaszczyk } 32604e2d1e0SGrzegorz Jaszczyk 327b1026e8aSDavid Lechner static int pruss_intc_irq_get_irqchip_state(struct irq_data *data, 328b1026e8aSDavid Lechner enum irqchip_irq_state which, 329b1026e8aSDavid Lechner bool *state) 330b1026e8aSDavid Lechner { 331b1026e8aSDavid Lechner struct pruss_intc *intc = irq_data_get_irq_chip_data(data); 332b1026e8aSDavid Lechner u32 reg, mask, srsr; 333b1026e8aSDavid Lechner 334b1026e8aSDavid Lechner if (which != IRQCHIP_STATE_PENDING) 335b1026e8aSDavid Lechner return -EINVAL; 336b1026e8aSDavid Lechner 337b1026e8aSDavid Lechner reg = PRU_INTC_SRSR(data->hwirq / 32); 338b1026e8aSDavid Lechner mask = BIT(data->hwirq % 32); 339b1026e8aSDavid Lechner 340b1026e8aSDavid Lechner srsr = pruss_intc_read_reg(intc, reg); 341b1026e8aSDavid Lechner 342b1026e8aSDavid Lechner *state = !!(srsr & mask); 343b1026e8aSDavid Lechner 344b1026e8aSDavid Lechner return 0; 345b1026e8aSDavid Lechner } 346b1026e8aSDavid Lechner 347b1026e8aSDavid Lechner static int pruss_intc_irq_set_irqchip_state(struct irq_data *data, 348b1026e8aSDavid Lechner enum irqchip_irq_state which, 349b1026e8aSDavid Lechner bool state) 350b1026e8aSDavid Lechner { 351b1026e8aSDavid Lechner struct pruss_intc *intc = irq_data_get_irq_chip_data(data); 352b1026e8aSDavid Lechner 353b1026e8aSDavid Lechner if (which != IRQCHIP_STATE_PENDING) 354b1026e8aSDavid Lechner return -EINVAL; 355b1026e8aSDavid Lechner 356b1026e8aSDavid Lechner if (state) 357b1026e8aSDavid Lechner pruss_intc_write_reg(intc, PRU_INTC_SISR, data->hwirq); 358b1026e8aSDavid Lechner else 359b1026e8aSDavid Lechner pruss_intc_write_reg(intc, PRU_INTC_SICR, data->hwirq); 360b1026e8aSDavid Lechner 361b1026e8aSDavid Lechner return 0; 362b1026e8aSDavid Lechner } 363b1026e8aSDavid Lechner 36404e2d1e0SGrzegorz Jaszczyk static struct irq_chip pruss_irqchip = { 36504e2d1e0SGrzegorz Jaszczyk .name = "pruss-intc", 36604e2d1e0SGrzegorz Jaszczyk .irq_ack = pruss_intc_irq_ack, 36704e2d1e0SGrzegorz Jaszczyk .irq_mask = pruss_intc_irq_mask, 36804e2d1e0SGrzegorz Jaszczyk .irq_unmask = pruss_intc_irq_unmask, 36904e2d1e0SGrzegorz Jaszczyk .irq_request_resources = pruss_intc_irq_reqres, 37004e2d1e0SGrzegorz Jaszczyk .irq_release_resources = pruss_intc_irq_relres, 371b1026e8aSDavid Lechner .irq_get_irqchip_state = pruss_intc_irq_get_irqchip_state, 372b1026e8aSDavid Lechner .irq_set_irqchip_state = pruss_intc_irq_set_irqchip_state, 37304e2d1e0SGrzegorz Jaszczyk }; 37404e2d1e0SGrzegorz Jaszczyk 37504e2d1e0SGrzegorz Jaszczyk static int pruss_intc_validate_mapping(struct pruss_intc *intc, int event, 37604e2d1e0SGrzegorz Jaszczyk int channel, int host) 37704e2d1e0SGrzegorz Jaszczyk { 37804e2d1e0SGrzegorz Jaszczyk struct device *dev = intc->dev; 37904e2d1e0SGrzegorz Jaszczyk int ret = 0; 38004e2d1e0SGrzegorz Jaszczyk 38104e2d1e0SGrzegorz Jaszczyk mutex_lock(&intc->lock); 38204e2d1e0SGrzegorz Jaszczyk 38304e2d1e0SGrzegorz Jaszczyk /* check if sysevent already assigned */ 38404e2d1e0SGrzegorz Jaszczyk if (intc->event_channel[event].ref_count > 0 && 38504e2d1e0SGrzegorz Jaszczyk intc->event_channel[event].value != channel) { 38604e2d1e0SGrzegorz Jaszczyk dev_err(dev, "event %d (req. ch %d) already assigned to channel %d\n", 38704e2d1e0SGrzegorz Jaszczyk event, channel, intc->event_channel[event].value); 38804e2d1e0SGrzegorz Jaszczyk ret = -EBUSY; 38904e2d1e0SGrzegorz Jaszczyk goto unlock; 39004e2d1e0SGrzegorz Jaszczyk } 39104e2d1e0SGrzegorz Jaszczyk 39204e2d1e0SGrzegorz Jaszczyk /* check if channel already assigned */ 39304e2d1e0SGrzegorz Jaszczyk if (intc->channel_host[channel].ref_count > 0 && 39404e2d1e0SGrzegorz Jaszczyk intc->channel_host[channel].value != host) { 39504e2d1e0SGrzegorz Jaszczyk dev_err(dev, "channel %d (req. host %d) already assigned to host %d\n", 39604e2d1e0SGrzegorz Jaszczyk channel, host, intc->channel_host[channel].value); 39704e2d1e0SGrzegorz Jaszczyk ret = -EBUSY; 39804e2d1e0SGrzegorz Jaszczyk goto unlock; 39904e2d1e0SGrzegorz Jaszczyk } 40004e2d1e0SGrzegorz Jaszczyk 40104e2d1e0SGrzegorz Jaszczyk intc->event_channel[event].value = channel; 40204e2d1e0SGrzegorz Jaszczyk intc->channel_host[channel].value = host; 40304e2d1e0SGrzegorz Jaszczyk 40404e2d1e0SGrzegorz Jaszczyk unlock: 40504e2d1e0SGrzegorz Jaszczyk mutex_unlock(&intc->lock); 40604e2d1e0SGrzegorz Jaszczyk return ret; 40704e2d1e0SGrzegorz Jaszczyk } 40804e2d1e0SGrzegorz Jaszczyk 40904e2d1e0SGrzegorz Jaszczyk static int 41004e2d1e0SGrzegorz Jaszczyk pruss_intc_irq_domain_xlate(struct irq_domain *d, struct device_node *node, 41104e2d1e0SGrzegorz Jaszczyk const u32 *intspec, unsigned int intsize, 41204e2d1e0SGrzegorz Jaszczyk unsigned long *out_hwirq, unsigned int *out_type) 41304e2d1e0SGrzegorz Jaszczyk { 41404e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = d->host_data; 41504e2d1e0SGrzegorz Jaszczyk struct device *dev = intc->dev; 41604e2d1e0SGrzegorz Jaszczyk int ret, sys_event, channel, host; 41704e2d1e0SGrzegorz Jaszczyk 41804e2d1e0SGrzegorz Jaszczyk if (intsize < 3) 41904e2d1e0SGrzegorz Jaszczyk return -EINVAL; 42004e2d1e0SGrzegorz Jaszczyk 42104e2d1e0SGrzegorz Jaszczyk sys_event = intspec[0]; 42204e2d1e0SGrzegorz Jaszczyk if (sys_event < 0 || sys_event >= intc->soc_config->num_system_events) { 42304e2d1e0SGrzegorz Jaszczyk dev_err(dev, "%d is not valid event number\n", sys_event); 42404e2d1e0SGrzegorz Jaszczyk return -EINVAL; 42504e2d1e0SGrzegorz Jaszczyk } 42604e2d1e0SGrzegorz Jaszczyk 42704e2d1e0SGrzegorz Jaszczyk channel = intspec[1]; 42804e2d1e0SGrzegorz Jaszczyk if (channel < 0 || channel >= intc->soc_config->num_host_events) { 42904e2d1e0SGrzegorz Jaszczyk dev_err(dev, "%d is not valid channel number", channel); 43004e2d1e0SGrzegorz Jaszczyk return -EINVAL; 43104e2d1e0SGrzegorz Jaszczyk } 43204e2d1e0SGrzegorz Jaszczyk 43304e2d1e0SGrzegorz Jaszczyk host = intspec[2]; 43404e2d1e0SGrzegorz Jaszczyk if (host < 0 || host >= intc->soc_config->num_host_events) { 43504e2d1e0SGrzegorz Jaszczyk dev_err(dev, "%d is not valid host irq number\n", host); 43604e2d1e0SGrzegorz Jaszczyk return -EINVAL; 43704e2d1e0SGrzegorz Jaszczyk } 43804e2d1e0SGrzegorz Jaszczyk 43904e2d1e0SGrzegorz Jaszczyk /* check if requested sys_event was already mapped, if so validate it */ 44004e2d1e0SGrzegorz Jaszczyk ret = pruss_intc_validate_mapping(intc, sys_event, channel, host); 44104e2d1e0SGrzegorz Jaszczyk if (ret) 44204e2d1e0SGrzegorz Jaszczyk return ret; 44304e2d1e0SGrzegorz Jaszczyk 44404e2d1e0SGrzegorz Jaszczyk *out_hwirq = sys_event; 44504e2d1e0SGrzegorz Jaszczyk *out_type = IRQ_TYPE_LEVEL_HIGH; 44604e2d1e0SGrzegorz Jaszczyk 44704e2d1e0SGrzegorz Jaszczyk return 0; 44804e2d1e0SGrzegorz Jaszczyk } 44904e2d1e0SGrzegorz Jaszczyk 45004e2d1e0SGrzegorz Jaszczyk static int pruss_intc_irq_domain_map(struct irq_domain *d, unsigned int virq, 45104e2d1e0SGrzegorz Jaszczyk irq_hw_number_t hw) 45204e2d1e0SGrzegorz Jaszczyk { 45304e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = d->host_data; 45404e2d1e0SGrzegorz Jaszczyk 45504e2d1e0SGrzegorz Jaszczyk pruss_intc_map(intc, hw); 45604e2d1e0SGrzegorz Jaszczyk 45704e2d1e0SGrzegorz Jaszczyk irq_set_chip_data(virq, intc); 45804e2d1e0SGrzegorz Jaszczyk irq_set_chip_and_handler(virq, &pruss_irqchip, handle_level_irq); 45904e2d1e0SGrzegorz Jaszczyk 46004e2d1e0SGrzegorz Jaszczyk return 0; 46104e2d1e0SGrzegorz Jaszczyk } 46204e2d1e0SGrzegorz Jaszczyk 46304e2d1e0SGrzegorz Jaszczyk static void pruss_intc_irq_domain_unmap(struct irq_domain *d, unsigned int virq) 46404e2d1e0SGrzegorz Jaszczyk { 46504e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = d->host_data; 46604e2d1e0SGrzegorz Jaszczyk unsigned long hwirq = irqd_to_hwirq(irq_get_irq_data(virq)); 46704e2d1e0SGrzegorz Jaszczyk 46804e2d1e0SGrzegorz Jaszczyk irq_set_chip_and_handler(virq, NULL, NULL); 46904e2d1e0SGrzegorz Jaszczyk irq_set_chip_data(virq, NULL); 47004e2d1e0SGrzegorz Jaszczyk pruss_intc_unmap(intc, hwirq); 47104e2d1e0SGrzegorz Jaszczyk } 47204e2d1e0SGrzegorz Jaszczyk 47304e2d1e0SGrzegorz Jaszczyk static const struct irq_domain_ops pruss_intc_irq_domain_ops = { 47404e2d1e0SGrzegorz Jaszczyk .xlate = pruss_intc_irq_domain_xlate, 47504e2d1e0SGrzegorz Jaszczyk .map = pruss_intc_irq_domain_map, 47604e2d1e0SGrzegorz Jaszczyk .unmap = pruss_intc_irq_domain_unmap, 47704e2d1e0SGrzegorz Jaszczyk }; 47804e2d1e0SGrzegorz Jaszczyk 47904e2d1e0SGrzegorz Jaszczyk static void pruss_intc_irq_handler(struct irq_desc *desc) 48004e2d1e0SGrzegorz Jaszczyk { 48104e2d1e0SGrzegorz Jaszczyk unsigned int irq = irq_desc_get_irq(desc); 48204e2d1e0SGrzegorz Jaszczyk struct irq_chip *chip = irq_desc_get_chip(desc); 48304e2d1e0SGrzegorz Jaszczyk struct pruss_host_irq_data *host_irq_data = irq_get_handler_data(irq); 48404e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = host_irq_data->intc; 48504e2d1e0SGrzegorz Jaszczyk u8 host_irq = host_irq_data->host_irq + FIRST_PRU_HOST_INT; 48604e2d1e0SGrzegorz Jaszczyk 48704e2d1e0SGrzegorz Jaszczyk chained_irq_enter(chip, desc); 48804e2d1e0SGrzegorz Jaszczyk 48904e2d1e0SGrzegorz Jaszczyk while (true) { 49004e2d1e0SGrzegorz Jaszczyk u32 hipir; 491*046a6ee2SMarc Zyngier int hwirq, err; 49204e2d1e0SGrzegorz Jaszczyk 49304e2d1e0SGrzegorz Jaszczyk /* get highest priority pending PRUSS system event */ 49404e2d1e0SGrzegorz Jaszczyk hipir = pruss_intc_read_reg(intc, PRU_INTC_HIPIR(host_irq)); 49504e2d1e0SGrzegorz Jaszczyk if (hipir & INTC_HIPIR_NONE_HINT) 49604e2d1e0SGrzegorz Jaszczyk break; 49704e2d1e0SGrzegorz Jaszczyk 49804e2d1e0SGrzegorz Jaszczyk hwirq = hipir & GENMASK(9, 0); 499*046a6ee2SMarc Zyngier err = generic_handle_domain_irq(intc->domain, hwirq); 50004e2d1e0SGrzegorz Jaszczyk 50104e2d1e0SGrzegorz Jaszczyk /* 50204e2d1e0SGrzegorz Jaszczyk * NOTE: manually ACK any system events that do not have a 50304e2d1e0SGrzegorz Jaszczyk * handler mapped yet 50404e2d1e0SGrzegorz Jaszczyk */ 505*046a6ee2SMarc Zyngier if (WARN_ON_ONCE(err)) 50604e2d1e0SGrzegorz Jaszczyk pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq); 50704e2d1e0SGrzegorz Jaszczyk } 50804e2d1e0SGrzegorz Jaszczyk 50904e2d1e0SGrzegorz Jaszczyk chained_irq_exit(chip, desc); 51004e2d1e0SGrzegorz Jaszczyk } 51104e2d1e0SGrzegorz Jaszczyk 51204e2d1e0SGrzegorz Jaszczyk static const char * const irq_names[MAX_NUM_HOST_IRQS] = { 51304e2d1e0SGrzegorz Jaszczyk "host_intr0", "host_intr1", "host_intr2", "host_intr3", 51404e2d1e0SGrzegorz Jaszczyk "host_intr4", "host_intr5", "host_intr6", "host_intr7", 51504e2d1e0SGrzegorz Jaszczyk }; 51604e2d1e0SGrzegorz Jaszczyk 51704e2d1e0SGrzegorz Jaszczyk static int pruss_intc_probe(struct platform_device *pdev) 51804e2d1e0SGrzegorz Jaszczyk { 51904e2d1e0SGrzegorz Jaszczyk const struct pruss_intc_match_data *data; 52004e2d1e0SGrzegorz Jaszczyk struct device *dev = &pdev->dev; 52104e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc; 52204e2d1e0SGrzegorz Jaszczyk struct pruss_host_irq_data *host_data; 52304e2d1e0SGrzegorz Jaszczyk int i, irq, ret; 5246016f32dSSuman Anna u8 max_system_events, irqs_reserved = 0; 52504e2d1e0SGrzegorz Jaszczyk 52604e2d1e0SGrzegorz Jaszczyk data = of_device_get_match_data(dev); 52704e2d1e0SGrzegorz Jaszczyk if (!data) 52804e2d1e0SGrzegorz Jaszczyk return -ENODEV; 52904e2d1e0SGrzegorz Jaszczyk 53004e2d1e0SGrzegorz Jaszczyk max_system_events = data->num_system_events; 53104e2d1e0SGrzegorz Jaszczyk 53204e2d1e0SGrzegorz Jaszczyk intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL); 53304e2d1e0SGrzegorz Jaszczyk if (!intc) 53404e2d1e0SGrzegorz Jaszczyk return -ENOMEM; 53504e2d1e0SGrzegorz Jaszczyk 53604e2d1e0SGrzegorz Jaszczyk intc->soc_config = data; 53704e2d1e0SGrzegorz Jaszczyk intc->dev = dev; 53804e2d1e0SGrzegorz Jaszczyk platform_set_drvdata(pdev, intc); 53904e2d1e0SGrzegorz Jaszczyk 54004e2d1e0SGrzegorz Jaszczyk intc->base = devm_platform_ioremap_resource(pdev, 0); 54104e2d1e0SGrzegorz Jaszczyk if (IS_ERR(intc->base)) 54204e2d1e0SGrzegorz Jaszczyk return PTR_ERR(intc->base); 54304e2d1e0SGrzegorz Jaszczyk 5446016f32dSSuman Anna ret = of_property_read_u8(dev->of_node, "ti,irqs-reserved", 5456016f32dSSuman Anna &irqs_reserved); 5466016f32dSSuman Anna 5476016f32dSSuman Anna /* 5486016f32dSSuman Anna * The irqs-reserved is used only for some SoC's therefore not having 5496016f32dSSuman Anna * this property is still valid 5506016f32dSSuman Anna */ 5516016f32dSSuman Anna if (ret < 0 && ret != -EINVAL) 5526016f32dSSuman Anna return ret; 5536016f32dSSuman Anna 55404e2d1e0SGrzegorz Jaszczyk pruss_intc_init(intc); 55504e2d1e0SGrzegorz Jaszczyk 55604e2d1e0SGrzegorz Jaszczyk mutex_init(&intc->lock); 55704e2d1e0SGrzegorz Jaszczyk 55804e2d1e0SGrzegorz Jaszczyk intc->domain = irq_domain_add_linear(dev->of_node, max_system_events, 55904e2d1e0SGrzegorz Jaszczyk &pruss_intc_irq_domain_ops, intc); 56004e2d1e0SGrzegorz Jaszczyk if (!intc->domain) 56104e2d1e0SGrzegorz Jaszczyk return -ENOMEM; 56204e2d1e0SGrzegorz Jaszczyk 56304e2d1e0SGrzegorz Jaszczyk for (i = 0; i < MAX_NUM_HOST_IRQS; i++) { 5646016f32dSSuman Anna if (irqs_reserved & BIT(i)) 5656016f32dSSuman Anna continue; 5666016f32dSSuman Anna 56704e2d1e0SGrzegorz Jaszczyk irq = platform_get_irq_byname(pdev, irq_names[i]); 56804e2d1e0SGrzegorz Jaszczyk if (irq <= 0) { 56904e2d1e0SGrzegorz Jaszczyk ret = (irq == 0) ? -EINVAL : irq; 57004e2d1e0SGrzegorz Jaszczyk goto fail_irq; 57104e2d1e0SGrzegorz Jaszczyk } 57204e2d1e0SGrzegorz Jaszczyk 57304e2d1e0SGrzegorz Jaszczyk intc->irqs[i] = irq; 57404e2d1e0SGrzegorz Jaszczyk 57504e2d1e0SGrzegorz Jaszczyk host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); 57604e2d1e0SGrzegorz Jaszczyk if (!host_data) { 57704e2d1e0SGrzegorz Jaszczyk ret = -ENOMEM; 57804e2d1e0SGrzegorz Jaszczyk goto fail_irq; 57904e2d1e0SGrzegorz Jaszczyk } 58004e2d1e0SGrzegorz Jaszczyk 58104e2d1e0SGrzegorz Jaszczyk host_data->intc = intc; 58204e2d1e0SGrzegorz Jaszczyk host_data->host_irq = i; 58304e2d1e0SGrzegorz Jaszczyk 58404e2d1e0SGrzegorz Jaszczyk irq_set_handler_data(irq, host_data); 58504e2d1e0SGrzegorz Jaszczyk irq_set_chained_handler(irq, pruss_intc_irq_handler); 58604e2d1e0SGrzegorz Jaszczyk } 58704e2d1e0SGrzegorz Jaszczyk 58804e2d1e0SGrzegorz Jaszczyk return 0; 58904e2d1e0SGrzegorz Jaszczyk 59004e2d1e0SGrzegorz Jaszczyk fail_irq: 5916016f32dSSuman Anna while (--i >= 0) { 5926016f32dSSuman Anna if (intc->irqs[i]) 5936016f32dSSuman Anna irq_set_chained_handler_and_data(intc->irqs[i], NULL, 5946016f32dSSuman Anna NULL); 5956016f32dSSuman Anna } 59604e2d1e0SGrzegorz Jaszczyk 59704e2d1e0SGrzegorz Jaszczyk irq_domain_remove(intc->domain); 59804e2d1e0SGrzegorz Jaszczyk 59904e2d1e0SGrzegorz Jaszczyk return ret; 60004e2d1e0SGrzegorz Jaszczyk } 60104e2d1e0SGrzegorz Jaszczyk 60204e2d1e0SGrzegorz Jaszczyk static int pruss_intc_remove(struct platform_device *pdev) 60304e2d1e0SGrzegorz Jaszczyk { 60404e2d1e0SGrzegorz Jaszczyk struct pruss_intc *intc = platform_get_drvdata(pdev); 60504e2d1e0SGrzegorz Jaszczyk u8 max_system_events = intc->soc_config->num_system_events; 60604e2d1e0SGrzegorz Jaszczyk unsigned int hwirq; 60704e2d1e0SGrzegorz Jaszczyk int i; 60804e2d1e0SGrzegorz Jaszczyk 6096016f32dSSuman Anna for (i = 0; i < MAX_NUM_HOST_IRQS; i++) { 6106016f32dSSuman Anna if (intc->irqs[i]) 6116016f32dSSuman Anna irq_set_chained_handler_and_data(intc->irqs[i], NULL, 6126016f32dSSuman Anna NULL); 6136016f32dSSuman Anna } 61404e2d1e0SGrzegorz Jaszczyk 61504e2d1e0SGrzegorz Jaszczyk for (hwirq = 0; hwirq < max_system_events; hwirq++) 61604e2d1e0SGrzegorz Jaszczyk irq_dispose_mapping(irq_find_mapping(intc->domain, hwirq)); 61704e2d1e0SGrzegorz Jaszczyk 61804e2d1e0SGrzegorz Jaszczyk irq_domain_remove(intc->domain); 61904e2d1e0SGrzegorz Jaszczyk 62004e2d1e0SGrzegorz Jaszczyk return 0; 62104e2d1e0SGrzegorz Jaszczyk } 62204e2d1e0SGrzegorz Jaszczyk 62304e2d1e0SGrzegorz Jaszczyk static const struct pruss_intc_match_data pruss_intc_data = { 62404e2d1e0SGrzegorz Jaszczyk .num_system_events = 64, 62504e2d1e0SGrzegorz Jaszczyk .num_host_events = 10, 62604e2d1e0SGrzegorz Jaszczyk }; 62704e2d1e0SGrzegorz Jaszczyk 6287e92dee6SSuman Anna static const struct pruss_intc_match_data icssg_intc_data = { 6297e92dee6SSuman Anna .num_system_events = 160, 6307e92dee6SSuman Anna .num_host_events = 20, 6317e92dee6SSuman Anna }; 6327e92dee6SSuman Anna 63304e2d1e0SGrzegorz Jaszczyk static const struct of_device_id pruss_intc_of_match[] = { 63404e2d1e0SGrzegorz Jaszczyk { 63504e2d1e0SGrzegorz Jaszczyk .compatible = "ti,pruss-intc", 63604e2d1e0SGrzegorz Jaszczyk .data = &pruss_intc_data, 63704e2d1e0SGrzegorz Jaszczyk }, 6387e92dee6SSuman Anna { 6397e92dee6SSuman Anna .compatible = "ti,icssg-intc", 6407e92dee6SSuman Anna .data = &icssg_intc_data, 6417e92dee6SSuman Anna }, 64204e2d1e0SGrzegorz Jaszczyk { /* sentinel */ }, 64304e2d1e0SGrzegorz Jaszczyk }; 64404e2d1e0SGrzegorz Jaszczyk MODULE_DEVICE_TABLE(of, pruss_intc_of_match); 64504e2d1e0SGrzegorz Jaszczyk 64604e2d1e0SGrzegorz Jaszczyk static struct platform_driver pruss_intc_driver = { 64704e2d1e0SGrzegorz Jaszczyk .driver = { 64804e2d1e0SGrzegorz Jaszczyk .name = "pruss-intc", 64904e2d1e0SGrzegorz Jaszczyk .of_match_table = pruss_intc_of_match, 65004e2d1e0SGrzegorz Jaszczyk .suppress_bind_attrs = true, 65104e2d1e0SGrzegorz Jaszczyk }, 65204e2d1e0SGrzegorz Jaszczyk .probe = pruss_intc_probe, 65304e2d1e0SGrzegorz Jaszczyk .remove = pruss_intc_remove, 65404e2d1e0SGrzegorz Jaszczyk }; 65504e2d1e0SGrzegorz Jaszczyk module_platform_driver(pruss_intc_driver); 65604e2d1e0SGrzegorz Jaszczyk 65704e2d1e0SGrzegorz Jaszczyk MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 65804e2d1e0SGrzegorz Jaszczyk MODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); 65904e2d1e0SGrzegorz Jaszczyk MODULE_AUTHOR("Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org>"); 66004e2d1e0SGrzegorz Jaszczyk MODULE_DESCRIPTION("TI PRU-ICSS INTC Driver"); 66104e2d1e0SGrzegorz Jaszczyk MODULE_LICENSE("GPL v2"); 662