From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: To: linux-pci@atrey.karlin.mff.cuni.cz From: Michael Ellerman Date: Thu, 25 Jan 2007 19:34:15 +1100 Subject: [RFC/PATCH 13/16] MPIC MSI allocator In-Reply-To: <1169714047.65693.647693675533.qpush@cradle> Message-Id: <20070125083416.D8D6EDE3B6@ozlabs.org> Cc: Greg Kroah-Hartman , Kyle McMartin , linuxppc-dev@ozlabs.org, Brice Goglin , shaohua.li@intel.com, "David S. Miller" , "Eric W. Biederman" List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To support MSI on MPIC we need a way to reserve and allocate hardware irq numbers, this patch implements an allocator for that. Updated to only do dogy-U3-fallback-hacks on U3, all other platforms must define a "msi-ranges" property on their MPIC node for MSI to work. Signed-off-by: Michael Ellerman --- arch/powerpc/sysdev/Makefile | 5 - arch/powerpc/sysdev/mpic.c | 4 arch/powerpc/sysdev/mpic.h | 27 ++++++ arch/powerpc/sysdev/mpic_msi.c | 171 +++++++++++++++++++++++++++++++++++++++++ include/asm-powerpc/mpic.h | 11 ++ 5 files changed, 217 insertions(+), 1 deletion(-) Index: msi/arch/powerpc/sysdev/Makefile =================================================================== --- msi.orig/arch/powerpc/sysdev/Makefile +++ msi/arch/powerpc/sysdev/Makefile @@ -2,7 +2,10 @@ ifeq ($(CONFIG_PPC64),y) EXTRA_CFLAGS += -mno-minimal-toc endif -obj-$(CONFIG_MPIC) += mpic.o +mpic-obj-y := mpic.o +mpic-obj-$(CONFIG_PCI_MSI) += mpic_msi.o +obj-$(CONFIG_MPIC) += $(mpic-obj-y) + obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_MPC106) += grackle.o obj-$(CONFIG_PPC_DCR) += dcr.o Index: msi/arch/powerpc/sysdev/mpic.c =================================================================== --- msi.orig/arch/powerpc/sysdev/mpic.c +++ msi/arch/powerpc/sysdev/mpic.c @@ -36,6 +36,8 @@ #include #include +#include "mpic.h" + #ifdef DEBUG #define DBG(fmt...) printk(fmt) #else @@ -825,6 +827,8 @@ static int mpic_host_map(struct irq_host if (hw >= mpic->irq_count) return -EINVAL; + mpic_msi_reserve_hwirq(mpic, hw); + /* Default chip */ chip = &mpic->hc_irq; Index: msi/arch/powerpc/sysdev/mpic.h =================================================================== --- /dev/null +++ msi/arch/powerpc/sysdev/mpic.h @@ -0,0 +1,27 @@ +#ifndef _POWERPC_SYSDEV_MPIC_H +#define _POWERPC_SYSDEV_MPIC_H + +/* + * Copyright 2006-2007, Michael Ellerman, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the + * License. + * + */ + +#include +#include + +#ifdef CONFIG_PCI_MSI +extern void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq); +#else +static inline void mpic_msi_reserve_hwirq(struct mpic *mpic, + irq_hw_number_t hwirq) +{ + return; +} +#endif + +#endif /* _POWERPC_SYSDEV_MPIC_H */ Index: msi/arch/powerpc/sysdev/mpic_msi.c =================================================================== --- /dev/null +++ msi/arch/powerpc/sysdev/mpic_msi.c @@ -0,0 +1,171 @@ +/* + * Copyright 2006-2007, Michael Ellerman, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 of the + * License. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +static void __mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq) +{ + msi_debug("reserving hwirq 0x%lx\n", hwirq); + bitmap_allocate_region(mpic->hwirq_bitmap, hwirq, 0); +} + +void mpic_msi_reserve_hwirq(struct mpic *mpic, irq_hw_number_t hwirq) +{ + unsigned long flags; + + /* The mpic calls this even when there is no allocator setup */ + if (!mpic->hwirq_bitmap) + return; + + spin_lock_irqsave(&mpic->bitmap_lock, flags); + __mpic_msi_reserve_hwirq(mpic, hwirq); + spin_unlock_irqrestore(&mpic->bitmap_lock, flags); +} + +irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num) +{ + unsigned long flags; + int offset, order = fls(num); + + spin_lock_irqsave(&mpic->bitmap_lock, flags); + /* + * This is fast, but stricter than we need. We might want to add + * a fallback routine which does a linear search with no alignment. + */ + offset = bitmap_find_free_region(mpic->hwirq_bitmap, mpic->irq_count, + order); + spin_unlock_irqrestore(&mpic->bitmap_lock, flags); + + msi_debug("allocated %d (2^%d) at offset %d\n", num, order, offset); + + return offset; +} + +void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num) +{ + unsigned long flags; + int order = fls(num); + + msi_debug("freeing %d (2^%d) at offset %d\n", num, order, offset); + + spin_lock_irqsave(&mpic->bitmap_lock, flags); + bitmap_release_region(mpic->hwirq_bitmap, offset, order); + spin_unlock_irqrestore(&mpic->bitmap_lock, flags); +} + +#ifdef CONFIG_MPIC_BROKEN_U3 +static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) +{ + irq_hw_number_t hwirq; + struct irq_host_ops *ops = mpic->irqhost->ops; + struct device_node *np; + int flags, index, i; + struct of_irq oirq; + + msi_debug("found U3, guessing msi allocator setup\n"); + + /* Reserve source numbers we know are reserved in the HW */ + for (i = 0; i < 8; i++) __mpic_msi_reserve_hwirq(mpic, i); + for (i = 42; i < 46; i++) __mpic_msi_reserve_hwirq(mpic, i); + for (i = 100; i < 105; i++) __mpic_msi_reserve_hwirq(mpic, i); + + np = NULL; + while ((np = of_find_all_nodes(np))) { + msi_debug("mapping hwirqs for %s\n", np->full_name); + + index = 0; + while (of_irq_map_one(np, index++, &oirq) == 0) { + ops->xlate(mpic->irqhost, NULL, oirq.specifier, + oirq.size, &hwirq, &flags); + __mpic_msi_reserve_hwirq(mpic, hwirq); + } + } + + return 0; +} +#else +static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic) { return -1; } +#endif + +static int mpic_msi_reserve_dt_hwirqs(struct mpic *mpic) +{ + int i, len; + const u32 *p; + + p = get_property(mpic->of_node, "msi-available-ranges", &len); + if (!p) { + msi_debug("no msi-available-ranges property found on %s\n", + mpic->of_node->full_name); + return -ENODEV; + } + + if (len % 8 != 0) { + printk(KERN_WARNING "Malformed msi-available-ranges " + "property on %s\n", mpic->of_node->full_name); + return -EINVAL; + } + + bitmap_allocate_region(mpic->hwirq_bitmap, 0, fls(mpic->irq_count)); + + /* Format is: ( )+ */ + len /= sizeof(u32); + for (i = 0; i < len / 2; i++, p += 2) + mpic_msi_free_hwirqs(mpic, *p, *(p + 1)); + + return 0; +} + +int mpic_msi_init_allocator(struct mpic *mpic) +{ + int rc, size; + + BUG_ON(mpic->hwirq_bitmap); + spin_lock_init(&mpic->bitmap_lock); + + size = mpic->irq_count / 8; + msi_debug("allocator bitmap size is 0x%x bytes\n", size); + + if (mem_init_done) + mpic->hwirq_bitmap = kmalloc(size, GFP_KERNEL); + else + mpic->hwirq_bitmap = alloc_bootmem(size); + + if (!mpic->hwirq_bitmap) { + msi_debug("no mem allocating allocator bitmap!\n"); + return -ENOMEM; + } + + memset(mpic->hwirq_bitmap, 0, size); + + rc = mpic_msi_reserve_dt_hwirqs(mpic); + if (rc) { + if (mpic->flags & MPIC_BROKEN_U3) + rc = mpic_msi_reserve_u3_hwirqs(mpic); + + if (rc) + goto out_free; + } + + return 0; + + out_free: + if (mem_init_done) + kfree(mpic->hwirq_bitmap); + + mpic->hwirq_bitmap = NULL; + return rc; +} Index: msi/include/asm-powerpc/mpic.h =================================================================== --- msi.orig/include/asm-powerpc/mpic.h +++ msi/include/asm-powerpc/mpic.h @@ -300,6 +300,11 @@ struct mpic u32 *hw_set; #endif +#ifdef CONFIG_PCI_MSI + spinlock_t bitmap_lock; + unsigned long *hwirq_bitmap; +#endif + /* link */ struct mpic *next; }; @@ -446,5 +451,11 @@ void mpic_set_clk_ratio(struct mpic *mpi /* Enable/Disable EPIC serial interrupt mode */ void mpic_set_serial_int(struct mpic *mpic, int enable); +#ifdef CONFIG_PCI_MSI +extern int mpic_msi_init_allocator(struct mpic *mpic); +extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num); +extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num); +#endif + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_MPIC_H */