summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-03-18 16:57:24 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-18 16:57:24 -0700
commit722874465e3cd3268387b96e02220f84b35a8d98 (patch)
tree52f453acb7274ce8e939cf2d5f6ee748b826ef15 /arch
parent31cc1dd344d941358345bd02f24c629dada9b08c (diff)
parent841d6e8c4e969b2cdd80f7216af34d932c41b1a6 (diff)
Merge branch 'for-linus' of git://git.monstr.eu/linux-2.6-microblaze
* '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 ...
Diffstat (limited to 'arch')
-rw-r--r--arch/microblaze/Kconfig64
-rw-r--r--arch/microblaze/Makefile1
-rw-r--r--arch/microblaze/include/asm/device.h4
-rw-r--r--arch/microblaze/include/asm/dma-mapping.h154
-rw-r--r--arch/microblaze/include/asm/io.h31
-rw-r--r--arch/microblaze/include/asm/irq.h37
-rw-r--r--arch/microblaze/include/asm/page.h12
-rw-r--r--arch/microblaze/include/asm/pci-bridge.h195
-rw-r--r--arch/microblaze/include/asm/pci.h178
-rw-r--r--arch/microblaze/include/asm/pgalloc.h2
-rw-r--r--arch/microblaze/include/asm/pgtable.h40
-rw-r--r--arch/microblaze/include/asm/prom.h15
-rw-r--r--arch/microblaze/include/asm/system.h3
-rw-r--r--arch/microblaze/include/asm/tlbflush.h2
-rw-r--r--arch/microblaze/kernel/Makefile2
-rw-r--r--arch/microblaze/kernel/asm-offsets.c1
-rw-r--r--arch/microblaze/kernel/cpu/cache.c211
-rw-r--r--arch/microblaze/kernel/dma.c156
-rw-r--r--arch/microblaze/kernel/entry.S116
-rw-r--r--arch/microblaze/kernel/head.S13
-rw-r--r--arch/microblaze/kernel/irq.c15
-rw-r--r--arch/microblaze/kernel/setup.c45
-rw-r--r--arch/microblaze/mm/Makefile2
-rw-r--r--arch/microblaze/mm/consistent.c246
-rw-r--r--arch/microblaze/mm/init.c39
-rw-r--r--arch/microblaze/mm/pgtable.c2
-rw-r--r--arch/microblaze/pci/Makefile6
-rw-r--r--arch/microblaze/pci/indirect_pci.c163
-rw-r--r--arch/microblaze/pci/iomap.c39
-rw-r--r--arch/microblaze/pci/pci-common.c1642
-rw-r--r--arch/microblaze/pci/pci_32.c430
-rw-r--r--arch/microblaze/pci/xilinx_pci.c168
32 files changed, 3839 insertions, 195 deletions
diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig
index b008168ae94..203ec61c6d4 100644
--- a/arch/microblaze/Kconfig
+++ b/arch/microblaze/Kconfig
@@ -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
diff --git a/arch/microblaze/Makefile b/arch/microblaze/Makefile
index d2d6cfcb1a3..836832dd9b2 100644
--- a/arch/microblaze/Makefile
+++ b/arch/microblaze/Makefile
@@ -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/
diff --git a/arch/microblaze/include/asm/device.h b/arch/microblaze/include/asm/device.h
index 78a038452c0..402b46e630f 100644
--- a/arch/microblaze/include/asm/device.h
+++ b/arch/microblaze/include/asm/device.h
@@ -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 {
diff --git a/arch/microblaze/include/asm/dma-mapping.h b/arch/microblaze/include/asm/dma-mapping.h
index d00e4009916..18b3731c850 100644
--- a/arch/microblaze/include/asm/dma-mapping.h
+++ b/arch/microblaze/include/asm/dma-mapping.h
@@ -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 */
diff --git a/arch/microblaze/include/asm/io.h b/arch/microblaze/include/asm/io.h
index 267c7c779e5..32d621a56ae 100644
--- a/arch/microblaze/include/asm/io.h
+++ b/arch/microblaze/include/asm/io.h
@@ -15,7 +15,23 @@
#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 */
diff --git a/arch/microblaze/include/asm/irq.h b/arch/microblaze/include/asm/irq.h
index 90f050535eb..31a35c33df6 100644
--- a/arch/microblaze/include/asm/irq.h
+++ b/arch/microblaze/include/asm/irq.h
@@ -14,6 +14,12 @@
#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 */
diff --git a/arch/microblaze/include/asm/page.h b/arch/microblaze/include/asm/page.h
index 9b66c0fa9a3..2dd1d04129e 100644
--- a/arch/microblaze/include/asm/page.h
+++ b/arch/microblaze/include/asm/page.h
@@ -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)))
diff --git a/arch/microblaze/include/asm/pci-bridge.h b/arch/microblaze/include/asm/pci-bridge.h
index 7ad28f6f5f1..0c77cda9f5d 100644
--- a/arch/microblaze/include/asm/pci-bridge.h
+++ b/arch/microblaze/include/asm/pci-bridge.h
@@ -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 */
diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h
index 9f0df5faf2c..bdd65aaee30 100644
--- a/arch/microblaze/include/asm/pci.h
+++ b/arch/microblaze/include/asm/pci.h
@@ -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 */
diff --git a/arch/microblaze/include/asm/pgalloc.h b/arch/microblaze/include/asm/pgalloc.h
index 7547f506456..f44b0d696fe 100644
--- a/arch/microblaze/include/asm/pgalloc.h
+++ b/arch/microblaze/include/asm/pgalloc.h
@@ -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 |
diff --git a/arch/microblaze/include/asm/pgtable.h b/arch/microblaze/include/asm/pgtable.h
index cc3a4dfc3ea..dd2bb60651c 100644
--- a/arch/microblaze/include/asm/pgtable.h
+++ b/arch/microblaze/include/asm/pgtable.h
@@ -16,6 +16,10 @@
#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__ */
diff --git a/arch/microblaze/include/asm/prom.h b/arch/microblaze/include/asm/prom.h
index 03f45a96320..e7d67a329bd 100644
--- a/arch/microblaze/include/asm/prom.h
+++ b/arch/microblaze/include/asm/prom.h
@@ -31,6 +31,21 @@
/* 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
*/
diff --git a/arch/microblaze/include/asm/system.h b/arch/microblaze/include/asm/system.h
index 157970688b2..59efb3fef95 100644
--- a/arch/microblaze/include/asm/system.h
+++ b/arch/microblaze/include/asm/system.h
@@ -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
diff --git a/arch/microblaze/include/asm/tlbflush.h b/arch/microblaze/include/asm/tlbflush.h
index 10ec70cd873..bcb8b41d55a 100644
--- a/arch/microblaze/include/asm/tlbflush.h
+++ b/arch/microblaze/include/asm/tlbflush.h
@@ -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(); }
diff --git a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile
index b07594eccf9..e51bc152082 100644
--- a/arch/microblaze/kernel/Makefile
+++ b/arch/microblaze/kernel/Makefile
@@ -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
diff --git a/arch/microblaze/kernel/asm-offsets.c b/arch/microblaze/kernel/asm-offsets.c
index 7bc7b68f97d..0071260a672 100644
--- a/arch/microblaze/kernel/asm-offsets.c
+++ b/arch/microblaze/kernel/asm-offsets.c
@@ -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 */
diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c
index 2a56bccce4e..f04d8a86dea 100644
--- a/arch/microblaze/kernel/cpu/cache.c
+++ b/arch/microblaze/kernel/cpu/cache.c
@@ -15,25 +15,6 @@
#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
index 00000000000..b1084974fcc
--- /dev/null
+++ b/arch/microblaze/kernel/dma.c
@@ -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);
diff --git a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S
index 3bad4ff4947..c0ede25c5b9 100644
--- a/arch/microblaze/kernel/entry.S
+++ b/arch/microblaze/kernel/entry.S
@@ -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 */
diff --git a/arch/microblaze/kernel/head.S b/arch/microblaze/kernel/head.S
index 30916193fcc..cb7815cfe5a 100644
--- a/arch/microblaze/kernel/head.S
+++ b/arch/microblaze/kernel/head.S
@@ -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
diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c
index 0f06034d1fe..6f39e2c001f 100644
--- a/arch/microblaze/kernel/irq.c
+++ b/arch/microblaze/kernel/irq.c
@@ -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);
diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c
index bb8c4b9ccb8..f974ec7aa35 100644
--- a/arch/microblaze/kernel/setup.c
+++ b/arch/microblaze/kernel/setup.c
@@ -22,7 +22,10 @@
#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);
diff --git a/arch/microblaze/mm/Makefile b/arch/microblaze/mm/Makefile
index 6c8a924d9e2..09c49ed8723 100644
--- a/arch/microblaze/mm/Makefile
+++ b/arch/microblaze/mm/Makefile
@@ -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
index 00000000000..a9b443e3fb9
--- /dev/null
+++ b/arch/microblaze/mm/consistent.c
@@ -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);
diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c
index a57cedf3671..1608e2e1a44 100644
--- a/arch/microblaze/mm/init.c
+++ b/arch/microblaze/mm/init.c
@@ -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;
+}
diff --git a/arch/microblaze/mm/pgtable.c b/arch/microblaze/mm/pgtable.c
index 2820081b21a..63a6fd07c48 100644
--- a/arch/microblaze/mm/pgtable.c
+++ b/arch/microblaze/mm/pgtable.c
@@ -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
index 00000000000..9889cc2e129
--- /dev/null
+++ b/arch/microblaze/pci/Makefile
@@ -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
index 00000000000..25f18f017f2
--- /dev/null
+++ b/arch/microblaze/pci/indirect_pci.c
@@ -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
index 00000000000..3fbf16f4e16
--- /dev/null
+++ b/arch/microblaze/pci/iomap.c
@@ -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
index 00000000000..0be34350d73
--- /dev/null
+++ b/arch/microblaze/pci/pci-common.c
@@ -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
index 00000000000..7e0c94f501c
--- /dev/null
+++ b/arch/microblaze/pci/pci_32.c
@@ -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
index 00000000000..7869a41b0f9
--- /dev/null
+++ b/arch/microblaze/pci/xilinx_pci.c
@@ -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);
+}