Merge branch 'for-linus' of git://git.monstr.eu/linux-2.6-microblaze
Linus Torvalds [Thu, 18 Mar 2010 23:57:24 +0000 (16:57 -0700)]
* 'for-linus' of git://git.monstr.eu/linux-2.6-microblaze: (27 commits)
  microblaze: entry.S use delay slot for return handlers
  microblaze: Save current task directly
  microblaze: Simplify entry.S - save/restore r3/r4 - ret_from_trap
  microblaze: PCI early support for noMMU system
  microblaze: Fix dma alloc and free coherent dma functions
  microblaze: Add consistent code
  microblaze: pgtable.h: move consistent functions
  microblaze: Remove ancient Kconfig option for consistent mapping
  microblaze: Remove VMALLOC_VMADDR
  microblaze: Add define for ASM_LOOP
  microblaze: Preliminary support for dma drivers
  microblaze: remove trailing space in messages
  microblaze: Use generic show_mem()
  microblaze: Change temp register for cmdline
  microblaze: Preliminary support for dma drivers
  microblaze: Move cache function to cache.c
  microblaze: Add support from PREEMPT
  microblaze: Add support for Xilinx PCI host bridge
  microblaze: Enable PCI, missing files
  microblaze: Add core PCI files
  ...

33 files changed:
arch/microblaze/Kconfig
arch/microblaze/Makefile
arch/microblaze/include/asm/device.h
arch/microblaze/include/asm/dma-mapping.h
arch/microblaze/include/asm/io.h
arch/microblaze/include/asm/irq.h
arch/microblaze/include/asm/page.h
arch/microblaze/include/asm/pci-bridge.h
arch/microblaze/include/asm/pci.h
arch/microblaze/include/asm/pgalloc.h
arch/microblaze/include/asm/pgtable.h
arch/microblaze/include/asm/prom.h
arch/microblaze/include/asm/system.h
arch/microblaze/include/asm/tlbflush.h
arch/microblaze/kernel/Makefile
arch/microblaze/kernel/asm-offsets.c
arch/microblaze/kernel/cpu/cache.c
arch/microblaze/kernel/dma.c [new file with mode: 0644]
arch/microblaze/kernel/entry.S
arch/microblaze/kernel/head.S
arch/microblaze/kernel/irq.c
arch/microblaze/kernel/setup.c
arch/microblaze/mm/Makefile
arch/microblaze/mm/consistent.c [new file with mode: 0644]
arch/microblaze/mm/init.c
arch/microblaze/mm/pgtable.c
arch/microblaze/pci/Makefile [new file with mode: 0644]
arch/microblaze/pci/indirect_pci.c [new file with mode: 0644]
arch/microblaze/pci/iomap.c [new file with mode: 0644]
arch/microblaze/pci/pci-common.c [new file with mode: 0644]
arch/microblaze/pci/pci_32.c [new file with mode: 0644]
arch/microblaze/pci/xilinx_pci.c [new file with mode: 0644]
drivers/pci/Makefile

index b008168..203ec61 100644 (file)
@@ -14,6 +14,8 @@ config MICROBLAZE
        select USB_ARCH_HAS_EHCI
        select ARCH_WANT_OPTIONAL_GPIOLIB
        select HAVE_OPROFILE
+       select HAVE_DMA_ATTRS
+       select HAVE_DMA_API_DEBUG
        select TRACING_SUPPORT
 
 config SWAP
@@ -76,9 +78,6 @@ config HAVE_LATENCYTOP_SUPPORT
 config PCI
        def_bool n
 
-config NO_DMA
-       def_bool y
-
 config DTC
        def_bool y
 
@@ -146,7 +145,6 @@ menu "Advanced setup"
 
 config ADVANCED_OPTIONS
        bool "Prompt for advanced kernel configuration options"
-       depends on MMU
        help
          This option will enable prompting for a variety of advanced kernel
          configuration options.  These options can cause the kernel to not
@@ -158,6 +156,15 @@ config ADVANCED_OPTIONS
 comment "Default settings for advanced configuration options are used"
        depends on !ADVANCED_OPTIONS
 
+config XILINX_UNCACHED_SHADOW
+       bool "Are you using uncached shadow for RAM ?"
+       depends on ADVANCED_OPTIONS && !MMU
+       default n
+       help
+         This is needed to be able to allocate uncachable memory regions.
+         The feature requires the design to define the RAM memory controller
+         window to be twice as large as the actual physical memory.
+
 config HIGHMEM_START_BOOL
        bool "Set high memory pool address"
        depends on ADVANCED_OPTIONS && HIGHMEM
@@ -175,7 +182,7 @@ config HIGHMEM_START
 
 config LOWMEM_SIZE_BOOL
        bool "Set maximum low memory"
-       depends on ADVANCED_OPTIONS
+       depends on ADVANCED_OPTIONS && MMU
        help
          This option allows you to set the maximum amount of memory which
          will be used as "low memory", that is, memory which the kernel can
@@ -187,7 +194,6 @@ config LOWMEM_SIZE_BOOL
 
 config LOWMEM_SIZE
        hex "Maximum low memory size (in bytes)" if LOWMEM_SIZE_BOOL
-       depends on MMU
        default "0x30000000"
 
 config KERNEL_START_BOOL
@@ -208,7 +214,7 @@ config KERNEL_START
 
 config TASK_SIZE_BOOL
        bool "Set custom user task size"
-       depends on ADVANCED_OPTIONS
+       depends on ADVANCED_OPTIONS && MMU
        help
          This option allows you to set the amount of virtual address space
          allocated to user tasks.  This can be useful in optimizing the
@@ -218,42 +224,34 @@ config TASK_SIZE_BOOL
 
 config TASK_SIZE
        hex "Size of user task space" if TASK_SIZE_BOOL
-       depends on MMU
        default "0x80000000"
 
-config CONSISTENT_START_BOOL
-       bool "Set custom consistent memory pool address"
-       depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
-       help
-         This option allows you to set the base virtual address
-         of the the consistent memory pool.  This pool of virtual
-         memory is used to make consistent memory allocations.
+endmenu
 
-config CONSISTENT_START
-       hex "Base virtual address of consistent memory pool" if CONSISTENT_START_BOOL
-       depends on MMU
-       default "0xff100000" if NOT_COHERENT_CACHE
+source "mm/Kconfig"
 
-config CONSISTENT_SIZE_BOOL
-       bool "Set custom consistent memory pool size"
-       depends on ADVANCED_OPTIONS && NOT_COHERENT_CACHE
-       help
-         This option allows you to set the size of the the
-         consistent memory pool.  This pool of virtual memory
-         is used to make consistent memory allocations.
+menu "Exectuable file formats"
 
-config CONSISTENT_SIZE
-       hex "Size of consistent memory pool" if CONSISTENT_SIZE_BOOL
-       depends on MMU
-       default "0x00200000" if NOT_COHERENT_CACHE
+source "fs/Kconfig.binfmt"
 
 endmenu
 
-source "mm/Kconfig"
+menu "Bus Options"
 
-menu "Exectuable file formats"
+config PCI
+       bool "PCI support"
 
-source "fs/Kconfig.binfmt"
+config PCI_DOMAINS
+       def_bool PCI
+
+config PCI_SYSCALL
+       def_bool PCI
+
+config PCI_XILINX
+       bool "Xilinx PCI host bridge support"
+       depends on PCI
+
+source "drivers/pci/Kconfig"
 
 endmenu
 
index d2d6cfc..836832d 100644 (file)
@@ -50,6 +50,7 @@ libs-y += $(LIBGCC)
 core-y += arch/microblaze/kernel/
 core-y += arch/microblaze/mm/
 core-y += arch/microblaze/platform/
+core-$(CONFIG_PCI) += arch/microblaze/pci/
 
 drivers-$(CONFIG_OPROFILE) += arch/microblaze/oprofile/
 
index 78a0384..402b46e 100644 (file)
@@ -14,6 +14,10 @@ struct device_node;
 struct dev_archdata {
        /* Optional pointer to an OF device node */
        struct device_node      *of_node;
+
+       /* DMA operations on that device */
+       struct dma_map_ops      *dma_ops;
+       void                    *dma_data;
 };
 
 struct pdev_archdata {
index d00e400..18b3731 100644 (file)
@@ -1 +1,153 @@
-#include <asm-generic/dma-mapping-broken.h>
+/*
+ * Implements the generic device dma API for microblaze and the pci
+ *
+ * Copyright (C) 2009-2010 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2009-2010 PetaLogix
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ *
+ * This file is base on powerpc and x86 dma-mapping.h versions
+ * Copyright (C) 2004 IBM
+ */
+
+#ifndef _ASM_MICROBLAZE_DMA_MAPPING_H
+#define _ASM_MICROBLAZE_DMA_MAPPING_H
+
+/*
+ * See Documentation/PCI/PCI-DMA-mapping.txt and
+ * Documentation/DMA-API.txt for documentation.
+ */
+
+#include <linux/types.h>
+#include <linux/cache.h>
+#include <linux/mm.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-debug.h>
+#include <linux/dma-attrs.h>
+#include <asm/io.h>
+#include <asm-generic/dma-coherent.h>
+
+#define DMA_ERROR_CODE         (~(dma_addr_t)0x0)
+
+#define __dma_alloc_coherent(dev, gfp, size, handle)   NULL
+#define __dma_free_coherent(size, addr)                ((void)0)
+#define __dma_sync(addr, size, rw)             ((void)0)
+
+static inline unsigned long device_to_mask(struct device *dev)
+{
+       if (dev->dma_mask && *dev->dma_mask)
+               return *dev->dma_mask;
+       /* Assume devices without mask can take 32 bit addresses */
+       return 0xfffffffful;
+}
+
+extern struct dma_map_ops *dma_ops;
+
+/*
+ * Available generic sets of operations
+ */
+extern struct dma_map_ops dma_direct_ops;
+
+static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+{
+       /* We don't handle the NULL dev case for ISA for now. We could
+        * do it via an out of line call but it is not needed for now. The
+        * only ISA DMA device we support is the floppy and we have a hack
+        * in the floppy driver directly to get a device for us.
+        */
+       if (unlikely(!dev) || !dev->archdata.dma_ops)
+               return NULL;
+
+       return dev->archdata.dma_ops;
+}
+
+static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops)
+{
+       dev->archdata.dma_ops = ops;
+}
+
+static inline int dma_supported(struct device *dev, u64 mask)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+
+       if (unlikely(!ops))
+               return 0;
+       if (!ops->dma_supported)
+               return 1;
+       return ops->dma_supported(dev, mask);
+}
+
+#ifdef CONFIG_PCI
+/* We have our own implementation of pci_set_dma_mask() */
+#define HAVE_ARCH_PCI_SET_DMA_MASK
+
+#endif
+
+static inline int dma_set_mask(struct device *dev, u64 dma_mask)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+
+       if (unlikely(ops == NULL))
+               return -EIO;
+       if (ops->set_dma_mask)
+               return ops->set_dma_mask(dev, dma_mask);
+       if (!dev->dma_mask || !dma_supported(dev, dma_mask))
+               return -EIO;
+       *dev->dma_mask = dma_mask;
+       return 0;
+}
+
+#include <asm-generic/dma-mapping-common.h>
+
+static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+       if (ops->mapping_error)
+               return ops->mapping_error(dev, dma_addr);
+
+       return (dma_addr == DMA_ERROR_CODE);
+}
+
+#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
+#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+#define dma_is_consistent(d, h)        (1)
+
+static inline void *dma_alloc_coherent(struct device *dev, size_t size,
+                                       dma_addr_t *dma_handle, gfp_t flag)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+       void *memory;
+
+       BUG_ON(!ops);
+
+       memory = ops->alloc_coherent(dev, size, dma_handle, flag);
+
+       debug_dma_alloc_coherent(dev, size, *dma_handle, memory);
+       return memory;
+}
+
+static inline void dma_free_coherent(struct device *dev, size_t size,
+                                    void *cpu_addr, dma_addr_t dma_handle)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
+
+       BUG_ON(!ops);
+       debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
+       ops->free_coherent(dev, size, cpu_addr, dma_handle);
+}
+
+static inline int dma_get_cache_alignment(void)
+{
+       return L1_CACHE_BYTES;
+}
+
+static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+               enum dma_data_direction direction)
+{
+       BUG_ON(direction == DMA_NONE);
+       __dma_sync(vaddr, size, (int)direction);
+}
+
+#endif /* _ASM_MICROBLAZE_DMA_MAPPING_H */
index 267c7c7..32d621a 100644 (file)
 #include <asm/page.h>
 #include <linux/types.h>
 #include <linux/mm.h>          /* Get struct page {...} */
+#include <asm-generic/iomap.h>
 
+#ifndef CONFIG_PCI
+#define _IO_BASE       0
+#define _ISA_MEM_BASE  0
+#define PCI_DRAM_OFFSET        0
+#else
+#define _IO_BASE       isa_io_base
+#define _ISA_MEM_BASE  isa_mem_base
+#define PCI_DRAM_OFFSET        pci_dram_offset
+#endif
+
+extern unsigned long isa_io_base;
+extern unsigned long pci_io_base;
+extern unsigned long pci_dram_offset;
+
+extern resource_size_t isa_mem_base;
 
 #define IO_SPACE_LIMIT (0xFFFFFFFF)
 
@@ -124,9 +140,6 @@ static inline void writel(unsigned int v, volatile void __iomem *addr)
 #define virt_to_phys(addr)     ((unsigned long)__virt_to_phys(addr))
 #define virt_to_bus(addr)      ((unsigned long)__virt_to_phys(addr))
 
-#define __page_address(page) \
-               (PAGE_OFFSET + (((page) - mem_map) << PAGE_SHIFT))
-#define page_to_phys(page)     virt_to_phys((void *)__page_address(page))
 #define page_to_bus(page)      (page_to_phys(page))
 #define bus_to_virt(addr)      (phys_to_virt(addr))
 
@@ -227,15 +240,7 @@ static inline void __iomem *__ioremap(phys_addr_t address, unsigned long size,
 #define out_8(a, v) __raw_writeb((v), (a))
 #define in_8(a) __raw_readb(a)
 
-/* FIXME */
-static inline void __iomem *ioport_map(unsigned long port, unsigned int len)
-{
-       return (void __iomem *) (port);
-}
-
-static inline void ioport_unmap(void __iomem *addr)
-{
-       /* Nothing to do */
-}
+#define ioport_map(port, nr)   ((void __iomem *)(port))
+#define ioport_unmap(addr)
 
 #endif /* _ASM_MICROBLAZE_IO_H */
index 90f0505..31a35c3 100644 (file)
 
 #include <linux/interrupt.h>
 
+/* This type is the placeholder for a hardware interrupt number. It has to
+ * be big enough to enclose whatever representation is used by a given
+ * platform.
+ */
+typedef unsigned long irq_hw_number_t;
+
 extern unsigned int nr_irq;
 
 #define NO_IRQ (-1)
@@ -21,7 +27,8 @@ extern unsigned int nr_irq;
 struct pt_regs;
 extern void do_IRQ(struct pt_regs *regs);
 
-/* irq_of_parse_and_map - Parse and Map an interrupt into linux virq space
+/**
+ * irq_of_parse_and_map - Parse and Map an interrupt into linux virq space
  * @device: Device node of the device whose interrupt is to be mapped
  * @index: Index of the interrupt to map
  *
@@ -40,4 +47,32 @@ static inline void irq_dispose_mapping(unsigned int virq)
        return;
 }
 
+struct irq_host;
+
+/**
+ * irq_create_mapping - Map a hardware interrupt into linux virq space
+ * @host: host owning this hardware interrupt or NULL for default host
+ * @hwirq: hardware irq number in that host space
+ *
+ * Only one mapping per hardware interrupt is permitted. Returns a linux
+ * virq number.
+ * If the sense/trigger is to be specified, set_irq_type() should be called
+ * on the number returned from that call.
+ */
+extern unsigned int irq_create_mapping(struct irq_host *host,
+                                       irq_hw_number_t hwirq);
+
+/**
+ * irq_create_of_mapping - Map a hardware interrupt into linux virq space
+ * @controller: Device node of the interrupt controller
+ * @inspec: Interrupt specifier from the device-tree
+ * @intsize: Size of the interrupt specifier from the device-tree
+ *
+ * This function is identical to irq_create_mapping except that it takes
+ * as input informations straight from the device-tree (typically the results
+ * of the of_irq_map_*() functions.
+ */
+extern unsigned int irq_create_of_mapping(struct device_node *controller,
+                                       u32 *intspec, unsigned int intsize);
+
 #endif /* _ASM_MICROBLAZE_IRQ_H */
index 9b66c0f..2dd1d04 100644 (file)
@@ -62,12 +62,6 @@ extern unsigned int __page_offset;
 #define PAGE_OFFSET    CONFIG_KERNEL_START
 
 /*
- * MAP_NR -- given an address, calculate the index of the page struct which
- * points to the address's page.
- */
-#define MAP_NR(addr) (((unsigned long)(addr) - PAGE_OFFSET) >> PAGE_SHIFT)
-
-/*
  * The basic type of a PTE - 32 bit physical addressing.
  */
 typedef unsigned long pte_basic_t;
@@ -154,7 +148,11 @@ extern int page_is_ram(unsigned long pfn);
 # define pfn_to_virt(pfn)      __va(pfn_to_phys((pfn)))
 
 #  ifdef CONFIG_MMU
-#  define virt_to_page(kaddr)  (mem_map +  MAP_NR(kaddr))
+
+#  define virt_to_page(kaddr)  (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT))
+#  define page_to_virt(page)   __va(page_to_pfn(page) << PAGE_SHIFT)
+#  define page_to_phys(page)     (page_to_pfn(page) << PAGE_SHIFT)
+
 #  else /* CONFIG_MMU */
 #  define virt_to_page(vaddr)  (pfn_to_page(virt_to_pfn(vaddr)))
 #  define page_to_virt(page)   (pfn_to_virt(page_to_pfn(page)))
index 7ad28f6..0c77cda 100644 (file)
@@ -1 +1,196 @@
+#ifndef _ASM_MICROBLAZE_PCI_BRIDGE_H
+#define _ASM_MICROBLAZE_PCI_BRIDGE_H
+#ifdef __KERNEL__
+/*
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
 #include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/ioport.h>
+
+struct device_node;
+
+enum {
+       /* Force re-assigning all resources (ignore firmware
+        * setup completely)
+        */
+       PCI_REASSIGN_ALL_RSRC   = 0x00000001,
+
+       /* Re-assign all bus numbers */
+       PCI_REASSIGN_ALL_BUS    = 0x00000002,
+
+       /* Do not try to assign, just use existing setup */
+       PCI_PROBE_ONLY          = 0x00000004,
+
+       /* Don't bother with ISA alignment unless the bridge has
+        * ISA forwarding enabled
+        */
+       PCI_CAN_SKIP_ISA_ALIGN  = 0x00000008,
+
+       /* Enable domain numbers in /proc */
+       PCI_ENABLE_PROC_DOMAINS = 0x00000010,
+       /* ... except for domain 0 */
+       PCI_COMPAT_DOMAIN_0             = 0x00000020,
+};
+
+/*
+ * Structure of a PCI controller (host bridge)
+ */
+struct pci_controller {
+       struct pci_bus *bus;
+       char is_dynamic;
+       struct device_node *dn;
+       struct list_head list_node;
+       struct device *parent;
+
+       int first_busno;
+       int last_busno;
+
+       int self_busno;
+
+       void __iomem *io_base_virt;
+       resource_size_t io_base_phys;
+
+       resource_size_t pci_io_size;
+
+       /* Some machines (PReP) have a non 1:1 mapping of
+        * the PCI memory space in the CPU bus space
+        */
+       resource_size_t pci_mem_offset;
+
+       /* Some machines have a special region to forward the ISA
+        * "memory" cycles such as VGA memory regions. Left to 0
+        * if unsupported
+        */
+       resource_size_t isa_mem_phys;
+       resource_size_t isa_mem_size;
+
+       struct pci_ops *ops;
+       unsigned int __iomem *cfg_addr;
+       void __iomem *cfg_data;
+
+       /*
+        * Used for variants of PCI indirect handling and possible quirks:
+        *  SET_CFG_TYPE - used on 4xx or any PHB that does explicit type0/1
+        *  EXT_REG - provides access to PCI-e extended registers
+        *  SURPRESS_PRIMARY_BUS - we surpress the setting of PCI_PRIMARY_BUS
+        *   on Freescale PCI-e controllers since they used the PCI_PRIMARY_BUS
+        *   to determine which bus number to match on when generating type0
+        *   config cycles
+        *  NO_PCIE_LINK - the Freescale PCI-e controllers have issues with
+        *   hanging if we don't have link and try to do config cycles to
+        *   anything but the PHB.  Only allow talking to the PHB if this is
+        *   set.
+        *  BIG_ENDIAN - cfg_addr is a big endian register
+        *  BROKEN_MRM - the 440EPx/GRx chips have an errata that causes hangs
+        *   on the PLB4.  Effectively disable MRM commands by setting this.
+        */
+#define INDIRECT_TYPE_SET_CFG_TYPE             0x00000001
+#define INDIRECT_TYPE_EXT_REG          0x00000002
+#define INDIRECT_TYPE_SURPRESS_PRIMARY_BUS     0x00000004
+#define INDIRECT_TYPE_NO_PCIE_LINK             0x00000008
+#define INDIRECT_TYPE_BIG_ENDIAN               0x00000010
+#define INDIRECT_TYPE_BROKEN_MRM               0x00000020
+       u32 indirect_type;
+
+       /* Currently, we limit ourselves to 1 IO range and 3 mem
+        * ranges since the common pci_bus structure can't handle more
+        */
+       struct resource io_resource;
+       struct resource mem_resources[3];
+       int global_number;      /* PCI domain number */
+};
+
+static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
+{
+       return bus->sysdata;
+}
+
+static inline int isa_vaddr_is_ioport(void __iomem *address)
+{
+       /* No specific ISA handling on ppc32 at this stage, it
+        * all goes through PCI
+        */
+       return 0;
+}
+
+/* These are used for config access before all the PCI probing
+   has been done. */
+extern int early_read_config_byte(struct pci_controller *hose, int bus,
+                       int dev_fn, int where, u8 *val);
+extern int early_read_config_word(struct pci_controller *hose, int bus,
+                       int dev_fn, int where, u16 *val);
+extern int early_read_config_dword(struct pci_controller *hose, int bus,
+                       int dev_fn, int where, u32 *val);
+extern int early_write_config_byte(struct pci_controller *hose, int bus,
+                       int dev_fn, int where, u8 val);
+extern int early_write_config_word(struct pci_controller *hose, int bus,
+                       int dev_fn, int where, u16 val);
+extern int early_write_config_dword(struct pci_controller *hose, int bus,
+                       int dev_fn, int where, u32 val);
+
+extern int early_find_capability(struct pci_controller *hose, int bus,
+                                int dev_fn, int cap);
+
+extern void setup_indirect_pci(struct pci_controller *hose,
+                              resource_size_t cfg_addr,
+                              resource_size_t cfg_data, u32 flags);
+
+/* Get the PCI host controller for an OF device */
+extern struct pci_controller *pci_find_hose_for_OF_device(
+                       struct device_node *node);
+
+/* Fill up host controller resources from the OF node */
+extern void pci_process_bridge_OF_ranges(struct pci_controller *hose,
+                       struct device_node *dev, int primary);
+
+/* Allocate & free a PCI host bridge structure */
+extern struct pci_controller *pcibios_alloc_controller(struct device_node *dev);
+extern void pcibios_free_controller(struct pci_controller *phb);
+extern void pcibios_setup_phb_resources(struct pci_controller *hose);
+
+#ifdef CONFIG_PCI
+extern unsigned int pci_flags;
+
+static inline void pci_set_flags(int flags)
+{
+       pci_flags = flags;
+}
+
+static inline void pci_add_flags(int flags)
+{
+       pci_flags |= flags;
+}
+
+static inline int pci_has_flag(int flag)
+{
+       return pci_flags & flag;
+}
+
+extern struct list_head hose_list;
+
+extern unsigned long pci_address_to_pio(phys_addr_t address);
+extern int pcibios_vaddr_is_ioport(void __iomem *address);
+#else
+static inline unsigned long pci_address_to_pio(phys_addr_t address)
+{
+       return (unsigned long)-1;
+}
+static inline int pcibios_vaddr_is_ioport(void __iomem *address)
+{
+       return 0;
+}
+
+static inline void pci_set_flags(int flags) { }
+static inline void pci_add_flags(int flags) { }
+static inline int pci_has_flag(int flag)
+{
+       return 0;
+}
+#endif /* CONFIG_PCI */
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_MICROBLAZE_PCI_BRIDGE_H */
index 9f0df5f..bdd65aa 100644 (file)
@@ -1 +1,177 @@
-#include <asm-generic/pci.h>
+/*
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Based on powerpc version
+ */
+
+#ifndef __ASM_MICROBLAZE_PCI_H
+#define __ASM_MICROBLAZE_PCI_H
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+
+#include <asm/scatterlist.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+#define PCIBIOS_MIN_IO         0x1000
+#define PCIBIOS_MIN_MEM                0x10000000
+
+struct pci_dev;
+
+/* Values for the `which' argument to sys_pciconfig_iobase syscall.  */
+#define IOBASE_BRIDGE_NUMBER   0
+#define IOBASE_MEMORY          1
+#define IOBASE_IO              2
+#define IOBASE_ISA_IO          3
+#define IOBASE_ISA_MEM         4
+
+#define pcibios_scan_all_fns(a, b)     0
+
+/*
+ * Set this to 1 if you want the kernel to re-assign all PCI
+ * bus numbers (don't do that on ppc64 yet !)
+ */
+#define pcibios_assign_all_busses() \
+       (pci_has_flag(PCI_REASSIGN_ALL_BUS))
+
+static inline void pcibios_set_master(struct pci_dev *dev)
+{
+       /* No special bus mastering setup handling */
+}
+
+static inline void pcibios_penalize_isa_irq(int irq, int active)
+{
+       /* We don't do dynamic PCI IRQ allocation */
+}
+
+#ifdef CONFIG_PCI
+extern void set_pci_dma_ops(struct dma_map_ops *dma_ops);
+extern struct dma_map_ops *get_pci_dma_ops(void);
+#else  /* CONFIG_PCI */
+#define set_pci_dma_ops(d)
+#define get_pci_dma_ops()      NULL
+#endif
+
+#ifdef CONFIG_PCI
+static inline void pci_dma_burst_advice(struct pci_dev *pdev,
+                                       enum pci_dma_burst_strategy *strat,
+                                       unsigned long *strategy_parameter)
+{
+       *strat = PCI_DMA_BURST_INFINITY;
+       *strategy_parameter = ~0UL;
+}
+#endif
+
+extern int pci_domain_nr(struct pci_bus *bus);
+
+/* Decide whether to display the domain number in /proc */
+extern int pci_proc_domain(struct pci_bus *bus);
+
+struct vm_area_struct;
+/* Map a range of PCI memory or I/O space for a device into user space */
+int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma,
+                       enum pci_mmap_state mmap_state, int write_combine);
+
+/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */
+#define HAVE_PCI_MMAP  1
+
+extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val,
+                          size_t count);
+extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val,
+                          size_t count);
+extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
+                                     struct vm_area_struct *vma,
+                                     enum pci_mmap_state mmap_state);
+
+#define HAVE_PCI_LEGACY        1
+
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define pci_unmap_addr(PTR, ADDR_NAME)         (0)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)        do { } while (0)
+#define pci_unmap_len(PTR, LEN_NAME)           (0)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)  do { } while (0)
+
+/* The PCI address space does equal the physical memory
+ * address space (no IOMMU).  The IDE and SCSI device layers use
+ * this boolean for bounce buffer decisions.
+ */
+#define PCI_DMA_BUS_IS_PHYS     (1)
+
+extern void pcibios_resource_to_bus(struct pci_dev *dev,
+                       struct pci_bus_region *region,
+                       struct resource *res);
+
+extern void pcibios_bus_to_resource(struct pci_dev *dev,
+                       struct resource *res,
+                       struct pci_bus_region *region);
+
+static inline struct resource *pcibios_select_root(struct pci_dev *pdev,
+                       struct resource *res)
+{
+       struct resource *root = NULL;
+
+       if (res->flags & IORESOURCE_IO)
+               root = &ioport_resource;
+       if (res->flags & IORESOURCE_MEM)
+               root = &iomem_resource;
+
+       return root;
+}
+
+extern void pcibios_claim_one_bus(struct pci_bus *b);
+
+extern void pcibios_finish_adding_to_bus(struct pci_bus *bus);
+
+extern void pcibios_resource_survey(void);
+
+extern struct pci_controller *init_phb_dynamic(struct device_node *dn);
+extern int remove_phb_dynamic(struct pci_controller *phb);
+
+extern struct pci_dev *of_create_pci_dev(struct device_node *node,
+                                       struct pci_bus *bus, int devfn);
+
+extern void of_scan_pci_bridge(struct device_node *node,
+                               struct pci_dev *dev);
+
+extern void of_scan_bus(struct device_node *node, struct pci_bus *bus);
+extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus);
+
+extern int pci_read_irq_line(struct pci_dev *dev);
+
+extern int pci_bus_find_capability(struct pci_bus *bus,
+                                               unsigned int devfn, int cap);
+
+struct file;
+extern pgprot_t        pci_phys_mem_access_prot(struct file *file,
+                                        unsigned long pfn,
+                                        unsigned long size,
+                                        pgprot_t prot);
+
+#define HAVE_ARCH_PCI_RESOURCE_TO_USER
+extern void pci_resource_to_user(const struct pci_dev *dev, int bar,
+                                const struct resource *rsrc,
+                                resource_size_t *start, resource_size_t *end);
+
+extern void pcibios_setup_bus_devices(struct pci_bus *bus);
+extern void pcibios_setup_bus_self(struct pci_bus *bus);
+
+/* This part of code was originaly in xilinx-pci.h */
+#ifdef CONFIG_PCI_XILINX
+extern void __init xilinx_pci_init(void);
+#else
+static inline void __init xilinx_pci_init(void) { return; }
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_MICROBLAZE_PCI_H */
index 7547f50..f44b0d6 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/io.h>
 #include <asm/page.h>
 #include <asm/cache.h>
+#include <asm/pgtable.h>
 
 #define PGDIR_ORDER    0
 
@@ -111,7 +112,6 @@ static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
                unsigned long address)
 {
        pte_t *pte;
-       extern int mem_init_done;
        extern void *early_get_page(void);
        if (mem_init_done) {
                pte = (pte_t *)__get_free_page(GFP_KERNEL |
index cc3a4df..dd2bb60 100644 (file)
 #define io_remap_pfn_range(vma, vaddr, pfn, size, prot)                \
                remap_pfn_range(vma, vaddr, pfn, size, prot)
 
+#ifndef __ASSEMBLY__
+extern int mem_init_done;
+#endif
+
 #ifndef CONFIG_MMU
 
 #define pgd_present(pgd)       (1) /* pages are always present on non MMU */
@@ -51,6 +55,8 @@ static inline int pte_file(pte_t pte) { return 0; }
 
 #define arch_enter_lazy_cpu_mode()     do {} while (0)
 
+#define pgprot_noncached_wc(prot)      prot
+
 #else /* CONFIG_MMU */
 
 #include <asm-generic/4level-fixup.h>
@@ -68,7 +74,6 @@ static inline int pte_file(pte_t pte) { return 0; }
 
 extern unsigned long va_to_phys(unsigned long address);
 extern pte_t *va_to_pte(unsigned long address);
-extern unsigned long ioremap_bot, ioremap_base;
 
 /*
  * The following only work if pte_present() is true.
@@ -85,11 +90,25 @@ static inline pte_t pte_mkspecial(pte_t pte)        { return pte; }
 #define VMALLOC_START  (CONFIG_KERNEL_START + \
                                max(32 * 1024 * 1024UL, memory_size))
 #define VMALLOC_END    ioremap_bot
-#define VMALLOC_VMADDR(x) ((unsigned long)(x))
 
 #endif /* __ASSEMBLY__ */
 
 /*
+ * Macro to mark a page protection value as "uncacheable".
+ */
+
+#define _PAGE_CACHE_CTL        (_PAGE_GUARDED | _PAGE_NO_CACHE | \
+                                                       _PAGE_WRITETHRU)
+
+#define pgprot_noncached(prot) \
+                       (__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \
+                                       _PAGE_NO_CACHE | _PAGE_GUARDED))
+
+#define pgprot_noncached_wc(prot) \
+                        (__pgprot((pgprot_val(prot) & ~_PAGE_CACHE_CTL) | \
+                                                       _PAGE_NO_CACHE))
+
+/*
  * The MicroBlaze MMU is identical to the PPC-40x MMU, and uses a hash
  * table containing PTEs, together with a set of 16 segment registers, to
  * define the virtual to physical address mapping.
@@ -397,7 +416,7 @@ static inline unsigned long pte_update(pte_t *p, unsigned long clr,
        mts     rmsr, %2\n\
        nop"
        : "=&r" (old), "=&r" (tmp), "=&r" (msr), "=m" (*p)
-       : "r" ((unsigned long)(p+1) - 4), "r" (clr), "r" (set), "m" (*p)
+       : "r" ((unsigned long)(p + 1) - 4), "r" (clr), "r" (set), "m" (*p)
        : "cc");
 
        return old;
@@ -566,18 +585,11 @@ void mapin_ram(void);
 int map_page(unsigned long va, phys_addr_t pa, int flags);
 
 extern int mem_init_done;
-extern unsigned long ioremap_base;
-extern unsigned long ioremap_bot;
 
 asmlinkage void __init mmu_init(void);
 
 void __init *early_get_page(void);
 
-void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle);
-void consistent_free(void *vaddr);
-void consistent_sync(void *vaddr, size_t size, int direction);
-void consistent_sync_page(struct page *page, unsigned long offset,
-       size_t size, int direction);
 #endif /* __ASSEMBLY__ */
 #endif /* __KERNEL__ */
 
@@ -586,6 +598,14 @@ void consistent_sync_page(struct page *page, unsigned long offset,
 #ifndef __ASSEMBLY__
 #include <asm-generic/pgtable.h>
 
+extern unsigned long ioremap_bot, ioremap_base;
+
+void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle);
+void consistent_free(void *vaddr);
+void consistent_sync(void *vaddr, size_t size, int direction);
+void consistent_sync_page(struct page *page, unsigned long offset,
+       size_t size, int direction);
+
 void setup_memory(void);
 #endif /* __ASSEMBLY__ */
 
index 03f45a9..e7d67a3 100644 (file)
 /* Other Prototypes */
 extern int early_uartlite_console(void);
 
+#ifdef CONFIG_PCI
+/*
+ * PCI <-> OF matching functions
+ * (XXX should these be here?)
+ */
+struct pci_bus;
+struct pci_dev;
+extern int pci_device_from_OF_node(struct device_node *node,
+                                       u8 *bus, u8 *devfn);
+extern struct device_node *pci_busdev_to_OF_node(struct pci_bus *bus,
+                                                       int devfn);
+extern struct device_node *pci_device_to_OF_node(struct pci_dev *dev);
+extern void pci_create_OF_bus_map(void);
+#endif
+
 /*
  * OF address retreival & translation
  */
index 1579706..59efb3f 100644 (file)
@@ -87,6 +87,9 @@ void free_initmem(void);
 extern char *klimit;
 extern void ret_from_fork(void);
 
+extern void *alloc_maybe_bootmem(size_t size, gfp_t mask);
+extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask);
+
 #ifdef CONFIG_DEBUG_FS
 extern struct dentry *of_debugfs_root;
 #endif
index 10ec70c..bcb8b41 100644 (file)
@@ -23,7 +23,7 @@
 extern void _tlbie(unsigned long address);
 extern void _tlbia(void);
 
-#define __tlbia()      _tlbia()
+#define __tlbia()      { preempt_disable(); _tlbia(); preempt_enable(); }
 
 static inline void local_flush_tlb_all(void)
        { __tlbia(); }
index b07594e..e51bc15 100644 (file)
@@ -14,7 +14,7 @@ endif
 
 extra-y := head.o vmlinux.lds
 
-obj-y += exceptions.o \
+obj-y += dma.o exceptions.o \
        hw_exception_handler.o init_task.o intc.o irq.o of_device.o \
        of_platform.o process.o prom.o prom_parse.o ptrace.o \
        setup.o signal.o sys_microblaze.o timer.o traps.o reset.o
index 7bc7b68..0071260 100644 (file)
@@ -90,6 +90,7 @@ int main(int argc, char *argv[])
        DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
        DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
        DEFINE(TI_CPU_CONTEXT, offsetof(struct thread_info, cpu_context));
+       DEFINE(TI_PREEMPT_COUNT, offsetof(struct thread_info, preempt_count));
        BLANK();
 
        /* struct cpu_context */
index 2a56bcc..f04d8a8 100644 (file)
 #include <asm/cpuinfo.h>
 #include <asm/pvr.h>
 
-static inline void __invalidate_flush_icache(unsigned int addr)
-{
-       __asm__ __volatile__ ("wic      %0, r0;"        \
-                                       : : "r" (addr));
-}
-
-static inline void __flush_dcache(unsigned int addr)
-{
-       __asm__ __volatile__ ("wdc.flush        %0, r0;"        \
-                                       : : "r" (addr));
-}
-
-static inline void __invalidate_dcache(unsigned int baseaddr,
-                                               unsigned int offset)
-{
-       __asm__ __volatile__ ("wdc.clear        %0, %1;"        \
-                                       : : "r" (baseaddr), "r" (offset));
-}
-
 static inline void __enable_icache_msr(void)
 {
        __asm__ __volatile__ (" msrset  r0, %0;         \
@@ -148,9 +129,9 @@ do {                                                                        \
        int step = -line_length;                                        \
        BUG_ON(step >= 0);                                              \
                                                                        \
-       __asm__ __volatile__ (" 1:      " #op " r0, %0;                 \
-                                       bgtid   %0, 1b;                 \
-                                       addk    %0, %0, %1;             \
+       __asm__ __volatile__ (" 1:      " #op " r0, %0;                 \
+                                       bgtid   %0, 1b;                 \
+                                       addk    %0, %0, %1;             \
                                        " : : "r" (len), "r" (step)     \
                                        : "memory");                    \
 } while (0);
@@ -162,9 +143,9 @@ do {                                                                        \
        int count = end - start;                                        \
        BUG_ON(count <= 0);                                             \
                                                                        \
-       __asm__ __volatile__ (" 1:      " #op " %0, %1;                 \
-                                       bgtid   %1, 1b;                 \
-                                       addk    %1, %1, %2;             \
+       __asm__ __volatile__ (" 1:      " #op " %0, %1;                 \
+                                       bgtid   %1, 1b;                 \
+                                       addk    %1, %1, %2;             \
                                        " : : "r" (start), "r" (count), \
                                        "r" (step) : "memory");         \
 } while (0);
@@ -175,7 +156,7 @@ do {                                                                        \
        int volatile temp;                                              \
        BUG_ON(end - start <= 0);                                       \
                                                                        \
-       __asm__ __volatile__ (" 1:      " #op " %1, r0;                 \
+       __asm__ __volatile__ (" 1:      " #op " %1, r0;                 \
                                        cmpu    %0, %1, %2;             \
                                        bgtid   %0, 1b;                 \
                                        addk    %1, %1, %3;             \
@@ -183,10 +164,14 @@ do {                                                                      \
                                        "r" (line_length) : "memory");  \
 } while (0);
 
+#define ASM_LOOP
+
 static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
@@ -196,8 +181,13 @@ static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
        local_irq_save(flags);
        __disable_icache_msr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
-
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wic      %0, r0;"        \
+                               : : "r" (i));
+#endif
        __enable_icache_msr();
        local_irq_restore(flags);
 }
@@ -206,7 +196,9 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,
                                unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
@@ -216,7 +208,13 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,
        local_irq_save(flags);
        __disable_icache_nomsr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wic      %0, r0;"        \
+                               : : "r" (i));
+#endif
 
        __enable_icache_nomsr();
        local_irq_restore(flags);
@@ -225,25 +223,41 @@ static void __flush_icache_range_nomsr_irq(unsigned long start,
 static void __flush_icache_range_noirq(unsigned long start,
                                unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
        CACHE_LOOP_LIMITS(start, end,
                        cpuinfo.icache_line_length, cpuinfo.icache_size);
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wic      %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 static void __flush_icache_all_msr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_icache_msr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
-
+#else
+       for (i = 0; i < cpuinfo.icache_size;
+                i += cpuinfo.icache_line_length)
+                       __asm__ __volatile__ ("wic      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_icache_msr();
        local_irq_restore(flags);
 }
@@ -251,35 +265,59 @@ static void __flush_icache_all_msr_irq(void)
 static void __flush_icache_all_nomsr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_icache_nomsr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
-
+#else
+       for (i = 0; i < cpuinfo.icache_size;
+                i += cpuinfo.icache_line_length)
+                       __asm__ __volatile__ ("wic      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_icache_nomsr();
        local_irq_restore(flags);
 }
 
 static void __flush_icache_all_noirq(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);
+#else
+       for (i = 0; i < cpuinfo.icache_size;
+                i += cpuinfo.icache_line_length)
+                       __asm__ __volatile__ ("wic      %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_all_msr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_dcache_msr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
-
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_dcache_msr();
        local_irq_restore(flags);
 }
@@ -287,60 +325,107 @@ static void __invalidate_dcache_all_msr_irq(void)
 static void __invalidate_dcache_all_nomsr_irq(void)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
 
        local_irq_save(flags);
        __disable_dcache_nomsr();
-
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);
-
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc      %0, r0;" \
+                                       : : "r" (i));
+#endif
        __enable_dcache_nomsr();
        local_irq_restore(flags);
 }
 
 static void __invalidate_dcache_all_noirq_wt(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc)
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc      %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
 /* FIXME this is weird - should be only wdc but not work
  * MS: I am getting bus errors and other weird things */
 static void __invalidate_dcache_all_wb(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
                                        wdc.clear)
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc.clear        %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_range_wb(unsigned long start,
                                                unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
        CACHE_LOOP_LIMITS(start, end,
                        cpuinfo.dcache_line_length, cpuinfo.dcache_size);
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wdc.clear        %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_range_nomsr_wt(unsigned long start,
                                                        unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
        CACHE_LOOP_LIMITS(start, end,
                        cpuinfo.dcache_line_length, cpuinfo.dcache_size);
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wdc      %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
                                                        unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
        CACHE_LOOP_LIMITS(start, end,
@@ -349,7 +434,13 @@ static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,
        local_irq_save(flags);
        __disable_dcache_msr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wdc      %0, r0;"        \
+                               : : "r" (i));
+#endif
 
        __enable_dcache_msr();
        local_irq_restore(flags);
@@ -359,7 +450,9 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
                                                        unsigned long end)
 {
        unsigned long flags;
-
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
@@ -369,7 +462,13 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
        local_irq_save(flags);
        __disable_dcache_nomsr();
 
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wdc      %0, r0;"        \
+                               : : "r" (i));
+#endif
 
        __enable_dcache_nomsr();
        local_irq_restore(flags);
@@ -377,19 +476,38 @@ static void __invalidate_dcache_range_nomsr_irq(unsigned long start,
 
 static void __flush_dcache_all_wb(void)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s\n", __func__);
+#ifdef ASM_LOOP
        CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
                                wdc.flush);
+#else
+       for (i = 0; i < cpuinfo.dcache_size;
+                i += cpuinfo.dcache_line_length)
+                       __asm__ __volatile__ ("wdc.flush        %0, r0;" \
+                                       : : "r" (i));
+#endif
 }
 
 static void __flush_dcache_range_wb(unsigned long start, unsigned long end)
 {
+#ifndef ASM_LOOP
+       int i;
+#endif
        pr_debug("%s: start 0x%x, end 0x%x\n", __func__,
                                (unsigned int)start, (unsigned int) end);
 
        CACHE_LOOP_LIMITS(start, end,
                        cpuinfo.dcache_line_length, cpuinfo.dcache_size);
+#ifdef ASM_LOOP
        CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);
+#else
+       for (i = start; i < end; i += cpuinfo.icache_line_length)
+               __asm__ __volatile__ ("wdc.flush        %0, r0;"        \
+                               : : "r" (i));
+#endif
 }
 
 /* struct for wb caches and for wt caches */
@@ -493,7 +611,7 @@ const struct scache wt_nomsr_noirq = {
 #define CPUVER_7_20_A  0x0c
 #define CPUVER_7_20_D  0x0f
 
-#define INFO(s)        printk(KERN_INFO "cache: " s " \n");
+#define INFO(s)        printk(KERN_INFO "cache: " s "\n");
 
 void microblaze_cache_init(void)
 {
@@ -532,4 +650,9 @@ void microblaze_cache_init(void)
                        }
                }
        }
+       invalidate_dcache();
+       enable_dcache();
+
+       invalidate_icache();
+       enable_icache();
 }
diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c
new file mode 100644 (file)
index 0000000..b108497
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2009-2010 PetaLogix
+ * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corporation
+ *
+ * Provide default implementations of the DMA mapping callbacks for
+ * directly mapped busses.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-debug.h>
+#include <asm/bug.h>
+#include <asm/cacheflush.h>
+
+/*
+ * Generic direct DMA implementation
+ *
+ * This implementation supports a per-device offset that can be applied if
+ * the address at which memory is visible to devices is not 0. Platform code
+ * can set archdata.dma_data to an unsigned long holding the offset. By
+ * default the offset is PCI_DRAM_OFFSET.
+ */
+static inline void __dma_sync_page(unsigned long paddr, unsigned long offset,
+                               size_t size, enum dma_data_direction direction)
+{
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               flush_dcache_range(paddr + offset, paddr + offset + size);
+               break;
+       case DMA_FROM_DEVICE:
+               invalidate_dcache_range(paddr + offset, paddr + offset + size);
+               break;
+       default:
+               BUG();
+       }
+}
+
+static unsigned long get_dma_direct_offset(struct device *dev)
+{
+       if (dev)
+               return (unsigned long)dev->archdata.dma_data;
+
+       return PCI_DRAM_OFFSET; /* FIXME Not sure if is correct */
+}
+
+#define NOT_COHERENT_CACHE
+
+static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
+                               dma_addr_t *dma_handle, gfp_t flag)
+{
+#ifdef NOT_COHERENT_CACHE
+       return consistent_alloc(flag, size, dma_handle);
+#else
+       void *ret;
+       struct page *page;
+       int node = dev_to_node(dev);
+
+       /* ignore region specifiers */
+       flag  &= ~(__GFP_HIGHMEM);
+
+       page = alloc_pages_node(node, flag, get_order(size));
+       if (page == NULL)
+               return NULL;
+       ret = page_address(page);
+       memset(ret, 0, size);
+       *dma_handle = virt_to_phys(ret) + get_dma_direct_offset(dev);
+
+       return ret;
+#endif
+}
+
+static void dma_direct_free_coherent(struct device *dev, size_t size,
+                             void *vaddr, dma_addr_t dma_handle)
+{
+#ifdef NOT_COHERENT_CACHE
+       consistent_free(vaddr);
+#else
+       free_pages((unsigned long)vaddr, get_order(size));
+#endif
+}
+
+static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl,
+                            int nents, enum dma_data_direction direction,
+                            struct dma_attrs *attrs)
+{
+       struct scatterlist *sg;
+       int i;
+
+       /* FIXME this part of code is untested */
+       for_each_sg(sgl, sg, nents, i) {
+               sg->dma_address = sg_phys(sg) + get_dma_direct_offset(dev);
+               sg->dma_length = sg->length;
+               __dma_sync_page(page_to_phys(sg_page(sg)), sg->offset,
+                                                       sg->length, direction);
+       }
+
+       return nents;
+}
+
+static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg,
+                               int nents, enum dma_data_direction direction,
+                               struct dma_attrs *attrs)
+{
+}
+
+static int dma_direct_dma_supported(struct device *dev, u64 mask)
+{
+       return 1;
+}
+
+static inline dma_addr_t dma_direct_map_page(struct device *dev,
+                                            struct page *page,
+                                            unsigned long offset,
+                                            size_t size,
+                                            enum dma_data_direction direction,
+                                            struct dma_attrs *attrs)
+{
+       __dma_sync_page(page_to_phys(page), offset, size, direction);
+       return page_to_phys(page) + offset + get_dma_direct_offset(dev);
+}
+
+static inline void dma_direct_unmap_page(struct device *dev,
+                                        dma_addr_t dma_address,
+                                        size_t size,
+                                        enum dma_data_direction direction,
+                                        struct dma_attrs *attrs)
+{
+/* There is not necessary to do cache cleanup
+ *
+ * phys_to_virt is here because in __dma_sync_page is __virt_to_phys and
+ * dma_address is physical address
+ */
+       __dma_sync_page(dma_address, 0 , size, direction);
+}
+
+struct dma_map_ops dma_direct_ops = {
+       .alloc_coherent = dma_direct_alloc_coherent,
+       .free_coherent  = dma_direct_free_coherent,
+       .map_sg         = dma_direct_map_sg,
+       .unmap_sg       = dma_direct_unmap_sg,
+       .dma_supported  = dma_direct_dma_supported,
+       .map_page       = dma_direct_map_page,
+       .unmap_page     = dma_direct_unmap_page,
+};
+EXPORT_SYMBOL(dma_direct_ops);
+
+/* Number of entries preallocated for DMA-API debugging */
+#define PREALLOC_DMA_DEBUG_ENTRIES (1 << 16)
+
+static int __init dma_init(void)
+{
+       dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
+
+       return 0;
+}
+fs_initcall(dma_init);
index 3bad4ff..c0ede25 100644 (file)
@@ -305,7 +305,7 @@ C_ENTRY(_user_exception):
        swi     r11, r1, PTO+PT_R1;             /* Store user SP.  */
        addi    r11, r0, 1;
        swi     r11, r0, TOPHYS(PER_CPU(KM));   /* Now we're in kernel-mode.  */
-2:     lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
+2:     lwi     CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
        /* Save away the syscall number.  */
        swi     r12, r1, PTO+PT_R0;
        tovirt(r1,r1)
@@ -322,8 +322,7 @@ C_ENTRY(_user_exception):
        rtid    r11, 0
        nop
 3:
-       add     r11, r0, CURRENT_TASK    /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO /* get thread info */
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */
        lwi     r11, r11, TI_FLAGS       /* get flags in thread info */
        andi    r11, r11, _TIF_WORK_SYSCALL_MASK
        beqi    r11, 4f
@@ -382,60 +381,50 @@ C_ENTRY(ret_from_trap):
 /* See if returning to kernel mode, if so, skip resched &c.  */
        bnei    r11, 2f;
 
+       swi     r3, r1, PTO + PT_R3
+       swi     r4, r1, PTO + PT_R4
+
        /* We're returning to user mode, so check for various conditions that
         * trigger rescheduling. */
-       # FIXME: Restructure all these flag checks.
-       add     r11, r0, CURRENT_TASK;  /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       /* FIXME: Restructure all these flag checks. */
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
        lwi     r11, r11, TI_FLAGS;             /* get flags in thread info */
        andi    r11, r11, _TIF_WORK_SYSCALL_MASK
        beqi    r11, 1f
 
-       swi     r3, r1, PTO + PT_R3
-       swi     r4, r1, PTO + PT_R4
        brlid   r15, do_syscall_trace_leave
        addik   r5, r1, PTO + PT_R0
-       lwi     r3, r1, PTO + PT_R3
-       lwi     r4, r1, PTO + PT_R4
 1:
-
        /* We're returning to user mode, so check for various conditions that
         * trigger rescheduling. */
-       /* Get current task ptr into r11 */
-       add     r11, r0, CURRENT_TASK;  /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       /* get thread info from current task */
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;
        lwi     r11, r11, TI_FLAGS;             /* get flags in thread info */
        andi    r11, r11, _TIF_NEED_RESCHED;
        beqi    r11, 5f;
 
-       swi     r3, r1, PTO + PT_R3; /* store syscall result */
-       swi     r4, r1, PTO + PT_R4;
        bralid  r15, schedule;  /* Call scheduler */
        nop;                            /* delay slot */
-       lwi     r3, r1, PTO + PT_R3; /* restore syscall result */
-       lwi     r4, r1, PTO + PT_R4;
 
        /* Maybe handle a signal */
-5:     add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+5:     /* get thread info from current task*/
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;
        lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
        andi    r11, r11, _TIF_SIGPENDING;
        beqi    r11, 1f;                /* Signals to handle, handle them */
 
-       swi     r3, r1, PTO + PT_R3; /* store syscall result */
-       swi     r4, r1, PTO + PT_R4;
        la      r5, r1, PTO;            /* Arg 1: struct pt_regs *regs */
-       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
        addi    r7, r0, 1;              /* Arg 3: int in_syscall */
        bralid  r15, do_signal; /* Handle any signals */
-       nop;
+       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
+
+/* Finally, return to user state.  */
+1:
        lwi     r3, r1, PTO + PT_R3; /* restore syscall result */
        lwi     r4, r1, PTO + PT_R4;
 
-/* Finally, return to user state.  */
-1:     swi     r0, r0, PER_CPU(KM);    /* Now officially in user state. */
-       add     r11, r0, CURRENT_TASK;  /* Get current task ptr into r11 */
-       swi     r11, r0, PER_CPU(CURRENT_SAVE); /* save current */
+       swi     r0, r0, PER_CPU(KM);    /* Now officially in user state. */
+       swi     CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
        VM_OFF;
        tophys(r1,r1);
        RESTORE_REGS;
@@ -565,7 +554,7 @@ C_ENTRY(sys_rt_sigreturn_wrapper):
        swi     r11, r1, PTO+PT_R1; /* Store user SP.  */               \
        addi    r11, r0, 1;                                             \
        swi     r11, r0, TOPHYS(PER_CPU(KM)); /* Now we're in kernel-mode.*/\
-2:     lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\
+2:     lwi     CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));        \
        /* Save away the syscall number.  */                            \
        swi     r0, r1, PTO+PT_R0;                                      \
        tovirt(r1,r1)
@@ -673,9 +662,7 @@ C_ENTRY(ret_from_exc):
 
        /* We're returning to user mode, so check for various conditions that
           trigger rescheduling. */
-       /* Get current task ptr into r11 */
-       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
        lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
        andi    r11, r11, _TIF_NEED_RESCHED;
        beqi    r11, 5f;
@@ -685,8 +672,7 @@ C_ENTRY(ret_from_exc):
        nop;                            /* delay slot */
 
        /* Maybe handle a signal */
-5:     add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+5:     lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
        lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
        andi    r11, r11, _TIF_SIGPENDING;
        beqi    r11, 1f;                /* Signals to handle, handle them */
@@ -705,15 +691,13 @@ C_ENTRY(ret_from_exc):
         * store return registers separately because this macros is use
         * for others exceptions */
        la      r5, r1, PTO;            /* Arg 1: struct pt_regs *regs */
-       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
        addi    r7, r0, 0;              /* Arg 3: int in_syscall */
        bralid  r15, do_signal; /* Handle any signals */
-       nop;
+       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
 
 /* Finally, return to user state.  */
 1:     swi     r0, r0, PER_CPU(KM);    /* Now officially in user state. */
-       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
-       swi     r11, r0, PER_CPU(CURRENT_SAVE); /* save current */
+       swi     CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
        VM_OFF;
        tophys(r1,r1);
 
@@ -802,7 +786,7 @@ C_ENTRY(_interrupt):
        swi     r11, r0, TOPHYS(PER_CPU(KM));
 
 2:
-       lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
+       lwi     CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
        swi     r0, r1, PTO + PT_R0;
        tovirt(r1,r1)
        la      r5, r1, PTO;
@@ -817,8 +801,7 @@ ret_from_irq:
        lwi     r11, r1, PTO + PT_MODE;
        bnei    r11, 2f;
 
-       add     r11, r0, CURRENT_TASK;
-       lwi     r11, r11, TS_THREAD_INFO;
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;
        lwi     r11, r11, TI_FLAGS; /* MS: get flags from thread info */
        andi    r11, r11, _TIF_NEED_RESCHED;
        beqi    r11, 5f
@@ -826,8 +809,7 @@ ret_from_irq:
        nop; /* delay slot */
 
     /* Maybe handle a signal */
-5:     add     r11, r0, CURRENT_TASK;
-       lwi     r11, r11, TS_THREAD_INFO; /* MS: get thread info */
+5:     lwi     r11, CURRENT_TASK, TS_THREAD_INFO; /* MS: get thread info */
        lwi     r11, r11, TI_FLAGS; /* get flags in thread info */
        andi    r11, r11, _TIF_SIGPENDING;
        beqid   r11, no_intr_resched
@@ -842,8 +824,7 @@ no_intr_resched:
     /* Disable interrupts, we are now committed to the state restore */
        disable_irq
        swi     r0, r0, PER_CPU(KM); /* MS: Now officially in user state. */
-       add     r11, r0, CURRENT_TASK;
-       swi     r11, r0, PER_CPU(CURRENT_SAVE);
+       swi     CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE);
        VM_OFF;
        tophys(r1,r1);
        lwi     r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */
@@ -853,7 +834,28 @@ no_intr_resched:
        lwi     r1, r1, PT_R1 - PT_SIZE;
        bri     6f;
 /* MS: Return to kernel state. */
-2:     VM_OFF /* MS: turn off MMU */
+2:
+#ifdef CONFIG_PREEMPT
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;
+       /* MS: get preempt_count from thread info */
+       lwi     r5, r11, TI_PREEMPT_COUNT;
+       bgti    r5, restore;
+
+       lwi     r5, r11, TI_FLAGS;              /* get flags in thread info */
+       andi    r5, r5, _TIF_NEED_RESCHED;
+       beqi    r5, restore /* if zero jump over */
+
+preempt:
+       /* interrupts are off that's why I am calling preempt_chedule_irq */
+       bralid  r15, preempt_schedule_irq
+       nop
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
+       lwi     r5, r11, TI_FLAGS;              /* get flags in thread info */
+       andi    r5, r5, _TIF_NEED_RESCHED;
+       bnei    r5, preempt /* if non zero jump to resched */
+restore:
+#endif
+       VM_OFF /* MS: turn off MMU */
        tophys(r1,r1)
        lwi     r3, r1, PTO + PT_R3; /* MS: restore saved r3, r4 registers */
        lwi     r4, r1, PTO + PT_R4;
@@ -915,7 +917,7 @@ C_ENTRY(_debug_exception):
        swi     r11, r1, PTO+PT_R1; /* Store user SP.  */
        addi    r11, r0, 1;
        swi     r11, r0, TOPHYS(PER_CPU(KM));   /* Now we're in kernel-mode.  */
-2:     lwi     r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */
+2:     lwi     CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));
        /* Save away the syscall number.  */
        swi     r0, r1, PTO+PT_R0;
        tovirt(r1,r1)
@@ -935,8 +937,7 @@ dbtrap_call:        rtbd    r11, 0;
        bnei    r11, 2f;
 
        /* Get current task ptr into r11 */
-       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
        lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
        andi    r11, r11, _TIF_NEED_RESCHED;
        beqi    r11, 5f;
@@ -949,8 +950,7 @@ dbtrap_call:        rtbd    r11, 0;
        /* XXX m68knommu also checks TASK_STATE & TASK_COUNTER here.  */
 
        /* Maybe handle a signal */
-5:     add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
-       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+5:     lwi     r11, CURRENT_TASK, TS_THREAD_INFO;      /* get thread info */
        lwi     r11, r11, TI_FLAGS;     /* get flags in thread info */
        andi    r11, r11, _TIF_SIGPENDING;
        beqi    r11, 1f;                /* Signals to handle, handle them */
@@ -966,16 +966,14 @@ dbtrap_call:      rtbd    r11, 0;
           (in a possibly modified form) after do_signal returns.  */
 
        la      r5, r1, PTO;            /* Arg 1: struct pt_regs *regs */
-       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
        addi  r7, r0, 0;        /* Arg 3: int in_syscall */
        bralid  r15, do_signal; /* Handle any signals */
-       nop;
+       add     r6, r0, r0;             /* Arg 2: sigset_t *oldset */
 
 
 /* Finally, return to user state.  */
 1:     swi     r0, r0, PER_CPU(KM);    /* Now officially in user state. */
-       add     r11, r0, CURRENT_TASK; /* Get current task ptr into r11 */
-       swi     r11, r0, PER_CPU(CURRENT_SAVE); /* save current */
+       swi     CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */
        VM_OFF;
        tophys(r1,r1);
 
@@ -1007,7 +1005,7 @@ DBTRAP_return:            /* Make global symbol for debugging */
 
 ENTRY(_switch_to)
        /* prepare return value */
-       addk    r3, r0, r31
+       addk    r3, r0, CURRENT_TASK
 
        /* save registers in cpu_context */
        /* use r11 and r12, volatile registers, as temp register */
@@ -1051,10 +1049,10 @@ ENTRY(_switch_to)
        nop
        swi     r12, r11, CC_FSR
 
-       /* update r31, the current */
-       lwi     r31, r6, TI_TASK/* give me pointer to task which will be next */
+       /* update r31, the current-give me pointer to task which will be next */
+       lwi     CURRENT_TASK, r6, TI_TASK
        /* stored it to current_save too */
-       swi     r31, r0, PER_CPU(CURRENT_SAVE)
+       swi     CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE)
 
        /* get new process' cpu context and restore */
        /* give me start where start context of next task */
index 3091619..cb7815c 100644 (file)
@@ -99,8 +99,8 @@ no_fdt_arg:
        tophys(r4,r4)                   /* convert to phys address */
        ori     r3, r0, COMMAND_LINE_SIZE - 1 /* number of loops */
 _copy_command_line:
-       lbu     r7, r5, r6 /* r7=r5+r6 - r5 contain pointer to command line */
-       sb      r7, r4, r6              /* addr[r4+r6]= r7*/
+       lbu     r2, r5, r6 /* r7=r5+r6 - r5 contain pointer to command line */
+       sb      r2, r4, r6              /* addr[r4+r6]= r7*/
        addik   r6, r6, 1               /* increment counting */
        bgtid   r3, _copy_command_line  /* loop for all entries       */
        addik   r3, r3, -1              /* descrement loop */
@@ -136,6 +136,11 @@ _invalidate:
        addik   r3, r3, -1
        /* sync */
 
+       /* Setup the kernel PID */
+       mts     rpid,r0                 /* Load the kernel PID */
+       nop
+       bri     4
+
        /*
         * We should still be executing code at physical address area
         * RAM_BASEADDR at this point. However, kernel code is at
@@ -146,10 +151,6 @@ _invalidate:
        addik   r3,r0, CONFIG_KERNEL_START /* Load the kernel virtual address */
        tophys(r4,r3)                   /* Load the kernel physical address */
 
-       mts     rpid,r0                 /* Load the kernel PID */
-       nop
-       bri     4
-
        /*
         * Configure and load two entries into TLB slots 0 and 1.
         * In case we are pinning TLBs, these are reserved in by the
index 0f06034..6f39e2c 100644 (file)
@@ -93,3 +93,18 @@ skip:
        }
        return 0;
 }
+
+/* MS: There is no any advance mapping mechanism. We are using simple 32bit
+  intc without any cascades or any connection that's why mapping is 1:1 */
+unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq)
+{
+       return hwirq;
+}
+EXPORT_SYMBOL_GPL(irq_create_mapping);
+
+unsigned int irq_create_of_mapping(struct device_node *controller,
+                                       u32 *intspec, unsigned int intsize)
+{
+       return intspec[0];
+}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
index bb8c4b9..f974ec7 100644 (file)
 #include <linux/io.h>
 #include <linux/bug.h>
 #include <linux/param.h>
+#include <linux/pci.h>
 #include <linux/cache.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
 #include <asm/cacheflush.h>
 #include <asm/entry.h>
 #include <asm/cpuinfo.h>
@@ -54,14 +57,10 @@ void __init setup_arch(char **cmdline_p)
 
        microblaze_cache_init();
 
-       invalidate_dcache();
-       enable_dcache();
-
-       invalidate_icache();
-       enable_icache();
-
        setup_memory();
 
+       xilinx_pci_init();
+
 #if defined(CONFIG_SELFMOD_INTC) || defined(CONFIG_SELFMOD_TIMER)
        printk(KERN_NOTICE "Self modified code enable\n");
 #endif
@@ -188,3 +187,37 @@ static int microblaze_debugfs_init(void)
 }
 arch_initcall(microblaze_debugfs_init);
 #endif
+
+static int dflt_bus_notify(struct notifier_block *nb,
+                               unsigned long action, void *data)
+{
+       struct device *dev = data;
+
+       /* We are only intereted in device addition */
+       if (action != BUS_NOTIFY_ADD_DEVICE)
+               return 0;
+
+       set_dma_ops(dev, &dma_direct_ops);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block dflt_plat_bus_notifier = {
+       .notifier_call = dflt_bus_notify,
+       .priority = INT_MAX,
+};
+
+static struct notifier_block dflt_of_bus_notifier = {
+       .notifier_call = dflt_bus_notify,
+       .priority = INT_MAX,
+};
+
+static int __init setup_bus_notifier(void)
+{
+       bus_register_notifier(&platform_bus_type, &dflt_plat_bus_notifier);
+       bus_register_notifier(&of_platform_bus_type, &dflt_of_bus_notifier);
+
+       return 0;
+}
+
+arch_initcall(setup_bus_notifier);
index 6c8a924..09c49ed 100644 (file)
@@ -2,6 +2,6 @@
 # Makefile
 #
 
-obj-y := init.o
+obj-y := consistent.o init.o
 
 obj-$(CONFIG_MMU) += pgtable.o mmu_context.o fault.o
diff --git a/arch/microblaze/mm/consistent.c b/arch/microblaze/mm/consistent.c
new file mode 100644 (file)
index 0000000..a9b443e
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Microblaze support for cache consistent memory.
+ * Copyright (C) 2010 Michal Simek <monstr@monstr.eu>
+ * Copyright (C) 2010 PetaLogix
+ * Copyright (C) 2005 John Williams <jwilliams@itee.uq.edu.au>
+ *
+ * Based on PowerPC version derived from arch/arm/mm/consistent.c
+ * Copyright (C) 2001 Dan Malek (dmalek@jlc.net)
+ * Copyright (C) 2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/stddef.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/bootmem.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <asm/pgalloc.h>
+#include <linux/io.h>
+#include <linux/hardirq.h>
+#include <asm/mmu_context.h>
+#include <asm/mmu.h>
+#include <linux/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/cpuinfo.h>
+
+#ifndef CONFIG_MMU
+
+/* I have to use dcache values because I can't relate on ram size */
+#define UNCACHED_SHADOW_MASK (cpuinfo.dcache_high - cpuinfo.dcache_base + 1)
+
+/*
+ * Consistent memory allocators. Used for DMA devices that want to
+ * share uncached memory with the processor core.
+ * My crufty no-MMU approach is simple. In the HW platform we can optionally
+ * mirror the DDR up above the processor cacheable region.  So, memory accessed
+ * in this mirror region will not be cached.  It's alloced from the same
+ * pool as normal memory, but the handle we return is shifted up into the
+ * uncached region.  This will no doubt cause big problems if memory allocated
+ * here is not also freed properly. -- JW
+ */
+void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
+{
+       struct page *page, *end, *free;
+       unsigned long order;
+       void *ret, *virt;
+
+       if (in_interrupt())
+               BUG();
+
+       size = PAGE_ALIGN(size);
+       order = get_order(size);
+
+       page = alloc_pages(gfp, order);
+       if (!page)
+               goto no_page;
+
+       /* We could do with a page_to_phys and page_to_bus here. */
+       virt = page_address(page);
+       ret = ioremap(virt_to_phys(virt), size);
+       if (!ret)
+               goto no_remap;
+
+       /*
+        * Here's the magic!  Note if the uncached shadow is not implemented,
+        * it's up to the calling code to also test that condition and make
+        * other arranegments, such as manually flushing the cache and so on.
+        */
+#ifdef CONFIG_XILINX_UNCACHED_SHADOW
+       ret = (void *)((unsigned) ret | UNCACHED_SHADOW_MASK);
+#endif
+       /* dma_handle is same as physical (shadowed) address */
+       *dma_handle = (dma_addr_t)ret;
+
+       /*
+        * free wasted pages.  We skip the first page since we know
+        * that it will have count = 1 and won't require freeing.
+        * We also mark the pages in use as reserved so that
+        * remap_page_range works.
+        */
+       page = virt_to_page(virt);
+       free = page + (size >> PAGE_SHIFT);
+       end  = page + (1 << order);
+
+       for (; page < end; page++) {
+               init_page_count(page);
+               if (page >= free)
+                       __free_page(page);
+               else
+                       SetPageReserved(page);
+       }
+
+       return ret;
+no_remap:
+       __free_pages(page, order);
+no_page:
+       return NULL;
+}
+
+#else
+
+void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle)
+{
+       int order, err, i;
+       unsigned long page, va, flags;
+       phys_addr_t pa;
+       struct vm_struct *area;
+       void     *ret;
+
+       if (in_interrupt())
+               BUG();
+
+       /* Only allocate page size areas. */
+       size = PAGE_ALIGN(size);
+       order = get_order(size);
+
+       page = __get_free_pages(gfp, order);
+       if (!page) {
+               BUG();
+               return NULL;
+       }
+
+       /*
+        * we need to ensure that there are no cachelines in use,
+        * or worse dirty in this area.
+        */
+       flush_dcache_range(virt_to_phys(page), virt_to_phys(page) + size);
+
+       /* Allocate some common virtual space to map the new pages. */
+       area = get_vm_area(size, VM_ALLOC);
+       if (area == NULL) {
+               free_pages(page, order);
+               return NULL;
+       }
+       va = (unsigned long) area->addr;
+       ret = (void *)va;
+
+       /* This gives us the real physical address of the first page. */
+       *dma_handle = pa = virt_to_bus((void *)page);
+
+       /* MS: This is the whole magic - use cache inhibit pages */
+       flags = _PAGE_KERNEL | _PAGE_NO_CACHE;
+
+       /*
+        * Set refcount=1 on all pages in an order>0
+        * allocation so that vfree() will actually
+        * free all pages that were allocated.
+        */
+       if (order > 0) {
+               struct page *rpage = virt_to_page(page);
+               for (i = 1; i < (1 << order); i++)
+                       init_page_count(rpage+i);
+       }
+
+       err = 0;
+       for (i = 0; i < size && err == 0; i += PAGE_SIZE)
+               err = map_page(va+i, pa+i, flags);
+
+       if (err) {
+               vfree((void *)va);
+               return NULL;
+       }
+
+       return ret;
+}
+#endif /* CONFIG_MMU */
+EXPORT_SYMBOL(consistent_alloc);
+
+/*
+ * free page(s) as defined by the above mapping.
+ */
+void consistent_free(void *vaddr)
+{
+       if (in_interrupt())
+               BUG();
+
+       /* Clear SHADOW_MASK bit in address, and free as per usual */
+#ifdef CONFIG_XILINX_UNCACHED_SHADOW
+       vaddr = (void *)((unsigned)vaddr & ~UNCACHED_SHADOW_MASK);
+#endif
+       vfree(vaddr);
+}
+EXPORT_SYMBOL(consistent_free);
+
+/*
+ * make an area consistent.
+ */
+void consistent_sync(void *vaddr, size_t size, int direction)
+{
+       unsigned long start;
+       unsigned long end;
+
+       start = (unsigned long)vaddr;
+
+       /* Convert start address back down to unshadowed memory region */
+#ifdef CONFIG_XILINX_UNCACHED_SHADOW
+       start &= ~UNCACHED_SHADOW_MASK;
+#endif
+       end = start + size;
+
+       switch (direction) {
+       case PCI_DMA_NONE:
+               BUG();
+       case PCI_DMA_FROMDEVICE:        /* invalidate only */
+               flush_dcache_range(start, end);
+               break;
+       case PCI_DMA_TODEVICE:          /* writeback only */
+               flush_dcache_range(start, end);
+               break;
+       case PCI_DMA_BIDIRECTIONAL:     /* writeback and invalidate */
+               flush_dcache_range(start, end);
+               break;
+       }
+}
+EXPORT_SYMBOL(consistent_sync);
+
+/*
+ * consistent_sync_page makes memory consistent. identical
+ * to consistent_sync, but takes a struct page instead of a
+ * virtual address
+ */
+void consistent_sync_page(struct page *page, unsigned long offset,
+       size_t size, int direction)
+{
+       unsigned long start = (unsigned long)page_address(page) + offset;
+       consistent_sync((void *)start, size, direction);
+}
+EXPORT_SYMBOL(consistent_sync_page);
index a57cedf..1608e2e 100644 (file)
@@ -23,6 +23,9 @@
 #include <asm/sections.h>
 #include <asm/tlb.h>
 
+/* Use for MMU and noMMU because of PCI generic code */
+int mem_init_done;
+
 #ifndef CONFIG_MMU
 unsigned int __page_offset;
 EXPORT_SYMBOL(__page_offset);
@@ -30,7 +33,6 @@ EXPORT_SYMBOL(__page_offset);
 #else
 DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
 
-int mem_init_done;
 static int init_bootmem_done;
 #endif /* CONFIG_MMU */
 
@@ -193,12 +195,6 @@ void free_initmem(void)
                        (unsigned long)(&__init_end));
 }
 
-/* FIXME from arch/powerpc/mm/mem.c*/
-void show_mem(void)
-{
-       printk(KERN_NOTICE "%s\n", __func__);
-}
-
 void __init mem_init(void)
 {
        high_memory = (void *)__va(memory_end);
@@ -208,9 +204,7 @@ void __init mem_init(void)
        printk(KERN_INFO "Memory: %luk/%luk available\n",
               nr_free_pages() << (PAGE_SHIFT-10),
               num_physpages << (PAGE_SHIFT-10));
-#ifdef CONFIG_MMU
        mem_init_done = 1;
-#endif
 }
 
 #ifndef CONFIG_MMU
@@ -222,6 +216,10 @@ int ___range_ok(unsigned long addr, unsigned long size)
 }
 EXPORT_SYMBOL(___range_ok);
 
+int page_is_ram(unsigned long pfn)
+{
+       return __range_ok(pfn, 0);
+}
 #else
 int page_is_ram(unsigned long pfn)
 {
@@ -349,4 +347,27 @@ void __init *early_get_page(void)
        }
        return p;
 }
+
 #endif /* CONFIG_MMU */
+
+void * __init_refok alloc_maybe_bootmem(size_t size, gfp_t mask)
+{
+       if (mem_init_done)
+               return kmalloc(size, mask);
+       else
+               return alloc_bootmem(size);
+}
+
+void * __init_refok zalloc_maybe_bootmem(size_t size, gfp_t mask)
+{
+       void *p;
+
+       if (mem_init_done)
+               p = kzalloc(size, mask);
+       else {
+               p = alloc_bootmem(size);
+               if (p)
+                       memset(p, 0, size);
+       }
+       return p;
+}
index 2820081..63a6fd0 100644 (file)
@@ -103,7 +103,7 @@ static void __iomem *__ioremap(phys_addr_t addr, unsigned long size,
                area = get_vm_area(size, VM_IOREMAP);
                if (area == NULL)
                        return NULL;
-               v = VMALLOC_VMADDR(area->addr);
+               v = (unsigned long) area->addr;
        } else {
                v = (ioremap_bot -= size);
        }
diff --git a/arch/microblaze/pci/Makefile b/arch/microblaze/pci/Makefile
new file mode 100644 (file)
index 0000000..9889cc2
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile
+#
+
+obj-$(CONFIG_PCI)              += pci_32.o pci-common.o indirect_pci.o iomap.o
+obj-$(CONFIG_PCI_XILINX)       += xilinx_pci.o
diff --git a/arch/microblaze/pci/indirect_pci.c b/arch/microblaze/pci/indirect_pci.c
new file mode 100644 (file)
index 0000000..25f18f0
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Support for indirect PCI bridges.
+ *
+ * Copyright (C) 1998 Gabriel Paubert.
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+static int
+indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
+                    int len, u32 *val)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       volatile void __iomem *cfg_data;
+       u8 cfg_type = 0;
+       u32 bus_no, reg;
+
+       if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
+               if (bus->number != hose->first_busno)
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+               if (devfn != 0)
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
+               if (bus->number != hose->first_busno)
+                       cfg_type = 1;
+
+       bus_no = (bus->number == hose->first_busno) ?
+                       hose->self_busno : bus->number;
+
+       if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
+               reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
+       else
+               reg = offset & 0xfc; /* Only 3 bits for function */
+
+       if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
+               out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
+                        (devfn << 8) | reg | cfg_type));
+       else
+               out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
+                        (devfn << 8) | reg | cfg_type));
+
+       /*
+        * Note: the caller has already checked that offset is
+        * suitably aligned and that len is 1, 2 or 4.
+        */
+       cfg_data = hose->cfg_data + (offset & 3); /* Only 3 bits for function */
+       switch (len) {
+       case 1:
+               *val = in_8(cfg_data);
+               break;
+       case 2:
+               *val = in_le16(cfg_data);
+               break;
+       default:
+               *val = in_le32(cfg_data);
+               break;
+       }
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
+                     int len, u32 val)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       volatile void __iomem *cfg_data;
+       u8 cfg_type = 0;
+       u32 bus_no, reg;
+
+       if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
+               if (bus->number != hose->first_busno)
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+               if (devfn != 0)
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+       }
+
+       if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
+               if (bus->number != hose->first_busno)
+                       cfg_type = 1;
+
+       bus_no = (bus->number == hose->first_busno) ?
+                       hose->self_busno : bus->number;
+
+       if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
+               reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
+       else
+               reg = offset & 0xfc;
+
+       if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
+               out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
+                        (devfn << 8) | reg | cfg_type));
+       else
+               out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
+                        (devfn << 8) | reg | cfg_type));
+
+       /* surpress setting of PCI_PRIMARY_BUS */
+       if (hose->indirect_type & INDIRECT_TYPE_SURPRESS_PRIMARY_BUS)
+               if ((offset == PCI_PRIMARY_BUS) &&
+                       (bus->number == hose->first_busno))
+                       val &= 0xffffff00;
+
+       /* Workaround for PCI_28 Errata in 440EPx/GRx */
+       if ((hose->indirect_type & INDIRECT_TYPE_BROKEN_MRM) &&
+                       offset == PCI_CACHE_LINE_SIZE) {
+               val = 0;
+       }
+
+       /*
+        * Note: the caller has already checked that offset is
+        * suitably aligned and that len is 1, 2 or 4.
+        */
+       cfg_data = hose->cfg_data + (offset & 3);
+       switch (len) {
+       case 1:
+               out_8(cfg_data, val);
+               break;
+       case 2:
+               out_le16(cfg_data, val);
+               break;
+       default:
+               out_le32(cfg_data, val);
+               break;
+       }
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops indirect_pci_ops = {
+       .read = indirect_read_config,
+       .write = indirect_write_config,
+};
+
+void __init
+setup_indirect_pci(struct pci_controller *hose,
+                  resource_size_t cfg_addr,
+                  resource_size_t cfg_data, u32 flags)
+{
+       resource_size_t base = cfg_addr & PAGE_MASK;
+       void __iomem *mbase;
+
+       mbase = ioremap(base, PAGE_SIZE);
+       hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK);
+       if ((cfg_data & PAGE_MASK) != base)
+               mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
+       hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK);
+       hose->ops = &indirect_pci_ops;
+       hose->indirect_type = flags;
+}
diff --git a/arch/microblaze/pci/iomap.c b/arch/microblaze/pci/iomap.c
new file mode 100644 (file)
index 0000000..3fbf16f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * ppc64 "iomap" interface implementation.
+ *
+ * (C) Copyright 2004 Linus Torvalds
+ */
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+
+void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
+{
+       resource_size_t start = pci_resource_start(dev, bar);
+       resource_size_t len = pci_resource_len(dev, bar);
+       unsigned long flags = pci_resource_flags(dev, bar);
+
+       if (!len)
+               return NULL;
+       if (max && len > max)
+               len = max;
+       if (flags & IORESOURCE_IO)
+               return ioport_map(start, len);
+       if (flags & IORESOURCE_MEM)
+               return ioremap(start, len);
+       /* What? */
+       return NULL;
+}
+EXPORT_SYMBOL(pci_iomap);
+
+void pci_iounmap(struct pci_dev *dev, void __iomem *addr)
+{
+       if (isa_vaddr_is_ioport(addr))
+               return;
+       if (pcibios_vaddr_is_ioport(addr))
+               return;
+       iounmap(addr);
+}
+EXPORT_SYMBOL(pci_iounmap);
diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c
new file mode 100644 (file)
index 0000000..0be3435
--- /dev/null
@@ -0,0 +1,1642 @@
+/*
+ * Contains common pci routines for ALL ppc platform
+ * (based on pci_32.c and pci_64.c)
+ *
+ * Port for PPC64 David Engebretsen, IBM Corp.
+ * Contains common pci routines for ppc64 platform, pSeries and iSeries brands.
+ *
+ * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
+ *   Rework, based on alpha PCI code.
+ *
+ * Common pmac/prep/chrp pci routines. -- Cort
+ *
+ * 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; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/syscalls.h>
+#include <linux/irq.h>
+#include <linux/vmalloc.h>
+
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/byteorder.h>
+
+static DEFINE_SPINLOCK(hose_spinlock);
+LIST_HEAD(hose_list);
+
+/* XXX kill that some day ... */
+static int global_phb_number;          /* Global phb counter */
+
+/* ISA Memory physical address */
+resource_size_t isa_mem_base;
+
+/* Default PCI flags is 0 on ppc32, modified at boot on ppc64 */
+unsigned int pci_flags;
+
+static struct dma_map_ops *pci_dma_ops = &dma_direct_ops;
+
+void set_pci_dma_ops(struct dma_map_ops *dma_ops)
+{
+       pci_dma_ops = dma_ops;
+}
+
+struct dma_map_ops *get_pci_dma_ops(void)
+{
+       return pci_dma_ops;
+}
+EXPORT_SYMBOL(get_pci_dma_ops);
+
+int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+{
+       return dma_set_mask(&dev->dev, mask);
+}
+
+int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{
+       int rc;
+
+       rc = dma_set_mask(&dev->dev, mask);
+       dev->dev.coherent_dma_mask = dev->dma_mask;
+
+       return rc;
+}
+
+struct pci_controller *pcibios_alloc_controller(struct device_node *dev)
+{
+       struct pci_controller *phb;
+
+       phb = zalloc_maybe_bootmem(sizeof(struct pci_controller), GFP_KERNEL);
+       if (!phb)
+               return NULL;
+       spin_lock(&hose_spinlock);
+       phb->global_number = global_phb_number++;
+       list_add_tail(&phb->list_node, &hose_list);
+       spin_unlock(&hose_spinlock);
+       phb->dn = dev;
+       phb->is_dynamic = mem_init_done;
+       return phb;
+}
+
+void pcibios_free_controller(struct pci_controller *phb)
+{
+       spin_lock(&hose_spinlock);
+       list_del(&phb->list_node);
+       spin_unlock(&hose_spinlock);
+
+       if (phb->is_dynamic)
+               kfree(phb);
+}
+
+static resource_size_t pcibios_io_size(const struct pci_controller *hose)
+{
+       return hose->io_resource.end - hose->io_resource.start + 1;
+}
+
+int pcibios_vaddr_is_ioport(void __iomem *address)
+{
+       int ret = 0;
+       struct pci_controller *hose;
+       resource_size_t size;
+
+       spin_lock(&hose_spinlock);
+       list_for_each_entry(hose, &hose_list, list_node) {
+               size = pcibios_io_size(hose);
+               if (address >= hose->io_base_virt &&
+                   address < (hose->io_base_virt + size)) {
+                       ret = 1;
+                       break;
+               }
+       }
+       spin_unlock(&hose_spinlock);
+       return ret;
+}
+
+unsigned long pci_address_to_pio(phys_addr_t address)
+{
+       struct pci_controller *hose;
+       resource_size_t size;
+       unsigned long ret = ~0;
+
+       spin_lock(&hose_spinlock);
+       list_for_each_entry(hose, &hose_list, list_node) {
+               size = pcibios_io_size(hose);
+               if (address >= hose->io_base_phys &&
+                   address < (hose->io_base_phys + size)) {
+                       unsigned long base =
+                               (unsigned long)hose->io_base_virt - _IO_BASE;
+                       ret = base + (address - hose->io_base_phys);
+                       break;
+               }
+       }
+       spin_unlock(&hose_spinlock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_address_to_pio);
+
+/*
+ * Return the domain number for this bus.
+ */
+int pci_domain_nr(struct pci_bus *bus)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+
+       return hose->global_number;
+}
+EXPORT_SYMBOL(pci_domain_nr);
+
+/* This routine is meant to be used early during boot, when the
+ * PCI bus numbers have not yet been assigned, and you need to
+ * issue PCI config cycles to an OF device.
+ * It could also be used to "fix" RTAS config cycles if you want
+ * to set pci_assign_all_buses to 1 and still use RTAS for PCI
+ * config cycles.
+ */
+struct pci_controller *pci_find_hose_for_OF_device(struct device_node *node)
+{
+       while (node) {
+               struct pci_controller *hose, *tmp;
+               list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
+                       if (hose->dn == node)
+                               return hose;
+               node = node->parent;
+       }
+       return NULL;
+}
+
+static ssize_t pci_show_devspec(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct pci_dev *pdev;
+       struct device_node *np;
+
+       pdev = to_pci_dev(dev);
+       np = pci_device_to_OF_node(pdev);
+       if (np == NULL || np->full_name == NULL)
+               return 0;
+       return sprintf(buf, "%s", np->full_name);
+}
+static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL);
+
+/* Add sysfs properties */
+int pcibios_add_platform_entries(struct pci_dev *pdev)
+{
+       return device_create_file(&pdev->dev, &dev_attr_devspec);
+}
+
+char __devinit *pcibios_setup(char *str)
+{
+       return str;
+}
+
+/*
+ * Reads the interrupt pin to determine if interrupt is use by card.
+ * If the interrupt is used, then gets the interrupt line from the
+ * openfirmware and sets it in the pci_dev and pci_config line.
+ */
+int pci_read_irq_line(struct pci_dev *pci_dev)
+{
+       struct of_irq oirq;
+       unsigned int virq;
+
+       /* The current device-tree that iSeries generates from the HV
+        * PCI informations doesn't contain proper interrupt routing,
+        * and all the fallback would do is print out crap, so we
+        * don't attempt to resolve the interrupts here at all, some
+        * iSeries specific fixup does it.
+        *
+        * In the long run, we will hopefully fix the generated device-tree
+        * instead.
+        */
+       pr_debug("PCI: Try to map irq for %s...\n", pci_name(pci_dev));
+
+#ifdef DEBUG
+       memset(&oirq, 0xff, sizeof(oirq));
+#endif
+       /* Try to get a mapping from the device-tree */
+       if (of_irq_map_pci(pci_dev, &oirq)) {
+               u8 line, pin;
+
+               /* If that fails, lets fallback to what is in the config
+                * space and map that through the default controller. We
+                * also set the type to level low since that's what PCI
+                * interrupts are. If your platform does differently, then
+                * either provide a proper interrupt tree or don't use this
+                * function.
+                */
+               if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin))
+                       return -1;
+               if (pin == 0)
+                       return -1;
+               if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) ||
+                   line == 0xff || line == 0) {
+                       return -1;
+               }
+               pr_debug(" No map ! Using line %d (pin %d) from PCI config\n",
+                        line, pin);
+
+               virq = irq_create_mapping(NULL, line);
+               if (virq != NO_IRQ)
+                       set_irq_type(virq, IRQ_TYPE_LEVEL_LOW);
+       } else {
+               pr_debug(" Got one, spec %d cells (0x%08x 0x%08x...) on %s\n",
+                        oirq.size, oirq.specifier[0], oirq.specifier[1],
+                        oirq.controller ? oirq.controller->full_name :
+                        "<default>");
+
+               virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
+                                            oirq.size);
+       }
+       if (virq == NO_IRQ) {
+               pr_debug(" Failed to map !\n");
+               return -1;
+       }
+
+       pr_debug(" Mapped to linux irq %d\n", virq);
+
+       pci_dev->irq = virq;
+
+       return 0;
+}
+EXPORT_SYMBOL(pci_read_irq_line);
+
+/*
+ * Platform support for /proc/bus/pci/X/Y mmap()s,
+ * modelled on the sparc64 implementation by Dave Miller.
+ *  -- paulus.
+ */
+
+/*
+ * Adjust vm_pgoff of VMA such that it is the physical page offset
+ * corresponding to the 32-bit pci bus offset for DEV requested by the user.
+ *
+ * Basically, the user finds the base address for his device which he wishes
+ * to mmap.  They read the 32-bit value from the config space base register,
+ * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
+ * offset parameter of mmap on /proc/bus/pci/XXX for that device.
+ *
+ * Returns negative error code on failure, zero on success.
+ */
+static struct resource *__pci_mmap_make_offset(struct pci_dev *dev,
+                                              resource_size_t *offset,
+                                              enum pci_mmap_state mmap_state)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       unsigned long io_offset = 0;
+       int i, res_bit;
+
+       if (hose == 0)
+               return NULL;            /* should never happen */
+
+       /* If memory, add on the PCI bridge address offset */
+       if (mmap_state == pci_mmap_mem) {
+#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
+               *offset += hose->pci_mem_offset;
+#endif
+               res_bit = IORESOURCE_MEM;
+       } else {
+               io_offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+               *offset += io_offset;
+               res_bit = IORESOURCE_IO;
+       }
+
+       /*
+        * Check that the offset requested corresponds to one of the
+        * resources of the device.
+        */
+       for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
+               struct resource *rp = &dev->resource[i];
+               int flags = rp->flags;
+
+               /* treat ROM as memory (should be already) */
+               if (i == PCI_ROM_RESOURCE)
+                       flags |= IORESOURCE_MEM;
+
+               /* Active and same type? */
+               if ((flags & res_bit) == 0)
+                       continue;
+
+               /* In the range of this resource? */
+               if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end)
+                       continue;
+
+               /* found it! construct the final physical address */
+               if (mmap_state == pci_mmap_io)
+                       *offset += hose->io_base_phys - io_offset;
+               return rp;
+       }
+
+       return NULL;
+}
+
+/*
+ * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
+ * device mapping.
+ */
+static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp,
+                                     pgprot_t protection,
+                                     enum pci_mmap_state mmap_state,
+                                     int write_combine)
+{
+       pgprot_t prot = protection;
+
+       /* Write combine is always 0 on non-memory space mappings. On
+        * memory space, if the user didn't pass 1, we check for a
+        * "prefetchable" resource. This is a bit hackish, but we use
+        * this to workaround the inability of /sysfs to provide a write
+        * combine bit
+        */
+       if (mmap_state != pci_mmap_mem)
+               write_combine = 0;
+       else if (write_combine == 0) {
+               if (rp->flags & IORESOURCE_PREFETCH)
+                       write_combine = 1;
+       }
+
+       return pgprot_noncached(prot);
+}
+
+/*
+ * This one is used by /dev/mem and fbdev who have no clue about the
+ * PCI device, it tries to find the PCI device first and calls the
+ * above routine
+ */
+pgprot_t pci_phys_mem_access_prot(struct file *file,
+                                 unsigned long pfn,
+                                 unsigned long size,
+                                 pgprot_t prot)
+{
+       struct pci_dev *pdev = NULL;
+       struct resource *found = NULL;
+       resource_size_t offset = ((resource_size_t)pfn) << PAGE_SHIFT;
+       int i;
+
+       if (page_is_ram(pfn))
+               return prot;
+
+       prot = pgprot_noncached(prot);
+       for_each_pci_dev(pdev) {
+               for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
+                       struct resource *rp = &pdev->resource[i];
+                       int flags = rp->flags;
+
+                       /* Active and same type? */
+                       if ((flags & IORESOURCE_MEM) == 0)
+                               continue;
+                       /* In the range of this resource? */
+                       if (offset < (rp->start & PAGE_MASK) ||
+                           offset > rp->end)
+                               continue;
+                       found = rp;
+                       break;
+               }
+               if (found)
+                       break;
+       }
+       if (found) {
+               if (found->flags & IORESOURCE_PREFETCH)
+                       prot = pgprot_noncached_wc(prot);
+               pci_dev_put(pdev);
+       }
+
+       pr_debug("PCI: Non-PCI map for %llx, prot: %lx\n",
+                (unsigned long long)offset, pgprot_val(prot));
+
+       return prot;
+}
+
+/*
+ * Perform the actual remap of the pages for a PCI device mapping, as
+ * appropriate for this architecture.  The region in the process to map
+ * is described by vm_start and vm_end members of VMA, the base physical
+ * address is found in vm_pgoff.
+ * The pci device structure is provided so that architectures may make mapping
+ * decisions on a per-device or per-bus basis.
+ *
+ * Returns a negative error code on failure, zero on success.
+ */
+int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
+                       enum pci_mmap_state mmap_state, int write_combine)
+{
+       resource_size_t offset =
+               ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT;
+       struct resource *rp;
+       int ret;
+
+       rp = __pci_mmap_make_offset(dev, &offset, mmap_state);
+       if (rp == NULL)
+               return -EINVAL;
+
+       vma->vm_pgoff = offset >> PAGE_SHIFT;
+       vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp,
+                                                 vma->vm_page_prot,
+                                                 mmap_state, write_combine);
+
+       ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                              vma->vm_end - vma->vm_start, vma->vm_page_prot);
+
+       return ret;
+}
+
+/* This provides legacy IO read access on a bus */
+int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size)
+{
+       unsigned long offset;
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       struct resource *rp = &hose->io_resource;
+       void __iomem *addr;
+
+       /* Check if port can be supported by that bus. We only check
+        * the ranges of the PHB though, not the bus itself as the rules
+        * for forwarding legacy cycles down bridges are not our problem
+        * here. So if the host bridge supports it, we do it.
+        */
+       offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+       offset += port;
+
+       if (!(rp->flags & IORESOURCE_IO))
+               return -ENXIO;
+       if (offset < rp->start || (offset + size) > rp->end)
+               return -ENXIO;
+       addr = hose->io_base_virt + port;
+
+       switch (size) {
+       case 1:
+               *((u8 *)val) = in_8(addr);
+               return 1;
+       case 2:
+               if (port & 1)
+                       return -EINVAL;
+               *((u16 *)val) = in_le16(addr);
+               return 2;
+       case 4:
+               if (port & 3)
+                       return -EINVAL;
+               *((u32 *)val) = in_le32(addr);
+               return 4;
+       }
+       return -EINVAL;
+}
+
+/* This provides legacy IO write access on a bus */
+int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
+{
+       unsigned long offset;
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       struct resource *rp = &hose->io_resource;
+       void __iomem *addr;
+
+       /* Check if port can be supported by that bus. We only check
+        * the ranges of the PHB though, not the bus itself as the rules
+        * for forwarding legacy cycles down bridges are not our problem
+        * here. So if the host bridge supports it, we do it.
+        */
+       offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+       offset += port;
+
+       if (!(rp->flags & IORESOURCE_IO))
+               return -ENXIO;
+       if (offset < rp->start || (offset + size) > rp->end)
+               return -ENXIO;
+       addr = hose->io_base_virt + port;
+
+       /* WARNING: The generic code is idiotic. It gets passed a pointer
+        * to what can be a 1, 2 or 4 byte quantity and always reads that
+        * as a u32, which means that we have to correct the location of
+        * the data read within those 32 bits for size 1 and 2
+        */
+       switch (size) {
+       case 1:
+               out_8(addr, val >> 24);
+               return 1;
+       case 2:
+               if (port & 1)
+                       return -EINVAL;
+               out_le16(addr, val >> 16);
+               return 2;
+       case 4:
+               if (port & 3)
+                       return -EINVAL;
+               out_le32(addr, val);
+               return 4;
+       }
+       return -EINVAL;
+}
+
+/* This provides legacy IO or memory mmap access on a bus */
+int pci_mmap_legacy_page_range(struct pci_bus *bus,
+                              struct vm_area_struct *vma,
+                              enum pci_mmap_state mmap_state)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       resource_size_t offset =
+               ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT;
+       resource_size_t size = vma->vm_end - vma->vm_start;
+       struct resource *rp;
+
+       pr_debug("pci_mmap_legacy_page_range(%04x:%02x, %s @%llx..%llx)\n",
+                pci_domain_nr(bus), bus->number,
+                mmap_state == pci_mmap_mem ? "MEM" : "IO",
+                (unsigned long long)offset,
+                (unsigned long long)(offset + size - 1));
+
+       if (mmap_state == pci_mmap_mem) {
+               /* Hack alert !
+                *
+                * Because X is lame and can fail starting if it gets an error
+                * trying to mmap legacy_mem (instead of just moving on without
+                * legacy memory access) we fake it here by giving it anonymous
+                * memory, effectively behaving just like /dev/zero
+                */
+               if ((offset + size) > hose->isa_mem_size) {
+#ifdef CONFIG_MMU
+                       printk(KERN_DEBUG
+                               "Process %s (pid:%d) mapped non-existing PCI"
+                               "legacy memory for 0%04x:%02x\n",
+                               current->comm, current->pid, pci_domain_nr(bus),
+                                                               bus->number);
+#endif
+                       if (vma->vm_flags & VM_SHARED)
+                               return shmem_zero_setup(vma);
+                       return 0;
+               }
+               offset += hose->isa_mem_phys;
+       } else {
+               unsigned long io_offset = (unsigned long)hose->io_base_virt - \
+                                                               _IO_BASE;
+               unsigned long roffset = offset + io_offset;
+               rp = &hose->io_resource;
+               if (!(rp->flags & IORESOURCE_IO))
+                       return -ENXIO;
+               if (roffset < rp->start || (roffset + size) > rp->end)
+                       return -ENXIO;
+               offset += hose->io_base_phys;
+       }
+       pr_debug(" -> mapping phys %llx\n", (unsigned long long)offset);
+
+       vma->vm_pgoff = offset >> PAGE_SHIFT;
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+       return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                              vma->vm_end - vma->vm_start,
+                              vma->vm_page_prot);
+}
+
+void pci_resource_to_user(const struct pci_dev *dev, int bar,
+                         const struct resource *rsrc,
+                         resource_size_t *start, resource_size_t *end)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       resource_size_t offset = 0;
+
+       if (hose == NULL)
+               return;
+
+       if (rsrc->flags & IORESOURCE_IO)
+               offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+
+       /* We pass a fully fixed up address to userland for MMIO instead of
+        * a BAR value because X is lame and expects to be able to use that
+        * to pass to /dev/mem !
+        *
+        * That means that we'll have potentially 64 bits values where some
+        * userland apps only expect 32 (like X itself since it thinks only
+        * Sparc has 64 bits MMIO) but if we don't do that, we break it on
+        * 32 bits CHRPs :-(
+        *
+        * Hopefully, the sysfs insterface is immune to that gunk. Once X
+        * has been fixed (and the fix spread enough), we can re-enable the
+        * 2 lines below and pass down a BAR value to userland. In that case
+        * we'll also have to re-enable the matching code in
+        * __pci_mmap_make_offset().
+        *
+        * BenH.
+        */
+#if 0
+       else if (rsrc->flags & IORESOURCE_MEM)
+               offset = hose->pci_mem_offset;
+#endif
+
+       *start = rsrc->start - offset;
+       *end = rsrc->end - offset;
+}
+
+/**
+ * pci_process_bridge_OF_ranges - Parse PCI bridge resources from device tree
+ * @hose: newly allocated pci_controller to be setup
+ * @dev: device node of the host bridge
+ * @primary: set if primary bus (32 bits only, soon to be deprecated)
+ *
+ * This function will parse the "ranges" property of a PCI host bridge device
+ * node and setup the resource mapping of a pci controller based on its
+ * content.
+ *
+ * Life would be boring if it wasn't for a few issues that we have to deal
+ * with here:
+ *
+ *   - We can only cope with one IO space range and up to 3 Memory space
+ *     ranges. However, some machines (thanks Apple !) tend to split their
+ *     space into lots of small contiguous ranges. So we have to coalesce.
+ *
+ *   - We can only cope with all memory ranges having the same offset
+ *     between CPU addresses and PCI addresses. Unfortunately, some bridges
+ *     are setup for a large 1:1 mapping along with a small "window" which
+ *     maps PCI address 0 to some arbitrary high address of the CPU space in
+ *     order to give access to the ISA memory hole.
+ *     The way out of here that I've chosen for now is to always set the
+ *     offset based on the first resource found, then override it if we
+ *     have a different offset and the previous was set by an ISA hole.
+ *
+ *   - Some busses have IO space not starting at 0, which causes trouble with
+ *     the way we do our IO resource renumbering. The code somewhat deals with
+ *     it for 64 bits but I would expect problems on 32 bits.
+ *
+ *   - Some 32 bits platforms such as 4xx can have physical space larger than
+ *     32 bits so we need to use 64 bits values for the parsing
+ */
+void __devinit pci_process_bridge_OF_ranges(struct pci_controller *hose,
+                                           struct device_node *dev,
+                                           int primary)
+{
+       const u32 *ranges;
+       int rlen;
+       int pna = of_n_addr_cells(dev);
+       int np = pna + 5;
+       int memno = 0, isa_hole = -1;
+       u32 pci_space;
+       unsigned long long pci_addr, cpu_addr, pci_next, cpu_next, size;
+       unsigned long long isa_mb = 0;
+       struct resource *res;
+
+       printk(KERN_INFO "PCI host bridge %s %s ranges:\n",
+              dev->full_name, primary ? "(primary)" : "");
+
+       /* Get ranges property */
+       ranges = of_get_property(dev, "ranges", &rlen);
+       if (ranges == NULL)
+               return;
+
+       /* Parse it */
+       pr_debug("Parsing ranges property...\n");
+       while ((rlen -= np * 4) >= 0) {
+               /* Read next ranges element */
+               pci_space = ranges[0];
+               pci_addr = of_read_number(ranges + 1, 2);
+               cpu_addr = of_translate_address(dev, ranges + 3);
+               size = of_read_number(ranges + pna + 3, 2);
+
+               pr_debug("pci_space: 0x%08x pci_addr:0x%016llx "
+                               "cpu_addr:0x%016llx size:0x%016llx\n",
+                                       pci_space, pci_addr, cpu_addr, size);
+
+               ranges += np;
+
+               /* If we failed translation or got a zero-sized region
+                * (some FW try to feed us with non sensical zero sized regions
+                * such as power3 which look like some kind of attempt
+                * at exposing the VGA memory hole)
+                */
+               if (cpu_addr == OF_BAD_ADDR || size == 0)
+                       continue;
+
+               /* Now consume following elements while they are contiguous */
+               for (; rlen >= np * sizeof(u32);
+                    ranges += np, rlen -= np * 4) {
+                       if (ranges[0] != pci_space)
+                               break;
+                       pci_next = of_read_number(ranges + 1, 2);
+                       cpu_next = of_translate_address(dev, ranges + 3);
+                       if (pci_next != pci_addr + size ||
+                           cpu_next != cpu_addr + size)
+                               break;
+                       size += of_read_number(ranges + pna + 3, 2);
+               }
+
+               /* Act based on address space type */
+               res = NULL;
+               switch ((pci_space >> 24) & 0x3) {
+               case 1:         /* PCI IO space */
+                       printk(KERN_INFO
+                              "  IO 0x%016llx..0x%016llx -> 0x%016llx\n",
+                              cpu_addr, cpu_addr + size - 1, pci_addr);
+
+                       /* We support only one IO range */
+                       if (hose->pci_io_size) {
+                               printk(KERN_INFO
+                                      " \\--> Skipped (too many) !\n");
+                               continue;
+                       }
+                       /* On 32 bits, limit I/O space to 16MB */
+                       if (size > 0x01000000)
+                               size = 0x01000000;
+
+                       /* 32 bits needs to map IOs here */
+                       hose->io_base_virt = ioremap(cpu_addr, size);
+
+                       /* Expect trouble if pci_addr is not 0 */
+                       if (primary)
+                               isa_io_base =
+                                       (unsigned long)hose->io_base_virt;
+                       /* pci_io_size and io_base_phys always represent IO
+                        * space starting at 0 so we factor in pci_addr
+                        */
+                       hose->pci_io_size = pci_addr + size;
+                       hose->io_base_phys = cpu_addr - pci_addr;
+
+                       /* Build resource */
+                       res = &hose->io_resource;
+                       res->flags = IORESOURCE_IO;
+                       res->start = pci_addr;
+                       break;
+               case 2:         /* PCI Memory space */
+               case 3:         /* PCI 64 bits Memory space */
+                       printk(KERN_INFO
+                              " MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n",
+                              cpu_addr, cpu_addr + size - 1, pci_addr,
+                              (pci_space & 0x40000000) ? "Prefetch" : "");
+
+                       /* We support only 3 memory ranges */
+                       if (memno >= 3) {
+                               printk(KERN_INFO
+                                      " \\--> Skipped (too many) !\n");
+                               continue;
+                       }
+                       /* Handles ISA memory hole space here */
+                       if (pci_addr == 0) {
+                               isa_mb = cpu_addr;
+                               isa_hole = memno;
+                               if (primary || isa_mem_base == 0)
+                                       isa_mem_base = cpu_addr;
+                               hose->isa_mem_phys = cpu_addr;
+                               hose->isa_mem_size = size;
+                       }
+
+                       /* We get the PCI/Mem offset from the first range or
+                        * the, current one if the offset came from an ISA
+                        * hole. If they don't match, bugger.
+                        */
+                       if (memno == 0 ||
+                           (isa_hole >= 0 && pci_addr != 0 &&
+                            hose->pci_mem_offset == isa_mb))
+                               hose->pci_mem_offset = cpu_addr - pci_addr;
+                       else if (pci_addr != 0 &&
+                                hose->pci_mem_offset != cpu_addr - pci_addr) {
+                               printk(KERN_INFO
+                                      " \\--> Skipped (offset mismatch) !\n");
+                               continue;
+                       }
+
+                       /* Build resource */
+                       res = &hose->mem_resources[memno++];
+                       res->flags = IORESOURCE_MEM;
+                       if (pci_space & 0x40000000)
+                               res->flags |= IORESOURCE_PREFETCH;
+                       res->start = cpu_addr;
+                       break;
+               }
+               if (res != NULL) {
+                       res->name = dev->full_name;
+                       res->end = res->start + size - 1;
+                       res->parent = NULL;
+                       res->sibling = NULL;
+                       res->child = NULL;
+               }
+       }
+
+       /* If there's an ISA hole and the pci_mem_offset is -not- matching
+        * the ISA hole offset, then we need to remove the ISA hole from
+        * the resource list for that brige
+        */
+       if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) {
+               unsigned int next = isa_hole + 1;
+               printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb);
+               if (next < memno)
+                       memmove(&hose->mem_resources[isa_hole],
+                               &hose->mem_resources[next],
+                               sizeof(struct resource) * (memno - next));
+               hose->mem_resources[--memno].flags = 0;
+       }
+}
+
+/* Decide whether to display the domain number in /proc */
+int pci_proc_domain(struct pci_bus *bus)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+
+       if (!(pci_flags & PCI_ENABLE_PROC_DOMAINS))
+               return 0;
+       if (pci_flags & PCI_COMPAT_DOMAIN_0)
+               return hose->global_number != 0;
+       return 1;
+}
+
+void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
+                            struct resource *res)
+{
+       resource_size_t offset = 0, mask = (resource_size_t)-1;
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+
+       if (!hose)
+               return;
+       if (res->flags & IORESOURCE_IO) {
+               offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+               mask = 0xffffffffu;
+       } else if (res->flags & IORESOURCE_MEM)
+               offset = hose->pci_mem_offset;
+
+       region->start = (res->start - offset) & mask;
+       region->end = (res->end - offset) & mask;
+}
+EXPORT_SYMBOL(pcibios_resource_to_bus);
+
+void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
+                            struct pci_bus_region *region)
+{
+       resource_size_t offset = 0, mask = (resource_size_t)-1;
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+
+       if (!hose)
+               return;
+       if (res->flags & IORESOURCE_IO) {
+               offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+               mask = 0xffffffffu;
+       } else if (res->flags & IORESOURCE_MEM)
+               offset = hose->pci_mem_offset;
+       res->start = (region->start + offset) & mask;
+       res->end = (region->end + offset) & mask;
+}
+EXPORT_SYMBOL(pcibios_bus_to_resource);
+
+/* Fixup a bus resource into a linux resource */
+static void __devinit fixup_resource(struct resource *res, struct pci_dev *dev)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       resource_size_t offset = 0, mask = (resource_size_t)-1;
+
+       if (res->flags & IORESOURCE_IO) {
+               offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+               mask = 0xffffffffu;
+       } else if (res->flags & IORESOURCE_MEM)
+               offset = hose->pci_mem_offset;
+
+       res->start = (res->start + offset) & mask;
+       res->end = (res->end + offset) & mask;
+}
+
+/* This header fixup will do the resource fixup for all devices as they are
+ * probed, but not for bridge ranges
+ */
+static void __devinit pcibios_fixup_resources(struct pci_dev *dev)
+{
+       struct pci_controller *hose = pci_bus_to_host(dev->bus);
+       int i;
+
+       if (!hose) {
+               printk(KERN_ERR "No host bridge for PCI dev %s !\n",
+                      pci_name(dev));
+               return;
+       }
+       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+               struct resource *res = dev->resource + i;
+               if (!res->flags)
+                       continue;
+               /* On platforms that have PCI_PROBE_ONLY set, we don't
+                * consider 0 as an unassigned BAR value. It's technically
+                * a valid value, but linux doesn't like it... so when we can
+                * re-assign things, we do so, but if we can't, we keep it
+                * around and hope for the best...
+                */
+               if (res->start == 0 && !(pci_flags & PCI_PROBE_ONLY)) {
+                       pr_debug("PCI:%s Resource %d %016llx-%016llx [%x]" \
+                                                       "is unassigned\n",
+                                pci_name(dev), i,
+                                (unsigned long long)res->start,
+                                (unsigned long long)res->end,
+                                (unsigned int)res->flags);
+                       res->end -= res->start;
+                       res->start = 0;
+                       res->flags |= IORESOURCE_UNSET;
+                       continue;
+               }
+
+               pr_debug("PCI:%s Resource %d %016llx-%016llx [%x] fixup...\n",
+                        pci_name(dev), i,
+                        (unsigned long long)res->start,\
+                        (unsigned long long)res->end,
+                        (unsigned int)res->flags);
+
+               fixup_resource(res, dev);
+
+               pr_debug("PCI:%s            %016llx-%016llx\n",
+                        pci_name(dev),
+                        (unsigned long long)res->start,
+                        (unsigned long long)res->end);
+       }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources);
+
+/* This function tries to figure out if a bridge resource has been initialized
+ * by the firmware or not. It doesn't have to be absolutely bullet proof, but
+ * things go more smoothly when it gets it right. It should covers cases such
+ * as Apple "closed" bridge resources and bare-metal pSeries unassigned bridges
+ */
+static int __devinit pcibios_uninitialized_bridge_resource(struct pci_bus *bus,
+                                                          struct resource *res)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       struct pci_dev *dev = bus->self;
+       resource_size_t offset;
+       u16 command;
+       int i;
+
+       /* We don't do anything if PCI_PROBE_ONLY is set */
+       if (pci_flags & PCI_PROBE_ONLY)
+               return 0;
+
+       /* Job is a bit different between memory and IO */
+       if (res->flags & IORESOURCE_MEM) {
+               /* If the BAR is non-0 (res != pci_mem_offset) then it's
+                * probably been initialized by somebody
+                */
+               if (res->start != hose->pci_mem_offset)
+                       return 0;
+
+               /* The BAR is 0, let's check if memory decoding is enabled on
+                * the bridge. If not, we consider it unassigned
+                */
+               pci_read_config_word(dev, PCI_COMMAND, &command);
+               if ((command & PCI_COMMAND_MEMORY) == 0)
+                       return 1;
+
+               /* Memory decoding is enabled and the BAR is 0. If any of
+                * the bridge resources covers that starting address (0 then
+                * it's good enough for us for memory
+                */
+               for (i = 0; i < 3; i++) {
+                       if ((hose->mem_resources[i].flags & IORESOURCE_MEM) &&
+                          hose->mem_resources[i].start == hose->pci_mem_offset)
+                               return 0;
+               }
+
+               /* Well, it starts at 0 and we know it will collide so we may as
+                * well consider it as unassigned. That covers the Apple case.
+                */
+               return 1;
+       } else {
+               /* If the BAR is non-0, then we consider it assigned */
+               offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+               if (((res->start - offset) & 0xfffffffful) != 0)
+                       return 0;
+
+               /* Here, we are a bit different than memory as typically IO
+                * space starting at low addresses -is- valid. What we do
+                * instead if that we consider as unassigned anything that
+                * doesn't have IO enabled in the PCI command register,
+                * and that's it.
+                */
+               pci_read_config_word(dev, PCI_COMMAND, &command);
+               if (command & PCI_COMMAND_IO)
+                       return 0;
+
+               /* It's starting at 0 and IO is disabled in the bridge, consider
+                * it unassigned
+                */
+               return 1;
+       }
+}
+
+/* Fixup resources of a PCI<->PCI bridge */
+static void __devinit pcibios_fixup_bridge(struct pci_bus *bus)
+{
+       struct resource *res;
+       int i;
+
+       struct pci_dev *dev = bus->self;
+
+       for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
+               res = bus->resource[i];
+               if (!res)
+                       continue;
+               if (!res->flags)
+                       continue;
+               if (i >= 3 && bus->self->transparent)
+                       continue;
+
+               pr_debug("PCI:%s Bus rsrc %d %016llx-%016llx [%x] fixup...\n",
+                        pci_name(dev), i,
+                        (unsigned long long)res->start,\
+                        (unsigned long long)res->end,
+                        (unsigned int)res->flags);
+
+               /* Perform fixup */
+               fixup_resource(res, dev);
+
+               /* Try to detect uninitialized P2P bridge resources,
+                * and clear them out so they get re-assigned later
+                */
+               if (pcibios_uninitialized_bridge_resource(bus, res)) {
+                       res->flags = 0;
+                       pr_debug("PCI:%s            (unassigned)\n",
+                                                               pci_name(dev));
+               } else {
+                       pr_debug("PCI:%s            %016llx-%016llx\n",
+                                pci_name(dev),
+                                (unsigned long long)res->start,
+                                (unsigned long long)res->end);
+               }
+       }
+}
+
+void __devinit pcibios_setup_bus_self(struct pci_bus *bus)
+{
+       /* Fix up the bus resources for P2P bridges */
+       if (bus->self != NULL)
+               pcibios_fixup_bridge(bus);
+}
+
+void __devinit pcibios_setup_bus_devices(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       pr_debug("PCI: Fixup bus devices %d (%s)\n",
+                bus->number, bus->self ? pci_name(bus->self) : "PHB");
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               struct dev_archdata *sd = &dev->dev.archdata;
+
+               /* Setup OF node pointer in archdata */
+               sd->of_node = pci_device_to_OF_node(dev);
+
+               /* Fixup NUMA node as it may not be setup yet by the generic
+                * code and is needed by the DMA init
+                */
+               set_dev_node(&dev->dev, pcibus_to_node(dev->bus));
+
+               /* Hook up default DMA ops */
+               sd->dma_ops = pci_dma_ops;
+               sd->dma_data = (void *)PCI_DRAM_OFFSET;
+
+               /* Read default IRQs and fixup if necessary */
+               pci_read_irq_line(dev);
+       }
+}
+
+void __devinit pcibios_fixup_bus(struct pci_bus *bus)
+{
+       /* When called from the generic PCI probe, read PCI<->PCI bridge
+        * bases. This is -not- called when generating the PCI tree from
+        * the OF device-tree.
+        */
+       if (bus->self != NULL)
+               pci_read_bridge_bases(bus);
+
+       /* Now fixup the bus bus */
+       pcibios_setup_bus_self(bus);
+
+       /* Now fixup devices on that bus */
+       pcibios_setup_bus_devices(bus);
+}
+EXPORT_SYMBOL(pcibios_fixup_bus);
+
+static int skip_isa_ioresource_align(struct pci_dev *dev)
+{
+       if ((pci_flags & PCI_CAN_SKIP_ISA_ALIGN) &&
+           !(dev->bus->bridge_ctl & PCI_BRIDGE_CTL_ISA))
+               return 1;
+       return 0;
+}
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ *
+ * Why? Because some silly external IO cards only decode
+ * the low 10 bits of the IO address. The 0x00-0xff region
+ * is reserved for motherboard devices that decode all 16
+ * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
+ * but we want to try to avoid allocating at 0x2900-0x2bff
+ * which might have be mirrored at 0x0100-0x03ff..
+ */
+void pcibios_align_resource(void *data, struct resource *res,
+                               resource_size_t size, resource_size_t align)
+{
+       struct pci_dev *dev = data;
+
+       if (res->flags & IORESOURCE_IO) {
+               resource_size_t start = res->start;
+
+               if (skip_isa_ioresource_align(dev))
+                       return;
+               if (start & 0x300) {
+                       start = (start + 0x3ff) & ~0x3ff;
+                       res->start = start;
+               }
+       }
+}
+EXPORT_SYMBOL(pcibios_align_resource);
+
+/*
+ * Reparent resource children of pr that conflict with res
+ * under res, and make res replace those children.
+ */
+static int __init reparent_resources(struct resource *parent,
+                                    struct resource *res)
+{
+       struct resource *p, **pp;
+       struct resource **firstpp = NULL;
+
+       for (pp = &parent->child; (p = *pp) != NULL; pp = &p->sibling) {
+               if (p->end < res->start)
+                       continue;
+               if (res->end < p->start)
+                       break;
+               if (p->start < res->start || p->end > res->end)
+                       return -1;      /* not completely contained */
+               if (firstpp == NULL)
+                       firstpp = pp;
+       }
+       if (firstpp == NULL)
+               return -1;      /* didn't find any conflicting entries? */
+       res->parent = parent;
+       res->child = *firstpp;
+       res->sibling = *pp;
+       *firstpp = res;
+       *pp = NULL;
+       for (p = res->child; p != NULL; p = p->sibling) {
+               p->parent = res;
+               pr_debug("PCI: Reparented %s [%llx..%llx] under %s\n",
+                        p->name,
+                        (unsigned long long)p->start,
+                        (unsigned long long)p->end, res->name);
+       }
+       return 0;
+}
+
+/*
+ *  Handle resources of PCI devices.  If the world were perfect, we could
+ *  just allocate all the resource regions and do nothing more.  It isn't.
+ *  On the other hand, we cannot just re-allocate all devices, as it would
+ *  require us to know lots of host bridge internals.  So we attempt to
+ *  keep as much of the original configuration as possible, but tweak it
+ *  when it's found to be wrong.
+ *
+ *  Known BIOS problems we have to work around:
+ *     - I/O or memory regions not configured
+ *     - regions configured, but not enabled in the command register
+ *     - bogus I/O addresses above 64K used
+ *     - expansion ROMs left enabled (this may sound harmless, but given
+ *       the fact the PCI specs explicitly allow address decoders to be
+ *       shared between expansion ROMs and other resource regions, it's
+ *       at least dangerous)
+ *
+ *  Our solution:
+ *     (1) Allocate resources for all buses behind PCI-to-PCI bridges.
+ *         This gives us fixed barriers on where we can allocate.
+ *     (2) Allocate resources for all enabled devices.  If there is
+ *         a collision, just mark the resource as unallocated. Also
+ *         disable expansion ROMs during this step.
+ *     (3) Try to allocate resources for disabled devices.  If the
+ *         resources were assigned correctly, everything goes well,
+ *         if they weren't, they won't disturb allocation of other
+ *         resources.
+ *     (4) Assign new addresses to resources which were either
+ *         not configured at all or misconfigured.  If explicitly
+ *         requested by the user, configure expansion ROM address
+ *         as well.
+ */
+
+void pcibios_allocate_bus_resources(struct pci_bus *bus)
+{
+       struct pci_bus *b;
+       int i;
+       struct resource *res, *pr;
+
+       pr_debug("PCI: Allocating bus resources for %04x:%02x...\n",
+                pci_domain_nr(bus), bus->number);
+
+       for (i = 0; i < PCI_BUS_NUM_RESOURCES; ++i) {
+               res = bus->resource[i];
+               if (!res || !res->flags
+                   || res->start > res->end || res->parent)
+                       continue;
+               if (bus->parent == NULL)
+                       pr = (res->flags & IORESOURCE_IO) ?
+                               &ioport_resource : &iomem_resource;
+               else {
+                       /* Don't bother with non-root busses when
+                        * re-assigning all resources. We clear the
+                        * resource flags as if they were colliding
+                        * and as such ensure proper re-allocation
+                        * later.
+                        */
+                       if (pci_flags & PCI_REASSIGN_ALL_RSRC)
+                               goto clear_resource;
+                       pr = pci_find_parent_resource(bus->self, res);
+                       if (pr == res) {
+                               /* this happens when the generic PCI
+                                * code (wrongly) decides that this
+                                * bridge is transparent  -- paulus
+                                */
+                               continue;
+                       }
+               }
+
+               pr_debug("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx "
+                        "[0x%x], parent %p (%s)\n",
+                        bus->self ? pci_name(bus->self) : "PHB",
+                        bus->number, i,
+                        (unsigned long long)res->start,
+                        (unsigned long long)res->end,
+                        (unsigned int)res->flags,
+                        pr, (pr && pr->name) ? pr->name : "nil");
+
+               if (pr && !(pr->flags & IORESOURCE_UNSET)) {
+                       if (request_resource(pr, res) == 0)
+                               continue;
+                       /*
+                        * Must be a conflict with an existing entry.
+                        * Move that entry (or entries) under the
+                        * bridge resource and try again.
+                        */
+                       if (reparent_resources(pr, res) == 0)
+                               continue;
+               }
+               printk(KERN_WARNING "PCI: Cannot allocate resource region "
+                      "%d of PCI bridge %d, will remap\n", i, bus->number);
+clear_resource:
+               res->flags = 0;
+       }
+
+       list_for_each_entry(b, &bus->children, node)
+               pcibios_allocate_bus_resources(b);
+}
+
+static inline void __devinit alloc_resource(struct pci_dev *dev, int idx)
+{
+       struct resource *pr, *r = &dev->resource[idx];
+
+       pr_debug("PCI: Allocating %s: Resource %d: %016llx..%016llx [%x]\n",
+                pci_name(dev), idx,
+                (unsigned long long)r->start,
+                (unsigned long long)r->end,
+                (unsigned int)r->flags);
+
+       pr = pci_find_parent_resource(dev, r);
+       if (!pr || (pr->flags & IORESOURCE_UNSET) ||
+           request_resource(pr, r) < 0) {
+               printk(KERN_WARNING "PCI: Cannot allocate resource region %d"
+                      " of device %s, will remap\n", idx, pci_name(dev));
+               if (pr)
+                       pr_debug("PCI:  parent is %p: %016llx-%016llx [%x]\n",
+                                pr,
+                                (unsigned long long)pr->start,
+                                (unsigned long long)pr->end,
+                                (unsigned int)pr->flags);
+               /* We'll assign a new address later */
+               r->flags |= IORESOURCE_UNSET;
+               r->end -= r->start;
+               r->start = 0;
+       }
+}
+
+static void __init pcibios_allocate_resources(int pass)
+{
+       struct pci_dev *dev = NULL;
+       int idx, disabled;
+       u16 command;
+       struct resource *r;
+
+       for_each_pci_dev(dev) {
+               pci_read_config_word(dev, PCI_COMMAND, &command);
+               for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) {
+                       r = &dev->resource[idx];
+                       if (r->parent)          /* Already allocated */
+                               continue;
+                       if (!r->flags || (r->flags & IORESOURCE_UNSET))
+                               continue;       /* Not assigned at all */
+                       /* We only allocate ROMs on pass 1 just in case they
+                        * have been screwed up by firmware
+                        */
+                       if (idx == PCI_ROM_RESOURCE)
+                               disabled = 1;
+                       if (r->flags & IORESOURCE_IO)
+                               disabled = !(command & PCI_COMMAND_IO);
+                       else
+                               disabled = !(command & PCI_COMMAND_MEMORY);
+                       if (pass == disabled)
+                               alloc_resource(dev, idx);
+               }
+               if (pass)
+                       continue;
+               r = &dev->resource[PCI_ROM_RESOURCE];
+               if (r->flags) {
+                       /* Turn the ROM off, leave the resource region,
+                        * but keep it unregistered.
+                        */
+                       u32 reg;
+                       pci_read_config_dword(dev, dev->rom_base_reg, &reg);
+                       if (reg & PCI_ROM_ADDRESS_ENABLE) {
+                               pr_debug("PCI: Switching off ROM of %s\n",
+                                        pci_name(dev));
+                               r->flags &= ~IORESOURCE_ROM_ENABLE;
+                               pci_write_config_dword(dev, dev->rom_base_reg,
+                                               reg & ~PCI_ROM_ADDRESS_ENABLE);
+                       }
+               }
+       }
+}
+
+static void __init pcibios_reserve_legacy_regions(struct pci_bus *bus)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       resource_size_t offset;
+       struct resource *res, *pres;
+       int i;
+
+       pr_debug("Reserving legacy ranges for domain %04x\n",
+                                                       pci_domain_nr(bus));
+
+       /* Check for IO */
+       if (!(hose->io_resource.flags & IORESOURCE_IO))
+               goto no_io;
+       offset = (unsigned long)hose->io_base_virt - _IO_BASE;
+       res = kzalloc(sizeof(struct resource), GFP_KERNEL);
+       BUG_ON(res == NULL);
+       res->name = "Legacy IO";
+       res->flags = IORESOURCE_IO;
+       res->start = offset;
+       res->end = (offset + 0xfff) & 0xfffffffful;
+       pr_debug("Candidate legacy IO: %pR\n", res);
+       if (request_resource(&hose->io_resource, res)) {
+               printk(KERN_DEBUG
+                      "PCI %04x:%02x Cannot reserve Legacy IO %pR\n",
+                      pci_domain_nr(bus), bus->number, res);
+               kfree(res);
+       }
+
+ no_io:
+       /* Check for memory */
+       offset = hose->pci_mem_offset;
+       pr_debug("hose mem offset: %016llx\n", (unsigned long long)offset);
+       for (i = 0; i < 3; i++) {
+               pres = &hose->mem_resources[i];
+               if (!(pres->flags & IORESOURCE_MEM))
+                       continue;
+               pr_debug("hose mem res: %pR\n", pres);
+               if ((pres->start - offset) <= 0xa0000 &&
+                   (pres->end - offset) >= 0xbffff)
+                       break;
+       }
+       if (i >= 3)
+               return;
+       res = kzalloc(sizeof(struct resource), GFP_KERNEL);
+       BUG_ON(res == NULL);
+       res->name = "Legacy VGA memory";
+       res->flags = IORESOURCE_MEM;
+       res->start = 0xa0000 + offset;
+       res->end = 0xbffff + offset;
+       pr_debug("Candidate VGA memory: %pR\n", res);
+       if (request_resource(pres, res)) {
+               printk(KERN_DEBUG
+                      "PCI %04x:%02x Cannot reserve VGA memory %pR\n",
+                      pci_domain_nr(bus), bus->number, res);
+               kfree(res);
+       }
+}
+
+void __init pcibios_resource_survey(void)
+{
+       struct pci_bus *b;
+
+       /* Allocate and assign resources. If we re-assign everything, then
+        * we skip the allocate phase
+        */
+       list_for_each_entry(b, &pci_root_buses, node)
+               pcibios_allocate_bus_resources(b);
+
+       if (!(pci_flags & PCI_REASSIGN_ALL_RSRC)) {
+               pcibios_allocate_resources(0);
+               pcibios_allocate_resources(1);
+       }
+
+       /* Before we start assigning unassigned resource, we try to reserve
+        * the low IO area and the VGA memory area if they intersect the
+        * bus available resources to avoid allocating things on top of them
+        */
+       if (!(pci_flags & PCI_PROBE_ONLY)) {
+               list_for_each_entry(b, &pci_root_buses, node)
+                       pcibios_reserve_legacy_regions(b);
+       }
+
+       /* Now, if the platform didn't decide to blindly trust the firmware,
+        * we proceed to assigning things that were left unassigned
+        */
+       if (!(pci_flags & PCI_PROBE_ONLY)) {
+               pr_debug("PCI: Assigning unassigned resources...\n");
+               pci_assign_unassigned_resources();
+       }
+}
+
+#ifdef CONFIG_HOTPLUG
+
+/* This is used by the PCI hotplug driver to allocate resource
+ * of newly plugged busses. We can try to consolidate with the
+ * rest of the code later, for now, keep it as-is as our main
+ * resource allocation function doesn't deal with sub-trees yet.
+ */
+void __devinit pcibios_claim_one_bus(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+       struct pci_bus *child_bus;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               int i;
+
+               for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+                       struct resource *r = &dev->resource[i];
+
+                       if (r->parent || !r->start || !r->flags)
+                               continue;
+
+                       pr_debug("PCI: Claiming %s: "
+                                "Resource %d: %016llx..%016llx [%x]\n",
+                                pci_name(dev), i,
+                                (unsigned long long)r->start,
+                                (unsigned long long)r->end,
+                                (unsigned int)r->flags);
+
+                       pci_claim_resource(dev, i);
+               }
+       }
+
+       list_for_each_entry(child_bus, &bus->children, node)
+               pcibios_claim_one_bus(child_bus);
+}
+EXPORT_SYMBOL_GPL(pcibios_claim_one_bus);
+
+
+/* pcibios_finish_adding_to_bus
+ *
+ * This is to be called by the hotplug code after devices have been
+ * added to a bus, this include calling it for a PHB that is just
+ * being added
+ */
+void pcibios_finish_adding_to_bus(struct pci_bus *bus)
+{
+       pr_debug("PCI: Finishing adding to hotplug bus %04x:%02x\n",
+                pci_domain_nr(bus), bus->number);
+
+       /* Allocate bus and devices resources */
+       pcibios_allocate_bus_resources(bus);
+       pcibios_claim_one_bus(bus);
+
+       /* Add new devices to global lists.  Register in proc, sysfs. */
+       pci_bus_add_devices(bus);
+
+       /* Fixup EEH */
+       eeh_add_device_tree_late(bus);
+}
+EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus);
+
+#endif /* CONFIG_HOTPLUG */
+
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+       return pci_enable_resources(dev, mask);
+}
+
+void __devinit pcibios_setup_phb_resources(struct pci_controller *hose)
+{
+       struct pci_bus *bus = hose->bus;
+       struct resource *res;
+       int i;
+
+       /* Hookup PHB IO resource */
+       bus->resource[0] = res = &hose->io_resource;
+
+       if (!res->flags) {
+               printk(KERN_WARNING "PCI: I/O resource not set for host"
+                      " bridge %s (domain %d)\n",
+                      hose->dn->full_name, hose->global_number);
+               /* Workaround for lack of IO resource only on 32-bit */
+               res->start = (unsigned long)hose->io_base_virt - isa_io_base;
+               res->end = res->start + IO_SPACE_LIMIT;
+               res->flags = IORESOURCE_IO;
+       }
+
+       pr_debug("PCI: PHB IO resource    = %016llx-%016llx [%lx]\n",
+                (unsigned long long)res->start,
+                (unsigned long long)res->end,
+                (unsigned long)res->flags);
+
+       /* Hookup PHB Memory resources */
+       for (i = 0; i < 3; ++i) {
+               res = &hose->mem_resources[i];
+               if (!res->flags) {
+                       if (i > 0)
+                               continue;
+                       printk(KERN_ERR "PCI: Memory resource 0 not set for "
+                              "host bridge %s (domain %d)\n",
+                              hose->dn->full_name, hose->global_number);
+
+                       /* Workaround for lack of MEM resource only on 32-bit */
+                       res->start = hose->pci_mem_offset;
+                       res->end = (resource_size_t)-1LL;
+                       res->flags = IORESOURCE_MEM;
+
+               }
+               bus->resource[i+1] = res;
+
+               pr_debug("PCI: PHB MEM resource %d = %016llx-%016llx [%lx]\n",
+                       i, (unsigned long long)res->start,
+                       (unsigned long long)res->end,
+                       (unsigned long)res->flags);
+       }
+
+       pr_debug("PCI: PHB MEM offset     = %016llx\n",
+                (unsigned long long)hose->pci_mem_offset);
+       pr_debug("PCI: PHB IO  offset     = %08lx\n",
+                (unsigned long)hose->io_base_virt - _IO_BASE);
+}
+
+/*
+ * Null PCI config access functions, for the case when we can't
+ * find a hose.
+ */
+#define NULL_PCI_OP(rw, size, type)                                    \
+static int                                                             \
+null_##rw##_config_##size(struct pci_dev *dev, int offset, type val)   \
+{                                                                      \
+       return PCIBIOS_DEVICE_NOT_FOUND;                                \
+}
+
+static int
+null_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
+                int len, u32 *val)
+{
+       return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+static int
+null_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
+                 int len, u32 val)
+{
+       return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+static struct pci_ops null_pci_ops = {
+       .read = null_read_config,
+       .write = null_write_config,
+};
+
+/*
+ * These functions are used early on before PCI scanning is done
+ * and all of the pci_dev and pci_bus structures have been created.
+ */
+static struct pci_bus *
+fake_pci_bus(struct pci_controller *hose, int busnr)
+{
+       static struct pci_bus bus;
+
+       if (!hose)
+               printk(KERN_ERR "Can't find hose for PCI bus %d!\n", busnr);
+
+       bus.number = busnr;
+       bus.sysdata = hose;
+       bus.ops = hose ? hose->ops : &null_pci_ops;
+       return &bus;
+}
+
+#define EARLY_PCI_OP(rw, size, type)                                   \
+int early_##rw##_config_##size(struct pci_controller *hose, int bus,   \
+                              int devfn, int offset, type value)       \
+{                                                                      \
+       return pci_bus_##rw##_config_##size(fake_pci_bus(hose, bus),    \
+                                           devfn, offset, value);      \
+}
+
+EARLY_PCI_OP(read, byte, u8 *)
+EARLY_PCI_OP(read, word, u16 *)
+EARLY_PCI_OP(read, dword, u32 *)
+EARLY_PCI_OP(write, byte, u8)
+EARLY_PCI_OP(write, word, u16)
+EARLY_PCI_OP(write, dword, u32)
+
+int early_find_capability(struct pci_controller *hose, int bus, int devfn,
+                         int cap)
+{
+       return pci_bus_find_capability(fake_pci_bus(hose, bus), devfn, cap);
+}
diff --git a/arch/microblaze/pci/pci_32.c b/arch/microblaze/pci/pci_32.c
new file mode 100644 (file)
index 0000000..7e0c94f
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Common pmac/prep/chrp pci routines. -- Cort
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bootmem.h>
+#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/of.h>
+
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/pci-bridge.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#undef DEBUG
+
+unsigned long isa_io_base;
+unsigned long pci_dram_offset;
+int pcibios_assign_bus_offset = 1;
+
+static u8 *pci_to_OF_bus_map;
+
+/* By default, we don't re-assign bus numbers. We do this only on
+ * some pmacs
+ */
+static int pci_assign_all_buses;
+
+static int pci_bus_count;
+
+/*
+ * Functions below are used on OpenFirmware machines.
+ */
+static void
+make_one_node_map(struct device_node *node, u8 pci_bus)
+{
+       const int *bus_range;
+       int len;
+
+       if (pci_bus >= pci_bus_count)
+               return;
+       bus_range = of_get_property(node, "bus-range", &len);
+       if (bus_range == NULL || len < 2 * sizeof(int)) {
+               printk(KERN_WARNING "Can't get bus-range for %s, "
+                      "assuming it starts at 0\n", node->full_name);
+               pci_to_OF_bus_map[pci_bus] = 0;
+       } else
+               pci_to_OF_bus_map[pci_bus] = bus_range[0];
+
+       for_each_child_of_node(node, node) {
+               struct pci_dev *dev;
+               const unsigned int *class_code, *reg;
+
+               class_code = of_get_property(node, "class-code", NULL);
+               if (!class_code ||
+                       ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
+                       (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
+                       continue;
+               reg = of_get_property(node, "reg", NULL);
+               if (!reg)
+                       continue;
+               dev = pci_get_bus_and_slot(pci_bus, ((reg[0] >> 8) & 0xff));
+               if (!dev || !dev->subordinate) {
+                       pci_dev_put(dev);
+                       continue;
+               }
+               make_one_node_map(node, dev->subordinate->number);
+               pci_dev_put(dev);
+       }
+}
+
+void
+pcibios_make_OF_bus_map(void)
+{
+       int i;
+       struct pci_controller *hose, *tmp;
+       struct property *map_prop;
+       struct device_node *dn;
+
+       pci_to_OF_bus_map = kmalloc(pci_bus_count, GFP_KERNEL);
+       if (!pci_to_OF_bus_map) {
+               printk(KERN_ERR "Can't allocate OF bus map !\n");
+               return;
+       }
+
+       /* We fill the bus map with invalid values, that helps
+        * debugging.
+        */
+       for (i = 0; i < pci_bus_count; i++)
+               pci_to_OF_bus_map[i] = 0xff;
+
+       /* For each hose, we begin searching bridges */
+       list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
+               struct device_node *node = hose->dn;
+
+               if (!node)
+                       continue;
+               make_one_node_map(node, hose->first_busno);
+       }
+       dn = of_find_node_by_path("/");
+       map_prop = of_find_property(dn, "pci-OF-bus-map", NULL);
+       if (map_prop) {
+               BUG_ON(pci_bus_count > map_prop->length);
+               memcpy(map_prop->value, pci_to_OF_bus_map, pci_bus_count);
+       }
+       of_node_put(dn);
+#ifdef DEBUG
+       printk(KERN_INFO "PCI->OF bus map:\n");
+       for (i = 0; i < pci_bus_count; i++) {
+               if (pci_to_OF_bus_map[i] == 0xff)
+                       continue;
+               printk(KERN_INFO "%d -> %d\n", i, pci_to_OF_bus_map[i]);
+       }
+#endif
+}
+
+typedef int (*pci_OF_scan_iterator)(struct device_node *node, void *data);
+
+static struct device_node *scan_OF_pci_childs(struct device_node *parent,
+                                       pci_OF_scan_iterator filter, void *data)
+{
+       struct device_node *node;
+       struct device_node *sub_node;
+
+       for_each_child_of_node(parent, node) {
+               const unsigned int *class_code;
+
+               if (filter(node, data)) {
+                       of_node_put(node);
+                       return node;
+               }
+
+               /* For PCI<->PCI bridges or CardBus bridges, we go down
+                * Note: some OFs create a parent node "multifunc-device" as
+                * a fake root for all functions of a multi-function device,
+                * we go down them as well.
+                */
+               class_code = of_get_property(node, "class-code", NULL);
+               if ((!class_code ||
+                       ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
+                       (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) &&
+                       strcmp(node->name, "multifunc-device"))
+                       continue;
+               sub_node = scan_OF_pci_childs(node, filter, data);
+               if (sub_node) {
+                       of_node_put(node);
+                       return sub_node;
+               }
+       }
+       return NULL;
+}
+
+static struct device_node *scan_OF_for_pci_dev(struct device_node *parent,
+                                              unsigned int devfn)
+{
+       struct device_node *np, *cnp;
+       const u32 *reg;
+       unsigned int psize;
+
+       for_each_child_of_node(parent, np) {
+               reg = of_get_property(np, "reg", &psize);
+               if (reg && psize >= 4 && ((reg[0] >> 8) & 0xff) == devfn)
+                       return np;
+
+               /* Note: some OFs create a parent node "multifunc-device" as
+                * a fake root for all functions of a multi-function device,
+                * we go down them as well. */
+               if (!strcmp(np->name, "multifunc-device")) {
+                       cnp = scan_OF_for_pci_dev(np, devfn);
+                       if (cnp)
+                               return cnp;
+               }
+       }
+       return NULL;
+}
+
+
+static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus)
+{
+       struct device_node *parent, *np;
+
+       /* Are we a root bus ? */
+       if (bus->self == NULL || bus->parent == NULL) {
+               struct pci_controller *hose = pci_bus_to_host(bus);
+               if (hose == NULL)
+                       return NULL;
+               return of_node_get(hose->dn);
+       }
+
+       /* not a root bus, we need to get our parent */
+       parent = scan_OF_for_pci_bus(bus->parent);
+       if (parent == NULL)
+               return NULL;
+
+       /* now iterate for children for a match */
+       np = scan_OF_for_pci_dev(parent, bus->self->devfn);
+       of_node_put(parent);
+
+       return np;
+}
+
+/*
+ * Scans the OF tree for a device node matching a PCI device
+ */
+struct device_node *
+pci_busdev_to_OF_node(struct pci_bus *bus, int devfn)
+{
+       struct device_node *parent, *np;
+
+       pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn);
+       parent = scan_OF_for_pci_bus(bus);
+       if (parent == NULL)
+               return NULL;
+       pr_debug(" parent is %s\n", parent ? parent->full_name : "<NULL>");
+       np = scan_OF_for_pci_dev(parent, devfn);
+       of_node_put(parent);
+       pr_debug(" result is %s\n", np ? np->full_name : "<NULL>");
+
+       /* XXX most callers don't release the returned node
+        * mostly because ppc64 doesn't increase the refcount,
+        * we need to fix that.
+        */
+       return np;
+}
+EXPORT_SYMBOL(pci_busdev_to_OF_node);
+
+struct device_node*
+pci_device_to_OF_node(struct pci_dev *dev)
+{
+       return pci_busdev_to_OF_node(dev->bus, dev->devfn);
+}
+EXPORT_SYMBOL(pci_device_to_OF_node);
+
+static int
+find_OF_pci_device_filter(struct device_node *node, void *data)
+{
+       return ((void *)node == data);
+}
+
+/*
+ * Returns the PCI device matching a given OF node
+ */
+int
+pci_device_from_OF_node(struct device_node *node, u8 *bus, u8 *devfn)
+{
+       const unsigned int *reg;
+       struct pci_controller *hose;
+       struct pci_dev *dev = NULL;
+
+       /* Make sure it's really a PCI device */
+       hose = pci_find_hose_for_OF_device(node);
+       if (!hose || !hose->dn)
+               return -ENODEV;
+       if (!scan_OF_pci_childs(hose->dn,
+                       find_OF_pci_device_filter, (void *)node))
+               return -ENODEV;
+       reg = of_get_property(node, "reg", NULL);
+       if (!reg)
+               return -ENODEV;
+       *bus = (reg[0] >> 16) & 0xff;
+       *devfn = ((reg[0] >> 8) & 0xff);
+
+       /* Ok, here we need some tweak. If we have already renumbered
+        * all busses, we can't rely on the OF bus number any more.
+        * the pci_to_OF_bus_map is not enough as several PCI busses
+        * may match the same OF bus number.
+        */
+       if (!pci_to_OF_bus_map)
+               return 0;
+
+       for_each_pci_dev(dev)
+               if (pci_to_OF_bus_map[dev->bus->number] == *bus &&
+                               dev->devfn == *devfn) {
+                       *bus = dev->bus->number;
+                       pci_dev_put(dev);
+                       return 0;
+               }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL(pci_device_from_OF_node);
+
+/* We create the "pci-OF-bus-map" property now so it appears in the
+ * /proc device tree
+ */
+void __init
+pci_create_OF_bus_map(void)
+{
+       struct property *of_prop;
+       struct device_node *dn;
+
+       of_prop = (struct property *) alloc_bootmem(sizeof(struct property) + \
+                                                                        256);
+       if (!of_prop)
+               return;
+       dn = of_find_node_by_path("/");
+       if (dn) {
+               memset(of_prop, -1, sizeof(struct property) + 256);
+               of_prop->name = "pci-OF-bus-map";
+               of_prop->length = 256;
+               of_prop->value = &of_prop[1];
+               prom_add_property(dn, of_prop);
+               of_node_put(dn);
+       }
+}
+
+static void __devinit pcibios_scan_phb(struct pci_controller *hose)
+{
+       struct pci_bus *bus;
+       struct device_node *node = hose->dn;
+       unsigned long io_offset;
+       struct resource *res = &hose->io_resource;
+
+       pr_debug("PCI: Scanning PHB %s\n",
+                node ? node->full_name : "<NO NAME>");
+
+       /* Create an empty bus for the toplevel */
+       bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, hose);
+       if (bus == NULL) {
+               printk(KERN_ERR "Failed to create bus for PCI domain %04x\n",
+                      hose->global_number);
+               return;
+       }
+       bus->secondary = hose->first_busno;
+       hose->bus = bus;
+
+       /* Fixup IO space offset */
+       io_offset = (unsigned long)hose->io_base_virt - isa_io_base;
+       res->start = (res->start + io_offset) & 0xffffffffu;
+       res->end = (res->end + io_offset) & 0xffffffffu;
+
+       /* Wire up PHB bus resources */
+       pcibios_setup_phb_resources(hose);
+
+       /* Scan children */
+       hose->last_busno = bus->subordinate = pci_scan_child_bus(bus);
+}
+
+static int __init pcibios_init(void)
+{
+       struct pci_controller *hose, *tmp;
+       int next_busno = 0;
+
+       printk(KERN_INFO "PCI: Probing PCI hardware\n");
+
+       if (pci_flags & PCI_REASSIGN_ALL_BUS) {
+               printk(KERN_INFO "setting pci_asign_all_busses\n");
+               pci_assign_all_buses = 1;
+       }
+
+       /* Scan all of the recorded PCI controllers.  */
+       list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
+               if (pci_assign_all_buses)
+                       hose->first_busno = next_busno;
+               hose->last_busno = 0xff;
+               pcibios_scan_phb(hose);
+               printk(KERN_INFO "calling pci_bus_add_devices()\n");
+               pci_bus_add_devices(hose->bus);
+               if (pci_assign_all_buses || next_busno <= hose->last_busno)
+                       next_busno = hose->last_busno + \
+                                       pcibios_assign_bus_offset;
+       }
+       pci_bus_count = next_busno;
+
+       /* OpenFirmware based machines need a map of OF bus
+        * numbers vs. kernel bus numbers since we may have to
+        * remap them.
+        */
+       if (pci_assign_all_buses)
+               pcibios_make_OF_bus_map();
+
+       /* Call common code to handle resource allocation */
+       pcibios_resource_survey();
+
+       return 0;
+}
+
+subsys_initcall(pcibios_init);
+
+static struct pci_controller*
+pci_bus_to_hose(int bus)
+{
+       struct pci_controller *hose, *tmp;
+
+       list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
+               if (bus >= hose->first_busno && bus <= hose->last_busno)
+                       return hose;
+       return NULL;
+}
+
+/* Provide information on locations of various I/O regions in physical
+ * memory.  Do this on a per-card basis so that we choose the right
+ * root bridge.
+ * Note that the returned IO or memory base is a physical address
+ */
+
+long sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn)
+{
+       struct pci_controller *hose;
+       long result = -EOPNOTSUPP;
+
+       hose = pci_bus_to_hose(bus);
+       if (!hose)
+               return -ENODEV;
+
+       switch (which) {
+       case IOBASE_BRIDGE_NUMBER:
+               return (long)hose->first_busno;
+       case IOBASE_MEMORY:
+               return (long)hose->pci_mem_offset;
+       case IOBASE_IO:
+               return (long)hose->io_base_phys;
+       case IOBASE_ISA_IO:
+               return (long)isa_io_base;
+       case IOBASE_ISA_MEM:
+               return (long)isa_mem_base;
+       }
+
+       return result;
+}
diff --git a/arch/microblaze/pci/xilinx_pci.c b/arch/microblaze/pci/xilinx_pci.c
new file mode 100644 (file)
index 0000000..7869a41
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * PCI support for Xilinx plbv46_pci soft-core which can be used on
+ * Xilinx Virtex ML410 / ML510 boards.
+ *
+ * Copyright 2009 Roderick Colenbrander
+ * Copyright 2009 Secret Lab Technologies Ltd.
+ *
+ * The pci bridge fixup code was copied from ppc4xx_pci.c and was written
+ * by Benjamin Herrenschmidt.
+ * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/ioport.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#define XPLB_PCI_ADDR 0x10c
+#define XPLB_PCI_DATA 0x110
+#define XPLB_PCI_BUS  0x114
+
+#define PCI_HOST_ENABLE_CMD (PCI_COMMAND_SERR | PCI_COMMAND_PARITY | \
+                               PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY)
+
+static struct of_device_id xilinx_pci_match[] = {
+       { .compatible = "xlnx,plbv46-pci-1.03.a", },
+       {}
+};
+
+/**
+ * xilinx_pci_fixup_bridge - Block Xilinx PHB configuration.
+ */
+static void xilinx_pci_fixup_bridge(struct pci_dev *dev)
+{
+       struct pci_controller *hose;
+       int i;
+
+       if (dev->devfn || dev->bus->self)
+               return;
+
+       hose = pci_bus_to_host(dev->bus);
+       if (!hose)
+               return;
+
+       if (!of_match_node(xilinx_pci_match, hose->dn))
+               return;
+
+       /* Hide the PCI host BARs from the kernel as their content doesn't
+        * fit well in the resource management
+        */
+       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+               dev->resource[i].start = 0;
+               dev->resource[i].end = 0;
+               dev->resource[i].flags = 0;
+       }
+
+       dev_info(&dev->dev, "Hiding Xilinx plb-pci host bridge resources %s\n",
+                pci_name(dev));
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, xilinx_pci_fixup_bridge);
+
+#ifdef DEBUG
+/**
+ * xilinx_pci_exclude_device - Don't do config access for non-root bus
+ *
+ * This is a hack.  Config access to any bus other than bus 0 does not
+ * currently work on the ML510 so we prevent it here.
+ */
+static int
+xilinx_pci_exclude_device(struct pci_controller *hose, u_char bus, u8 devfn)
+{
+       return (bus != 0);
+}
+
+/**
+ * xilinx_early_pci_scan - List pci config space for available devices
+ *
+ * List pci devices in very early phase.
+ */
+void __init xilinx_early_pci_scan(struct pci_controller *hose)
+{
+       u32 bus = 0;
+       u32 val, dev, func, offset;
+
+       /* Currently we have only 2 device connected - up-to 32 devices */
+       for (dev = 0; dev < 2; dev++) {
+               /* List only first function number - up-to 8 functions */
+               for (func = 0; func < 1; func++) {
+                       printk(KERN_INFO "%02x:%02x:%02x", bus, dev, func);
+                       /* read the first 64 standardized bytes */
+                       /* Up-to 192 bytes can be list of capabilities */
+                       for (offset = 0; offset < 64; offset += 4) {
+                               early_read_config_dword(hose, bus,
+                                       PCI_DEVFN(dev, func), offset, &val);
+                               if (offset == 0 && val == 0xFFFFFFFF) {
+                                       printk(KERN_CONT "\nABSENT");
+                                       break;
+                               }
+                               if (!(offset % 0x10))
+                                       printk(KERN_CONT "\n%04x:    ", offset);
+
+                               printk(KERN_CONT "%08x  ", val);
+                       }
+                       printk(KERN_INFO "\n");
+               }
+       }
+}
+#else
+void __init xilinx_early_pci_scan(struct pci_controller *hose)
+{
+}
+#endif
+
+/**
+ * xilinx_pci_init - Find and register a Xilinx PCI host bridge
+ */
+void __init xilinx_pci_init(void)
+{
+       struct pci_controller *hose;
+       struct resource r;
+       void __iomem *pci_reg;
+       struct device_node *pci_node;
+
+       pci_node = of_find_matching_node(NULL, xilinx_pci_match);
+       if (!pci_node)
+               return;
+
+       if (of_address_to_resource(pci_node, 0, &r)) {
+               pr_err("xilinx-pci: cannot resolve base address\n");
+               return;
+       }
+
+       hose = pcibios_alloc_controller(pci_node);
+       if (!hose) {
+               pr_err("xilinx-pci: pcibios_alloc_controller() failed\n");
+               return;
+       }
+
+       /* Setup config space */
+       setup_indirect_pci(hose, r.start + XPLB_PCI_ADDR,
+                          r.start + XPLB_PCI_DATA,
+                          INDIRECT_TYPE_SET_CFG_TYPE);
+
+       /* According to the xilinx plbv46_pci documentation the soft-core starts
+        * a self-init when the bus master enable bit is set. Without this bit
+        * set the pci bus can't be scanned.
+        */
+       early_write_config_word(hose, 0, 0, PCI_COMMAND, PCI_HOST_ENABLE_CMD);
+
+       /* Set the max latency timer to 255 */
+       early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0xff);
+
+       /* Set the max bus number to 255, and bus/subbus no's to 0 */
+       pci_reg = of_iomap(pci_node, 0);
+       out_be32(pci_reg + XPLB_PCI_BUS, 0x000000ff);
+       iounmap(pci_reg);
+
+       /* Register the host bridge with the linux kernel! */
+       pci_process_bridge_OF_ranges(hose, pci_node,
+                                       INDIRECT_TYPE_SET_CFG_TYPE);
+
+       pr_info("xilinx-pci: Registered PCI host bridge\n");
+       xilinx_early_pci_scan(hose);
+}
index 3d102dd..0b51857 100644 (file)
@@ -48,6 +48,7 @@ obj-$(CONFIG_PPC) += setup-bus.o
 obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
 obj-$(CONFIG_X86_VISWS) += setup-irq.o
 obj-$(CONFIG_MN10300) += setup-bus.o
+obj-$(CONFIG_MICROBLAZE) += setup-bus.o
 
 #
 # ACPI Related PCI FW Functions