13a93261fSAshish Kalra /*
23a93261fSAshish Kalra * Driver for ePAPR Embedded Hypervisor PIC
33a93261fSAshish Kalra *
43a93261fSAshish Kalra * Copyright 2008-2011 Freescale Semiconductor, Inc.
53a93261fSAshish Kalra *
63a93261fSAshish Kalra * Author: Ashish Kalra <ashish.kalra@freescale.com>
73a93261fSAshish Kalra *
83a93261fSAshish Kalra * This file is licensed under the terms of the GNU General Public License
93a93261fSAshish Kalra * version 2. This program is licensed "as is" without any warranty of any
103a93261fSAshish Kalra * kind, whether express or implied.
113a93261fSAshish Kalra */
123a93261fSAshish Kalra
133a93261fSAshish Kalra #include <linux/types.h>
143a93261fSAshish Kalra #include <linux/kernel.h>
153a93261fSAshish Kalra #include <linux/init.h>
163a93261fSAshish Kalra #include <linux/irq.h>
1713a9a5d1SMarc Zyngier #include <linux/irqdomain.h>
183a93261fSAshish Kalra #include <linux/smp.h>
193a93261fSAshish Kalra #include <linux/interrupt.h>
203a93261fSAshish Kalra #include <linux/slab.h>
213a93261fSAshish Kalra #include <linux/spinlock.h>
223a93261fSAshish Kalra #include <linux/of.h>
23c11eede6SRob Herring #include <linux/of_address.h>
243a93261fSAshish Kalra
253a93261fSAshish Kalra #include <asm/io.h>
263a93261fSAshish Kalra #include <asm/irq.h>
273a93261fSAshish Kalra #include <asm/smp.h>
283a93261fSAshish Kalra #include <asm/machdep.h>
293a93261fSAshish Kalra #include <asm/ehv_pic.h>
303a93261fSAshish Kalra #include <asm/fsl_hcalls.h>
313a93261fSAshish Kalra
323a93261fSAshish Kalra static struct ehv_pic *global_ehv_pic;
333a93261fSAshish Kalra static DEFINE_SPINLOCK(ehv_pic_lock);
343a93261fSAshish Kalra
353a93261fSAshish Kalra static u32 hwirq_intspec[NR_EHV_PIC_INTS];
363a93261fSAshish Kalra static u32 __iomem *mpic_percpu_base_vaddr;
373a93261fSAshish Kalra
383a93261fSAshish Kalra #define IRQ_TYPE_MPIC_DIRECT 4
393a93261fSAshish Kalra #define MPIC_EOI 0x00B0
403a93261fSAshish Kalra
413a93261fSAshish Kalra /*
423a93261fSAshish Kalra * Linux descriptor level callbacks
433a93261fSAshish Kalra */
443a93261fSAshish Kalra
ehv_pic_unmask_irq(struct irq_data * d)45*c265735fSChristophe Leroy static void ehv_pic_unmask_irq(struct irq_data *d)
463a93261fSAshish Kalra {
473a93261fSAshish Kalra unsigned int src = virq_to_hw(d->irq);
483a93261fSAshish Kalra
493a93261fSAshish Kalra ev_int_set_mask(src, 0);
503a93261fSAshish Kalra }
513a93261fSAshish Kalra
ehv_pic_mask_irq(struct irq_data * d)52*c265735fSChristophe Leroy static void ehv_pic_mask_irq(struct irq_data *d)
533a93261fSAshish Kalra {
543a93261fSAshish Kalra unsigned int src = virq_to_hw(d->irq);
553a93261fSAshish Kalra
563a93261fSAshish Kalra ev_int_set_mask(src, 1);
573a93261fSAshish Kalra }
583a93261fSAshish Kalra
ehv_pic_end_irq(struct irq_data * d)59*c265735fSChristophe Leroy static void ehv_pic_end_irq(struct irq_data *d)
603a93261fSAshish Kalra {
613a93261fSAshish Kalra unsigned int src = virq_to_hw(d->irq);
623a93261fSAshish Kalra
633a93261fSAshish Kalra ev_int_eoi(src);
643a93261fSAshish Kalra }
653a93261fSAshish Kalra
ehv_pic_direct_end_irq(struct irq_data * d)66*c265735fSChristophe Leroy static void ehv_pic_direct_end_irq(struct irq_data *d)
673a93261fSAshish Kalra {
683a93261fSAshish Kalra out_be32(mpic_percpu_base_vaddr + MPIC_EOI / 4, 0);
693a93261fSAshish Kalra }
703a93261fSAshish Kalra
ehv_pic_set_affinity(struct irq_data * d,const struct cpumask * dest,bool force)71*c265735fSChristophe Leroy static int ehv_pic_set_affinity(struct irq_data *d, const struct cpumask *dest,
723a93261fSAshish Kalra bool force)
733a93261fSAshish Kalra {
743a93261fSAshish Kalra unsigned int src = virq_to_hw(d->irq);
753a93261fSAshish Kalra unsigned int config, prio, cpu_dest;
763a93261fSAshish Kalra int cpuid = irq_choose_cpu(dest);
773a93261fSAshish Kalra unsigned long flags;
783a93261fSAshish Kalra
793a93261fSAshish Kalra spin_lock_irqsave(&ehv_pic_lock, flags);
803a93261fSAshish Kalra ev_int_get_config(src, &config, &prio, &cpu_dest);
813a93261fSAshish Kalra ev_int_set_config(src, config, prio, cpuid);
823a93261fSAshish Kalra spin_unlock_irqrestore(&ehv_pic_lock, flags);
833a93261fSAshish Kalra
84dcb615aeSAlexander Gordeev return IRQ_SET_MASK_OK;
853a93261fSAshish Kalra }
863a93261fSAshish Kalra
ehv_pic_type_to_vecpri(unsigned int type)873a93261fSAshish Kalra static unsigned int ehv_pic_type_to_vecpri(unsigned int type)
883a93261fSAshish Kalra {
893a93261fSAshish Kalra /* Now convert sense value */
903a93261fSAshish Kalra
913a93261fSAshish Kalra switch (type & IRQ_TYPE_SENSE_MASK) {
923a93261fSAshish Kalra case IRQ_TYPE_EDGE_RISING:
933a93261fSAshish Kalra return EHV_PIC_INFO(VECPRI_SENSE_EDGE) |
943a93261fSAshish Kalra EHV_PIC_INFO(VECPRI_POLARITY_POSITIVE);
953a93261fSAshish Kalra
963a93261fSAshish Kalra case IRQ_TYPE_EDGE_FALLING:
973a93261fSAshish Kalra case IRQ_TYPE_EDGE_BOTH:
983a93261fSAshish Kalra return EHV_PIC_INFO(VECPRI_SENSE_EDGE) |
993a93261fSAshish Kalra EHV_PIC_INFO(VECPRI_POLARITY_NEGATIVE);
1003a93261fSAshish Kalra
1013a93261fSAshish Kalra case IRQ_TYPE_LEVEL_HIGH:
1023a93261fSAshish Kalra return EHV_PIC_INFO(VECPRI_SENSE_LEVEL) |
1033a93261fSAshish Kalra EHV_PIC_INFO(VECPRI_POLARITY_POSITIVE);
1043a93261fSAshish Kalra
1053a93261fSAshish Kalra case IRQ_TYPE_LEVEL_LOW:
1063a93261fSAshish Kalra default:
1073a93261fSAshish Kalra return EHV_PIC_INFO(VECPRI_SENSE_LEVEL) |
1083a93261fSAshish Kalra EHV_PIC_INFO(VECPRI_POLARITY_NEGATIVE);
1093a93261fSAshish Kalra }
1103a93261fSAshish Kalra }
1113a93261fSAshish Kalra
ehv_pic_set_irq_type(struct irq_data * d,unsigned int flow_type)112*c265735fSChristophe Leroy static int ehv_pic_set_irq_type(struct irq_data *d, unsigned int flow_type)
1133a93261fSAshish Kalra {
1143a93261fSAshish Kalra unsigned int src = virq_to_hw(d->irq);
1153a93261fSAshish Kalra unsigned int vecpri, vold, vnew, prio, cpu_dest;
1163a93261fSAshish Kalra unsigned long flags;
1173a93261fSAshish Kalra
1183a93261fSAshish Kalra if (flow_type == IRQ_TYPE_NONE)
1193a93261fSAshish Kalra flow_type = IRQ_TYPE_LEVEL_LOW;
1203a93261fSAshish Kalra
121c866cda4SThomas Gleixner irqd_set_trigger_type(d, flow_type);
1223a93261fSAshish Kalra
1233a93261fSAshish Kalra vecpri = ehv_pic_type_to_vecpri(flow_type);
1243a93261fSAshish Kalra
1253a93261fSAshish Kalra spin_lock_irqsave(&ehv_pic_lock, flags);
1263a93261fSAshish Kalra ev_int_get_config(src, &vold, &prio, &cpu_dest);
1273a93261fSAshish Kalra vnew = vold & ~(EHV_PIC_INFO(VECPRI_POLARITY_MASK) |
1283a93261fSAshish Kalra EHV_PIC_INFO(VECPRI_SENSE_MASK));
1293a93261fSAshish Kalra vnew |= vecpri;
1303a93261fSAshish Kalra
1313a93261fSAshish Kalra /*
1323a93261fSAshish Kalra * TODO : Add specific interface call for platform to set
1333a93261fSAshish Kalra * individual interrupt priorities.
1343a93261fSAshish Kalra * platform currently using static/default priority for all ints
1353a93261fSAshish Kalra */
1363a93261fSAshish Kalra
1373a93261fSAshish Kalra prio = 8;
1383a93261fSAshish Kalra
1393a93261fSAshish Kalra ev_int_set_config(src, vecpri, prio, cpu_dest);
1403a93261fSAshish Kalra
1413a93261fSAshish Kalra spin_unlock_irqrestore(&ehv_pic_lock, flags);
142c866cda4SThomas Gleixner return IRQ_SET_MASK_OK_NOCOPY;
1433a93261fSAshish Kalra }
1443a93261fSAshish Kalra
1453a93261fSAshish Kalra static struct irq_chip ehv_pic_irq_chip = {
1463a93261fSAshish Kalra .irq_mask = ehv_pic_mask_irq,
1473a93261fSAshish Kalra .irq_unmask = ehv_pic_unmask_irq,
1483a93261fSAshish Kalra .irq_eoi = ehv_pic_end_irq,
1493a93261fSAshish Kalra .irq_set_type = ehv_pic_set_irq_type,
1503a93261fSAshish Kalra };
1513a93261fSAshish Kalra
1523a93261fSAshish Kalra static struct irq_chip ehv_pic_direct_eoi_irq_chip = {
1533a93261fSAshish Kalra .irq_mask = ehv_pic_mask_irq,
1543a93261fSAshish Kalra .irq_unmask = ehv_pic_unmask_irq,
1553a93261fSAshish Kalra .irq_eoi = ehv_pic_direct_end_irq,
1563a93261fSAshish Kalra .irq_set_type = ehv_pic_set_irq_type,
1573a93261fSAshish Kalra };
1583a93261fSAshish Kalra
159ef24ba70SMichael Ellerman /* Return an interrupt vector or 0 if no interrupt is pending. */
ehv_pic_get_irq(void)1603a93261fSAshish Kalra unsigned int ehv_pic_get_irq(void)
1613a93261fSAshish Kalra {
1623a93261fSAshish Kalra int irq;
1633a93261fSAshish Kalra
1643a93261fSAshish Kalra BUG_ON(global_ehv_pic == NULL);
1653a93261fSAshish Kalra
1663a93261fSAshish Kalra if (global_ehv_pic->coreint_flag)
1673a93261fSAshish Kalra irq = mfspr(SPRN_EPR); /* if core int mode */
1683a93261fSAshish Kalra else
1693a93261fSAshish Kalra ev_int_iack(0, &irq); /* legacy mode */
1703a93261fSAshish Kalra
1713a93261fSAshish Kalra if (irq == 0xFFFF) /* 0xFFFF --> no irq is pending */
172ef24ba70SMichael Ellerman return 0;
1733a93261fSAshish Kalra
1743a93261fSAshish Kalra /*
1753a93261fSAshish Kalra * this will also setup revmap[] in the slow path for the first
1763a93261fSAshish Kalra * time, next calls will always use fast path by indexing revmap
1773a93261fSAshish Kalra */
1783a93261fSAshish Kalra return irq_linear_revmap(global_ehv_pic->irqhost, irq);
1793a93261fSAshish Kalra }
1803a93261fSAshish Kalra
ehv_pic_host_match(struct irq_domain * h,struct device_node * node,enum irq_domain_bus_token bus_token)181ad3aedfbSMarc Zyngier static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node,
182ad3aedfbSMarc Zyngier enum irq_domain_bus_token bus_token)
1833a93261fSAshish Kalra {
1843a93261fSAshish Kalra /* Exact match, unless ehv_pic node is NULL */
1855d4c9bc7SMarc Zyngier struct device_node *of_node = irq_domain_get_of_node(h);
1865d4c9bc7SMarc Zyngier return of_node == NULL || of_node == node;
1873a93261fSAshish Kalra }
1883a93261fSAshish Kalra
ehv_pic_host_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hw)189bae1d8f1SGrant Likely static int ehv_pic_host_map(struct irq_domain *h, unsigned int virq,
1903a93261fSAshish Kalra irq_hw_number_t hw)
1913a93261fSAshish Kalra {
1923a93261fSAshish Kalra struct ehv_pic *ehv_pic = h->host_data;
1933a93261fSAshish Kalra struct irq_chip *chip;
1943a93261fSAshish Kalra
1953a93261fSAshish Kalra /* Default chip */
1963a93261fSAshish Kalra chip = &ehv_pic->hc_irq;
1973a93261fSAshish Kalra
1983a93261fSAshish Kalra if (mpic_percpu_base_vaddr)
1993a93261fSAshish Kalra if (hwirq_intspec[hw] & IRQ_TYPE_MPIC_DIRECT)
2003a93261fSAshish Kalra chip = &ehv_pic_direct_eoi_irq_chip;
2013a93261fSAshish Kalra
2023a93261fSAshish Kalra irq_set_chip_data(virq, chip);
2033a93261fSAshish Kalra /*
2043a93261fSAshish Kalra * using handle_fasteoi_irq as our irq handler, this will
2053a93261fSAshish Kalra * only call the eoi callback and suitable for the MPIC
2063a93261fSAshish Kalra * controller which set ISR/IPR automatically and clear the
2073a93261fSAshish Kalra * highest priority active interrupt in ISR/IPR when we do
2083a93261fSAshish Kalra * a specific eoi
2093a93261fSAshish Kalra */
2103a93261fSAshish Kalra irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq);
2113a93261fSAshish Kalra
2123a93261fSAshish Kalra /* Set default irq type */
2133a93261fSAshish Kalra irq_set_irq_type(virq, IRQ_TYPE_NONE);
2143a93261fSAshish Kalra
2153a93261fSAshish Kalra return 0;
2163a93261fSAshish Kalra }
2173a93261fSAshish Kalra
ehv_pic_host_xlate(struct irq_domain * h,struct device_node * ct,const u32 * intspec,unsigned int intsize,irq_hw_number_t * out_hwirq,unsigned int * out_flags)218bae1d8f1SGrant Likely static int ehv_pic_host_xlate(struct irq_domain *h, struct device_node *ct,
2193a93261fSAshish Kalra const u32 *intspec, unsigned int intsize,
2203a93261fSAshish Kalra irq_hw_number_t *out_hwirq, unsigned int *out_flags)
2213a93261fSAshish Kalra
2223a93261fSAshish Kalra {
2233a93261fSAshish Kalra /*
2243a93261fSAshish Kalra * interrupt sense values coming from the guest device tree
2253a93261fSAshish Kalra * interrupt specifiers can have four possible sense and
2263a93261fSAshish Kalra * level encoding information and they need to
2273a93261fSAshish Kalra * be translated between firmware type & linux type.
2283a93261fSAshish Kalra */
2293a93261fSAshish Kalra
2303a93261fSAshish Kalra static unsigned char map_of_senses_to_linux_irqtype[4] = {
2313a93261fSAshish Kalra IRQ_TYPE_EDGE_FALLING,
2323a93261fSAshish Kalra IRQ_TYPE_EDGE_RISING,
2333a93261fSAshish Kalra IRQ_TYPE_LEVEL_LOW,
2343a93261fSAshish Kalra IRQ_TYPE_LEVEL_HIGH,
2353a93261fSAshish Kalra };
2363a93261fSAshish Kalra
2373a93261fSAshish Kalra *out_hwirq = intspec[0];
2383a93261fSAshish Kalra if (intsize > 1) {
2393a93261fSAshish Kalra hwirq_intspec[intspec[0]] = intspec[1];
2403a93261fSAshish Kalra *out_flags = map_of_senses_to_linux_irqtype[intspec[1] &
2413a93261fSAshish Kalra ~IRQ_TYPE_MPIC_DIRECT];
2423a93261fSAshish Kalra } else {
2433a93261fSAshish Kalra *out_flags = IRQ_TYPE_NONE;
2443a93261fSAshish Kalra }
2453a93261fSAshish Kalra
2463a93261fSAshish Kalra return 0;
2473a93261fSAshish Kalra }
2483a93261fSAshish Kalra
2499f70b8ebSGrant Likely static const struct irq_domain_ops ehv_pic_host_ops = {
2503a93261fSAshish Kalra .match = ehv_pic_host_match,
2513a93261fSAshish Kalra .map = ehv_pic_host_map,
2523a93261fSAshish Kalra .xlate = ehv_pic_host_xlate,
2533a93261fSAshish Kalra };
2543a93261fSAshish Kalra
ehv_pic_init(void)2553a93261fSAshish Kalra void __init ehv_pic_init(void)
2563a93261fSAshish Kalra {
2573a93261fSAshish Kalra struct device_node *np, *np2;
2583a93261fSAshish Kalra struct ehv_pic *ehv_pic;
2593a93261fSAshish Kalra
2603a93261fSAshish Kalra np = of_find_compatible_node(NULL, NULL, "epapr,hv-pic");
2613a93261fSAshish Kalra if (!np) {
2623a93261fSAshish Kalra pr_err("ehv_pic_init: could not find epapr,hv-pic node\n");
2633a93261fSAshish Kalra return;
2643a93261fSAshish Kalra }
2653a93261fSAshish Kalra
2663a93261fSAshish Kalra ehv_pic = kzalloc(sizeof(struct ehv_pic), GFP_KERNEL);
2673a93261fSAshish Kalra if (!ehv_pic) {
2683a93261fSAshish Kalra of_node_put(np);
2693a93261fSAshish Kalra return;
2703a93261fSAshish Kalra }
2713a93261fSAshish Kalra
272a8db8cf0SGrant Likely ehv_pic->irqhost = irq_domain_add_linear(np, NR_EHV_PIC_INTS,
273a8db8cf0SGrant Likely &ehv_pic_host_ops, ehv_pic);
2743a93261fSAshish Kalra if (!ehv_pic->irqhost) {
2753a93261fSAshish Kalra of_node_put(np);
276e3854b6eSJulia Lawall kfree(ehv_pic);
2773a93261fSAshish Kalra return;
2783a93261fSAshish Kalra }
2793a93261fSAshish Kalra
2803a93261fSAshish Kalra np2 = of_find_compatible_node(NULL, NULL, "fsl,hv-mpic-per-cpu");
2813a93261fSAshish Kalra if (np2) {
2823a93261fSAshish Kalra mpic_percpu_base_vaddr = of_iomap(np2, 0);
2833a93261fSAshish Kalra if (!mpic_percpu_base_vaddr)
2843a93261fSAshish Kalra pr_err("ehv_pic_init: of_iomap failed\n");
2853a93261fSAshish Kalra
2863a93261fSAshish Kalra of_node_put(np2);
2873a93261fSAshish Kalra }
2883a93261fSAshish Kalra
2893a93261fSAshish Kalra ehv_pic->hc_irq = ehv_pic_irq_chip;
2903a93261fSAshish Kalra ehv_pic->hc_irq.irq_set_affinity = ehv_pic_set_affinity;
2914d57e351SRob Herring ehv_pic->coreint_flag = of_property_read_bool(np, "has-external-proxy");
2923a93261fSAshish Kalra
2933a93261fSAshish Kalra global_ehv_pic = ehv_pic;
2943a93261fSAshish Kalra irq_set_default_host(global_ehv_pic->irqhost);
2953a93261fSAshish Kalra }
296