19ad1acb4SBartosz Golaszewski // SPDX-License-Identifier: GPL-2.0-only 29ad1acb4SBartosz Golaszewski // 39ad1acb4SBartosz Golaszewski // Author: Steve Chen <schen@mvista.com> 49ad1acb4SBartosz Golaszewski // Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com> 59ad1acb4SBartosz Golaszewski // Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> 69ad1acb4SBartosz Golaszewski // Copyright (C) 2019, Texas Instruments 79ad1acb4SBartosz Golaszewski // 89ad1acb4SBartosz Golaszewski // TI Common Platform Interrupt Controller (cp_intc) driver 90521444dSSergei Shtylyov 1007caba96SHeiko Schocher #include <linux/export.h> 110521444dSSergei Shtylyov #include <linux/init.h> 120521444dSSergei Shtylyov #include <linux/irq.h> 139a7f2fc8SDavid Lechner #include <linux/irqchip.h> 146567954bSBartosz Golaszewski #include <linux/irqchip/irq-davinci-cp-intc.h> 1507caba96SHeiko Schocher #include <linux/irqdomain.h> 160521444dSSergei Shtylyov #include <linux/io.h> 17961e657fSHeiko Schocher #include <linux/of.h> 18961e657fSHeiko Schocher #include <linux/of_address.h> 19961e657fSHeiko Schocher #include <linux/of_irq.h> 200521444dSSergei Shtylyov 21d0064594SBartosz Golaszewski #include <asm/exception.h> 22ed4d189bSBartosz Golaszewski 23b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_CTRL 0x04 243b5d1c50SBartosz Golaszewski #define DAVINCI_CP_INTC_HOST_CTRL 0x0c 25b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_GLOBAL_ENABLE 0x10 26b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_SYS_STAT_IDX_CLR 0x24 27b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET 0x28 283b5d1c50SBartosz Golaszewski #define DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR 0x2c 29b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET 0x34 30b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR 0x38 31b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_PRIO_IDX 0x80 32b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_SYS_STAT_CLR(n) (0x0280 + (n << 2)) 33b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_SYS_ENABLE_CLR(n) (0x0380 + (n << 2)) 34b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_CHAN_MAP(n) (0x0400 + (n << 2)) 353b5d1c50SBartosz Golaszewski #define DAVINCI_CP_INTC_SYS_POLARITY(n) (0x0d00 + (n << 2)) 363b5d1c50SBartosz Golaszewski #define DAVINCI_CP_INTC_SYS_TYPE(n) (0x0d80 + (n << 2)) 37b35b55e7SBartosz Golaszewski #define DAVINCI_CP_INTC_HOST_ENABLE(n) (0x1500 + (n << 2)) 38d0064594SBartosz Golaszewski #define DAVINCI_CP_INTC_PRI_INDX_MASK GENMASK(9, 0) 39d0064594SBartosz Golaszewski #define DAVINCI_CP_INTC_GPIR_NONE BIT(31) 40d0064594SBartosz Golaszewski 41b35b55e7SBartosz Golaszewski static void __iomem *davinci_cp_intc_base; 42b35b55e7SBartosz Golaszewski static struct irq_domain *davinci_cp_intc_irq_domain; 43fb746842SBartosz Golaszewski 44b35b55e7SBartosz Golaszewski static inline unsigned int davinci_cp_intc_read(unsigned int offset) 450521444dSSergei Shtylyov { 46d43da8d7SBartosz Golaszewski return readl_relaxed(davinci_cp_intc_base + offset); 470521444dSSergei Shtylyov } 480521444dSSergei Shtylyov 49b35b55e7SBartosz Golaszewski static inline void davinci_cp_intc_write(unsigned long value, 50b35b55e7SBartosz Golaszewski unsigned int offset) 510521444dSSergei Shtylyov { 52d43da8d7SBartosz Golaszewski writel_relaxed(value, davinci_cp_intc_base + offset); 530521444dSSergei Shtylyov } 540521444dSSergei Shtylyov 55b35b55e7SBartosz Golaszewski static void davinci_cp_intc_ack_irq(struct irq_data *d) 560521444dSSergei Shtylyov { 57b35b55e7SBartosz Golaszewski davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_STAT_IDX_CLR); 580521444dSSergei Shtylyov } 590521444dSSergei Shtylyov 60b35b55e7SBartosz Golaszewski static void davinci_cp_intc_mask_irq(struct irq_data *d) 610521444dSSergei Shtylyov { 620521444dSSergei Shtylyov /* XXX don't know why we need to disable nIRQ here... */ 63b35b55e7SBartosz Golaszewski davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR); 64b35b55e7SBartosz Golaszewski davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR); 65b35b55e7SBartosz Golaszewski davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET); 660521444dSSergei Shtylyov } 670521444dSSergei Shtylyov 68b35b55e7SBartosz Golaszewski static void davinci_cp_intc_unmask_irq(struct irq_data *d) 690521444dSSergei Shtylyov { 70b35b55e7SBartosz Golaszewski davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET); 710521444dSSergei Shtylyov } 720521444dSSergei Shtylyov 73b35b55e7SBartosz Golaszewski static int davinci_cp_intc_set_irq_type(struct irq_data *d, 74b35b55e7SBartosz Golaszewski unsigned int flow_type) 750521444dSSergei Shtylyov { 769762d876SBartosz Golaszewski unsigned int reg, mask, polarity, type; 779762d876SBartosz Golaszewski 789762d876SBartosz Golaszewski reg = BIT_WORD(d->hwirq); 799762d876SBartosz Golaszewski mask = BIT_MASK(d->hwirq); 809762d876SBartosz Golaszewski polarity = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_POLARITY(reg)); 819762d876SBartosz Golaszewski type = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_TYPE(reg)); 820521444dSSergei Shtylyov 830521444dSSergei Shtylyov switch (flow_type) { 840521444dSSergei Shtylyov case IRQ_TYPE_EDGE_RISING: 850521444dSSergei Shtylyov polarity |= mask; 860521444dSSergei Shtylyov type |= mask; 870521444dSSergei Shtylyov break; 880521444dSSergei Shtylyov case IRQ_TYPE_EDGE_FALLING: 890521444dSSergei Shtylyov polarity &= ~mask; 900521444dSSergei Shtylyov type |= mask; 910521444dSSergei Shtylyov break; 920521444dSSergei Shtylyov case IRQ_TYPE_LEVEL_HIGH: 930521444dSSergei Shtylyov polarity |= mask; 940521444dSSergei Shtylyov type &= ~mask; 950521444dSSergei Shtylyov break; 960521444dSSergei Shtylyov case IRQ_TYPE_LEVEL_LOW: 970521444dSSergei Shtylyov polarity &= ~mask; 980521444dSSergei Shtylyov type &= ~mask; 990521444dSSergei Shtylyov break; 1000521444dSSergei Shtylyov default: 1010521444dSSergei Shtylyov return -EINVAL; 1020521444dSSergei Shtylyov } 1030521444dSSergei Shtylyov 104b35b55e7SBartosz Golaszewski davinci_cp_intc_write(polarity, DAVINCI_CP_INTC_SYS_POLARITY(reg)); 105b35b55e7SBartosz Golaszewski davinci_cp_intc_write(type, DAVINCI_CP_INTC_SYS_TYPE(reg)); 1060521444dSSergei Shtylyov 1070521444dSSergei Shtylyov return 0; 1080521444dSSergei Shtylyov } 1090521444dSSergei Shtylyov 110b35b55e7SBartosz Golaszewski static struct irq_chip davinci_cp_intc_irq_chip = { 1110521444dSSergei Shtylyov .name = "cp_intc", 112b35b55e7SBartosz Golaszewski .irq_ack = davinci_cp_intc_ack_irq, 113b35b55e7SBartosz Golaszewski .irq_mask = davinci_cp_intc_mask_irq, 114b35b55e7SBartosz Golaszewski .irq_unmask = davinci_cp_intc_unmask_irq, 115b35b55e7SBartosz Golaszewski .irq_set_type = davinci_cp_intc_set_irq_type, 1163f86e570SSudeep Holla .flags = IRQCHIP_SKIP_SET_WAKE, 1170521444dSSergei Shtylyov }; 1180521444dSSergei Shtylyov 119d0064594SBartosz Golaszewski static asmlinkage void __exception_irq_entry 120b35b55e7SBartosz Golaszewski davinci_cp_intc_handle_irq(struct pt_regs *regs) 121d0064594SBartosz Golaszewski { 122d0064594SBartosz Golaszewski int gpir, irqnr, none; 123d0064594SBartosz Golaszewski 124d0064594SBartosz Golaszewski /* 125d0064594SBartosz Golaszewski * The interrupt number is in first ten bits. The NONE field set to 1 126d0064594SBartosz Golaszewski * indicates a spurious irq. 127d0064594SBartosz Golaszewski */ 128d0064594SBartosz Golaszewski 129b35b55e7SBartosz Golaszewski gpir = davinci_cp_intc_read(DAVINCI_CP_INTC_PRIO_IDX); 130d0064594SBartosz Golaszewski irqnr = gpir & DAVINCI_CP_INTC_PRI_INDX_MASK; 131d0064594SBartosz Golaszewski none = gpir & DAVINCI_CP_INTC_GPIR_NONE; 132d0064594SBartosz Golaszewski 133d0064594SBartosz Golaszewski if (unlikely(none)) { 134d0064594SBartosz Golaszewski pr_err_once("%s: spurious irq!\n", __func__); 135d0064594SBartosz Golaszewski return; 136d0064594SBartosz Golaszewski } 137d0064594SBartosz Golaszewski 1380953fb26SMark Rutland generic_handle_domain_irq(davinci_cp_intc_irq_domain, irqnr); 139d0064594SBartosz Golaszewski } 140d0064594SBartosz Golaszewski 141b35b55e7SBartosz Golaszewski static int davinci_cp_intc_host_map(struct irq_domain *h, unsigned int virq, 14207caba96SHeiko Schocher irq_hw_number_t hw) 1430521444dSSergei Shtylyov { 14407caba96SHeiko Schocher pr_debug("cp_intc_host_map(%d, 0x%lx)\n", virq, hw); 14507caba96SHeiko Schocher 146b35b55e7SBartosz Golaszewski irq_set_chip(virq, &davinci_cp_intc_irq_chip); 147e8d36d5dSRob Herring irq_set_probe(virq); 14807caba96SHeiko Schocher irq_set_handler(virq, handle_edge_irq); 1499762d876SBartosz Golaszewski 15007caba96SHeiko Schocher return 0; 15107caba96SHeiko Schocher } 15207caba96SHeiko Schocher 153b35b55e7SBartosz Golaszewski static const struct irq_domain_ops davinci_cp_intc_irq_domain_ops = { 154b35b55e7SBartosz Golaszewski .map = davinci_cp_intc_host_map, 15507caba96SHeiko Schocher .xlate = irq_domain_xlate_onetwocell, 15607caba96SHeiko Schocher }; 15707caba96SHeiko Schocher 1586567954bSBartosz Golaszewski static int __init 1596567954bSBartosz Golaszewski davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config, 1606567954bSBartosz Golaszewski struct device_node *node) 16107caba96SHeiko Schocher { 1626567954bSBartosz Golaszewski unsigned int num_regs = BITS_TO_LONGS(config->num_irqs); 1636567954bSBartosz Golaszewski int offset, irq_base; 1649cf58a45SBartosz Golaszewski void __iomem *req; 1659cf58a45SBartosz Golaszewski 1669cf58a45SBartosz Golaszewski req = request_mem_region(config->reg.start, 1679cf58a45SBartosz Golaszewski resource_size(&config->reg), 1689cf58a45SBartosz Golaszewski "davinci-cp-intc"); 1699cf58a45SBartosz Golaszewski if (!req) { 1709cf58a45SBartosz Golaszewski pr_err("%s: register range busy\n", __func__); 1719cf58a45SBartosz Golaszewski return -EBUSY; 1729cf58a45SBartosz Golaszewski } 1730521444dSSergei Shtylyov 1746567954bSBartosz Golaszewski davinci_cp_intc_base = ioremap(config->reg.start, 1756567954bSBartosz Golaszewski resource_size(&config->reg)); 1766c702da6SBartosz Golaszewski if (!davinci_cp_intc_base) { 1776c702da6SBartosz Golaszewski pr_err("%s: unable to ioremap register range\n", __func__); 17807caba96SHeiko Schocher return -EINVAL; 1796c702da6SBartosz Golaszewski } 1800521444dSSergei Shtylyov 181b35b55e7SBartosz Golaszewski davinci_cp_intc_write(0, DAVINCI_CP_INTC_GLOBAL_ENABLE); 1820521444dSSergei Shtylyov 1830521444dSSergei Shtylyov /* Disable all host interrupts */ 184b35b55e7SBartosz Golaszewski davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_ENABLE(0)); 1850521444dSSergei Shtylyov 1860521444dSSergei Shtylyov /* Disable system interrupts */ 1876567954bSBartosz Golaszewski for (offset = 0; offset < num_regs; offset++) 1886567954bSBartosz Golaszewski davinci_cp_intc_write(~0, 1896567954bSBartosz Golaszewski DAVINCI_CP_INTC_SYS_ENABLE_CLR(offset)); 1900521444dSSergei Shtylyov 1910521444dSSergei Shtylyov /* Set to normal mode, no nesting, no priority hold */ 192b35b55e7SBartosz Golaszewski davinci_cp_intc_write(0, DAVINCI_CP_INTC_CTRL); 193b35b55e7SBartosz Golaszewski davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_CTRL); 1940521444dSSergei Shtylyov 1950521444dSSergei Shtylyov /* Clear system interrupt status */ 1966567954bSBartosz Golaszewski for (offset = 0; offset < num_regs; offset++) 1976567954bSBartosz Golaszewski davinci_cp_intc_write(~0, 1986567954bSBartosz Golaszewski DAVINCI_CP_INTC_SYS_STAT_CLR(offset)); 1990521444dSSergei Shtylyov 2000521444dSSergei Shtylyov /* Enable nIRQ (what about nFIQ?) */ 201b35b55e7SBartosz Golaszewski davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET); 2020521444dSSergei Shtylyov 2036567954bSBartosz Golaszewski /* Default all priorities to channel 7. */ 2046567954bSBartosz Golaszewski num_regs = (config->num_irqs + 3) >> 2; /* 4 channels per register */ 2056567954bSBartosz Golaszewski for (offset = 0; offset < num_regs; offset++) 2066567954bSBartosz Golaszewski davinci_cp_intc_write(0x07070707, 2076567954bSBartosz Golaszewski DAVINCI_CP_INTC_CHAN_MAP(offset)); 2080521444dSSergei Shtylyov 2096567954bSBartosz Golaszewski irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0); 21007caba96SHeiko Schocher if (irq_base < 0) { 2116c702da6SBartosz Golaszewski pr_err("%s: unable to allocate interrupt descriptors: %d\n", 2126c702da6SBartosz Golaszewski __func__, irq_base); 2136c702da6SBartosz Golaszewski return irq_base; 21407caba96SHeiko Schocher } 21507caba96SHeiko Schocher 216b35b55e7SBartosz Golaszewski davinci_cp_intc_irq_domain = irq_domain_add_legacy( 2176567954bSBartosz Golaszewski node, config->num_irqs, irq_base, 0, 218b35b55e7SBartosz Golaszewski &davinci_cp_intc_irq_domain_ops, NULL); 21907caba96SHeiko Schocher 220b35b55e7SBartosz Golaszewski if (!davinci_cp_intc_irq_domain) { 2216c702da6SBartosz Golaszewski pr_err("%s: unable to create an interrupt domain\n", __func__); 22207caba96SHeiko Schocher return -EINVAL; 2230521444dSSergei Shtylyov } 2240521444dSSergei Shtylyov 225b35b55e7SBartosz Golaszewski set_handle_irq(davinci_cp_intc_handle_irq); 226d0064594SBartosz Golaszewski 2270521444dSSergei Shtylyov /* Enable global interrupt */ 228b35b55e7SBartosz Golaszewski davinci_cp_intc_write(1, DAVINCI_CP_INTC_GLOBAL_ENABLE); 22907caba96SHeiko Schocher 23007caba96SHeiko Schocher return 0; 23107caba96SHeiko Schocher } 23207caba96SHeiko Schocher 2336567954bSBartosz Golaszewski int __init davinci_cp_intc_init(const struct davinci_cp_intc_config *config) 23407caba96SHeiko Schocher { 2356567954bSBartosz Golaszewski return davinci_cp_intc_do_init(config, NULL); 2360521444dSSergei Shtylyov } 2379a7f2fc8SDavid Lechner 2386567954bSBartosz Golaszewski static int __init davinci_cp_intc_of_init(struct device_node *node, 2396567954bSBartosz Golaszewski struct device_node *parent) 2406567954bSBartosz Golaszewski { 2416567954bSBartosz Golaszewski struct davinci_cp_intc_config config = { }; 2426567954bSBartosz Golaszewski int ret; 2436567954bSBartosz Golaszewski 2446567954bSBartosz Golaszewski ret = of_address_to_resource(node, 0, &config.reg); 2456567954bSBartosz Golaszewski if (ret) { 2466567954bSBartosz Golaszewski pr_err("%s: unable to get the register range from device-tree\n", 2476567954bSBartosz Golaszewski __func__); 2486567954bSBartosz Golaszewski return ret; 2496567954bSBartosz Golaszewski } 2506567954bSBartosz Golaszewski 2516567954bSBartosz Golaszewski ret = of_property_read_u32(node, "ti,intc-size", &config.num_irqs); 2526567954bSBartosz Golaszewski if (ret) { 2536567954bSBartosz Golaszewski pr_err("%s: unable to read the 'ti,intc-size' property\n", 2546567954bSBartosz Golaszewski __func__); 2556567954bSBartosz Golaszewski return ret; 2566567954bSBartosz Golaszewski } 2576567954bSBartosz Golaszewski 2586567954bSBartosz Golaszewski return davinci_cp_intc_do_init(&config, node); 2596567954bSBartosz Golaszewski } 260b35b55e7SBartosz Golaszewski IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", davinci_cp_intc_of_init); 261