[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [microblaze-uclinux] PCI support for Microblaze.
Hi Leonid
Attached is the patch - there's a reasonable chance it won't apply
cleanly as it is part of a pending series I have in my internal tree.
However, all of the hard work is done, resolving any conflicts manually
should be pretty straight forward.
Basically I've pulled the PPC PCI support out, and massaged it to fit.
It works well in my experience here, but no doubt there'll be a few
minor things to iron out.
Cheers,
John
Leonid wrote:
Hi:
I have Spartan 3e with Microblaze 7 and I want to use PLBV46 PCI Full
Bridge (v1.01a) on it. Can anybody suggest where to get Linux kernel
which already has it supported? If I can get hold on relevant reference
design (EDK 10.1 is the best) it will be even better.
I would appreciate any information regarding PCI support on Microblaze.
Thanks,
Leonid.
___________________________
microblaze-uclinux mailing list
microblaze-uclinux@xxxxxxxxxxxxxx
Project Home Page : http://www.itee.uq.edu.au/~jwilliams/mblaze-uclinux
Mailing List Archive : http://www.itee.uq.edu.au/~listarch/microblaze-uclinux/
---
software/linux-2.6.x-petalogix/arch/microblaze/Kconfig | 4
software/linux-2.6.x-petalogix/arch/microblaze/Makefile | 2
software/linux-2.6.x-petalogix/arch/microblaze/kernel/setup.c | 5
software/linux-2.6.x-petalogix/arch/microblaze/mm/consistent.c | 5
software/linux-2.6.x-petalogix/arch/microblaze/mm/init.c | 8
software/linux-2.6.x-petalogix/arch/microblaze/pci/Makefile | 3
software/linux-2.6.x-petalogix/arch/microblaze/pci/indirect_pci.c | 172 +
software/linux-2.6.x-petalogix/arch/microblaze/pci/pci.c | 1320 ++++++++++
software/linux-2.6.x-petalogix/arch/microblaze/pci/pci_auto.c | 516 +++
software/linux-2.6.x-petalogix/arch/microblaze/platform/Kconfig.platform | 11
software/linux-2.6.x-petalogix/arch/microblaze/platform/common/Makefile | 4
software/linux-2.6.x-petalogix/arch/microblaze/platform/common/xpci.c | 307 ++
software/linux-2.6.x-petalogix/drivers/pci/Makefile | 1
software/linux-2.6.x-petalogix/drivers/pci/proc.c | 2
software/linux-2.6.x-petalogix/include/asm-microblaze/dma-mapping.h | 19
software/linux-2.6.x-petalogix/include/asm-microblaze/dma.h | 8
software/linux-2.6.x-petalogix/include/asm-microblaze/io.h | 18
software/linux-2.6.x-petalogix/include/asm-microblaze/mmu.h | 2
software/linux-2.6.x-petalogix/include/asm-microblaze/page.h | 3
software/linux-2.6.x-petalogix/include/asm-microblaze/pci-bridge.h | 25
software/linux-2.6.x-petalogix/include/asm-microblaze/pci.h | 55
software/linux-2.6.x-petalogix/include/asm-microblaze/pgtable.h | 4
22 files changed, 2474 insertions(+), 20 deletions(-)
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/dma.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/dma.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/dma.h
@@ -15,7 +15,13 @@
* unlimited. */
#define MAX_DMA_ADDRESS (0)
-#define ISA_DMA_THRESHOLD (0)
+/* Defined in arch/microblaze/setup.c */
+extern unsigned ISA_DMA_THRESHOLD;
+#ifdef CONFIG_PCI
+extern int isa_dma_bridge_buggy;
+#else
+#define isa_dma_bridge_buggy (0)
+#endif
#endif /* _ASM_DMA_H */
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/pgtable.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/pgtable.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/pgtable.h
@@ -24,6 +24,10 @@
#define PAGE_READONLY __pgprot(0) /* these mean nothing to NO_MM */
#define PAGE_KERNEL __pgprot(0) /* these mean nothing to NO_MM */
+/* Some hacks to keep PowerPC-derived PCI code happy */
+#define _PAGE_NO_CACHE (0)
+#define _PAGE_GUARDED (0)
+
#define __swp_type(x) (0)
#define __swp_offset(x) (0)
#define __swp_entry(typ,off) ((swp_entry_t) { ((typ) | ((off) << 7)) })
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/dma-mapping.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/dma-mapping.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/dma-mapping.h
@@ -11,6 +11,7 @@
#ifndef _ASM_DMA_MAPPING_H
#define _ASM_DMA_MAPPING_H
+#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/bug.h>
@@ -25,7 +26,7 @@ void *consistent_alloc(int gfp, size_t s
static inline int
dma_supported(struct device *dev, u64 mask)
{
- return 0;
+ return 1;
}
static inline dma_addr_t
@@ -114,7 +115,19 @@ dma_map_single(struct device *dev, void
return virt_to_bus(ptr);
}
-#define dma_unmap_single(dev, addr, size, dir) do { } while (0)
-
+static inline void
+dma_unmap_single(struct device *dev, void *ptr, size_t size,
+ enum dma_data_direction direction)
+{
+ switch(direction) {
+ case DMA_FROM_DEVICE:
+ flush_dcache_range((unsigned)ptr,(unsigned)ptr+size);
+ /* Fall through */
+ case DMA_TO_DEVICE:
+ break;
+ default:
+ BUG();
+ }
+}
#endif /* _ASM_DMA_MAPPING_H */
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/mmu.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/mmu.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/mmu.h
@@ -11,6 +11,8 @@
#ifndef _ASM_MMU_H
#define _ASM_MMU_H
+typedef unsigned long phys_addr_t;
+
typedef struct {
struct vm_list_struct *vmlist;
unsigned long end_brk;
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/pci-bridge.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/pci-bridge.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/pci-bridge.h
@@ -20,8 +20,8 @@ extern unsigned long pci_bus_mem_base_ph
extern struct pci_controller* pcibios_alloc_controller(void);
/* Helper function for setting up resources */
-extern void pci_init_resource(struct resource *res, unsigned long start,
- unsigned long end, int flags, char *name);
+extern void pci_init_resource(struct resource *res, resource_size_t start,
+ resource_size_t end, int flags, char *name);
/* Get the PCI host controller for a bus */
extern struct pci_controller* pci_bus_to_hose(int bus);
@@ -43,18 +43,19 @@ struct pci_controller {
struct pci_controller *next;
struct pci_bus *bus;
void *arch_data;
+ struct device *parent;
int first_busno;
int last_busno;
int bus_offset;
void __iomem *io_base_virt;
- unsigned long io_base_phys;
+ resource_size_t io_base_phys;
/* Some machines (PReP) have a non 1:1 mapping of
* the PCI memory space in the CPU bus space
*/
- unsigned long pci_mem_offset;
+ resource_size_t pci_mem_offset;
struct pci_ops *ops;
volatile unsigned int __iomem *cfg_addr;
@@ -79,6 +80,11 @@ struct pci_controller {
struct resource mem_space;
};
+static inline struct pci_controller *pci_bus_to_host(struct pci_bus *bus)
+{
+ return bus->sysdata;
+}
+
/* These are used for config access before all the PCI probing
has been done. */
int early_read_config_byte(struct pci_controller *hose, int bus, int dev_fn,
@@ -116,7 +122,7 @@ static inline unsigned char bridge_swizz
/*
* The following macro is used to lookup irqs in a standard table
- * format for those PPC systems that do not already have PCI
+ * format for those MICROBLAZE systems that do not already have PCI
* interrupts properly routed.
*/
/* FIXME - double check this */
@@ -132,5 +138,14 @@ static inline unsigned char bridge_swizz
*/
extern int pciauto_bus_scan(struct pci_controller *, int);
+#ifdef CONFIG_PCI
+extern unsigned long pci_address_to_pio(phys_addr_t address);
+#else
+static inline unsigned long pci_address_to_pio(phys_addr_t address)
+{
+ return (unsigned long)-1;
+}
+#endif
+
#endif
#endif /* __KERNEL__ */
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/io.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/io.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/io.h
@@ -14,6 +14,24 @@
#include <asm/byteorder.h>
#include <asm/page.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
+
+
+#define ___IO_BASE ((void __iomem *)_IO_BASE)
+extern unsigned long isa_io_base;
+extern unsigned long isa_mem_base;
+extern unsigned long pci_io_base;
+extern unsigned long pci_dram_offset;
+
+
static inline unsigned char __raw_readb(const volatile void __iomem *addr)
{
return *(volatile unsigned char __force *)addr;
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/page.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/page.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/page.h
@@ -78,6 +78,9 @@ extern unsigned long max_low_pfn;
extern unsigned long min_low_pfn;
extern unsigned long max_pfn;
+
+extern int page_is_ram(unsigned long pfn);
+
#define __pa(vaddr) ((unsigned long) (vaddr))
#define __va(paddr) ((void *) (paddr))
Index: b/software/linux-2.6.x-petalogix/include/asm-microblaze/pci.h
===================================================================
--- a/software/linux-2.6.x-petalogix/include/asm-microblaze/pci.h
+++ b/software/linux-2.6.x-petalogix/include/asm-microblaze/pci.h
@@ -12,6 +12,13 @@
#include <asm-generic/pci-dma-compat.h>
struct pci_dev;
+/* Platform-provided IRQ mapping and swizzling functions. Declared
+ weak so we can still build if not provided, using sensible defaults */
+int microblaze_pci_map_irq(struct pci_dev *dev, unsigned char idsel,
+ unsigned char pin) __attribute__ ((weak));
+
+u8 microblaze_pci_swizzle(struct pci_dev *, u8 *) __attribute__ ((weak));
+
/* Values for the `which' argument to sys_pciconfig_iobase syscall. */
#define IOBASE_BRIDGE_NUMBER 0
@@ -24,9 +31,9 @@ struct pci_dev;
* Set this to 1 if you want the kernel to re-assign all PCI
* bus numbers
*/
-extern int pci_assign_all_busses;
+extern int pci_assign_all_buses;
-#define pcibios_assign_all_busses() (pci_assign_all_busses)
+#define pcibios_assign_all_busses() (pci_assign_all_buses)
#define pcibios_scan_all_fns(a, b) 0
#define PCIBIOS_MIN_IO 0x1000
@@ -61,6 +68,27 @@ extern unsigned long pci_bus_to_phys(uns
*/
#define PCI_DMA_BUS_IS_PHYS (1)
+#ifdef CONFIG_NOT_COHERENT_CACHE
+/*
+ * pci_unmap_{page,single} are NOPs but pci_dma_sync_single_for_cpu()
+ * and so on are not, so...
+ */
+
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) \
+ dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) \
+ __u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME) \
+ ((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) \
+ (((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME) \
+ ((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL) \
+ (((PTR)->LEN_NAME) = (VAL))
+
+#else /* coherent */
+
/* pci_unmap_{page,single} is a nop so... */
#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
@@ -69,6 +97,8 @@ extern unsigned long pci_bus_to_phys(uns
#define pci_unmap_len(PTR, LEN_NAME) (0)
#define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0)
+#endif /* CONFIG_NOT_COHERENT_CACHE */
+
#ifdef CONFIG_PCI
static inline void pci_dma_burst_advice(struct pci_dev *pdev,
enum pci_dma_burst_strategy *strat,
@@ -80,7 +110,7 @@ static inline void pci_dma_burst_advice(
#endif
/*
- * At present there are very few 32-bit PPC machines that can have
+ * At present there are very few 32-bit MICROBLAZE machines that can have
* memory above the 4GB point, and we don't support that.
*/
#define pci_dac_dma_supported(pci_dev, mask) (0)
@@ -88,11 +118,13 @@ static inline void pci_dma_burst_advice(
/* Return the index of the PCI controller for device PDEV. */
#define pci_domain_nr(bus) ((struct pci_controller *)(bus)->sysdata)->index
+#if 0
/* Set the name of the bus as it appears in /proc/bus/pci */
static inline int pci_proc_domain(struct pci_bus *bus)
{
return 0;
}
+#endif
/* 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,
@@ -109,18 +141,31 @@ 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_add_platform_entries(struct pci_dev *dev);
struct file;
extern pgprot_t pci_phys_mem_access_prot(struct file *file,
- unsigned long offset,
+ 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,
- u64 *start, u64 *end);
+ resource_size_t *start, resource_size_t *end);
#endif /* __KERNEL__ */
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/Kconfig
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/Kconfig
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/Kconfig
@@ -51,10 +51,6 @@ config GENERIC_CALIBRATE_DELAY
bool
default y
-config PCI
- bool
- default n
-
config UID16
bool
default y
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/kernel/setup.c
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/kernel/setup.c
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/kernel/setup.c
@@ -36,6 +36,8 @@ DEFINE_PER_CPU(unsigned int, ENTRY_SP);
DEFINE_PER_CPU(unsigned int, R11_SAVE); /* Temp variable for entry */
DEFINE_PER_CPU(unsigned int, CURRENT_SAVE); /* Saved current pointer */
+unsigned ISA_DMA_THRESHOLD = (~0UL);
+
extern void early_printk(const char *fmt, ...);
extern void irq_early_init(void);
extern int __init setup_early_printk(char *opt);
@@ -70,6 +72,9 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con;
#endif
#endif
+#ifdef CONFIG_PCI
+ microblaze_find_bridges();
+#endif
}
#ifdef CONFIG_MTD_UCLINUX_EBSS
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/platform/common/xpci.c
===================================================================
--- /dev/null
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/platform/common/xpci.c
@@ -0,0 +1,307 @@
+/*
+ * Authors: Frank Rowand <frank_rowand@xxxxxxxxxx>,
+ * Debbie Chu <debbie_chu@xxxxxxxxxx>, or source@xxxxxxxxxx
+ * Further modifications by Armin Kuster <akuster@xxxxxxxxxx>
+ *
+ * 2000 (c) MontaVista, Software, Inc. 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.
+ *
+ * Based on arch/ppc/kernel/indirect.c, Copyright (C) 1998 Gabriel Paubert.
+ */
+
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+//#include <asm/machdep.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+//#include <asm/ocp.h>
+//#include <asm/ibm4xx.h>
+#include <asm/pci-bridge.h>
+//#include <asm/ibm_ocp_pci.h>
+
+
+
+/* Some big assumptions
+ - IPIFBAR0 is the PCI "memory" space
+ - IPIFBAR1 is the PCI "io" space
+*/
+
+/* PCI Memory space */
+#define MICROBLAZE_PCI_LOWER_MEM CONFIG_XILINX_PCI_0_IPIFBAR_0
+#define MICROBLAZE_PCI_UPPER_MEM CONFIG_XILINX_PCI_0_IPIF_HIGHADDR_0
+
+/* FIXME hardcoded */
+#define MICROBLAZE_PCI_IO_SIZE (0x10000)
+
+/* PCI I/O space processor address */
+#define MICROBLAZE_PCI_PHY_IO_BASE (CONFIG_XILINX_PCI_0_IPIFBAR_1)
+/* PCI I/O space PCI address */
+#define MICROBLAZE_PCI_LOWER_IO (0x0)
+#define MICROBLAZE_PCI_UPPER_IO (MICROBLAZE_PCI_LOWER_IO + MICROBLAZE_PCI_IO_SIZE - 1)
+
+/* PCI Configuration space address and data registers */
+#define MICROBLAZE_PCI_CONFIG_ADDR (CONFIG_XILINX_PCI_0_BASEADDR + 0x10c)
+#define MICROBLAZE_PCI_CONFIG_DATA (CONFIG_XILINX_PCI_0_BASEADDR + 0x110)
+
+/* Base address of PCI controller registers (PLB space) */
+#define MICROBLAZE_PCI_CTRL_BASE (CONFIG_XILINX_PCI_0_BASEADDR)
+
+/* Offsets of some useful registers */
+#define XPCI_IPIF_DISR_OFFSET 0UL /**< device interrupt status register */
+#define XPCI_IPIF_DIPR_OFFSET 4UL /**< device interrupt pending register */
+#define XPCI_IPIF_DIER_OFFSET 8UL /**< device interrupt enable register */
+#define XPCI_IPIF_DIIR_OFFSET 24UL /**< device interrupt ID register */
+#define XPCI_IPIF_DGIER_OFFSET 28UL /**< device global interrupt enable register */
+#define XPCI_IPIF_IISR_OFFSET 32UL /**< IP interrupt status register */
+#define XPCI_IPIF_IIER_OFFSET 40UL /**< IP interrupt enable register */
+#define XPCI_IPIF_RESETR_OFFSET 0x80UL /**< reset register */
+#define XPCI_PREOVRD_OFFSET 0x100 /**< Prefetch override */
+#define XPCI_IAR_OFFSET 0x104 /**< PCI interrupt ack */
+#define XPCI_SC_DATA_OFFSET 0x108 /**< Special cycle data */
+#define XPCI_CAR_OFFSET 0x10C /**< Config addr reg (port) */
+#define XPCI_CDR_OFFSET 0x110 /**< Config command data */
+#define XPCI_BUSNO_OFFSET 0x114 /**< bus/subordinate bus numbers */
+#define XPCI_STATCMD_OFFSET 0x118 /**< PCI config status/command */
+#define XPCI_STATV3_OFFSET 0x11C /**< V3 core transaction status */
+#define XPCI_INHIBIT_OFFSET 0x120 /**< Inhibit transfers on errors */
+#define XPCI_LMADDR_OFFSET 0x124 /**< Local bus master address definition */
+#define XPCI_LMA_R_OFFSET 0x128 /**< Local bus master read error address */
+#define XPCI_LMA_W_OFFSET 0x12C /**< Local bus master write error address */
+#define XPCI_SERR_R_OFFSET 0x130 /**< PCI initiater read SERR address */
+#define XPCI_SERR_W_OFFSET 0x134 /**< PCI initiater write SERR address */
+#define XPCI_PIADDR_OFFSET 0x138 /**< PCI address definition */
+#define XPCI_PIA_R_OFFSET 0x13C /**< PCI read error address */
+#define XPCI_PIA_W_OFFSET 0x140 /**< PCI write error address */
+#define XPCI_IPIF2PCI_0_OFFSET 0x180 /**< IPIFBAR to PCIBAR translation 0 */
+#define XPCI_IPIF2PCI_1_OFFSET 0x184 /**< IPIFBAR to PCIBAR translation 1 */
+#define XPCI_IPIF2PCI_2_OFFSET 0x188 /**< IPIFBAR to PCIBAR translation 2 */
+#define XPCI_IPIF2PCI_3_OFFSET 0x18C /**< IPIFBAR to PCIBAR translation 3 */
+#define XPCI_IPIF2PCI_4_OFFSET 0x190 /**< IPIFBAR to PCIBAR translation 4 */
+#define XPCI_IPIF2PCI_5_OFFSET 0x194 /**< IPIFBAR to PCIBAR translation 5 */
+#define XPCI_HBDN_OFFSET 0x198 /**< Host bridge device number */
+
+
+/*
+ * Default IPIF Device interrupt enable (XPCI_IPIF_IIER_OFFSET),
+ * status registers (XPCI_IPIF_IISR_OFFSET), and pending register
+ * (XPCI_IPIF_IIPR_OFFSET)
+ */
+#define XPCI_IPIF_INT_PCI 0x00000004 /* PCI core interrupt */
+#define XPCI_IPIF_INT_DMA 0x00000008 /* DMA interrupt */
+
+/* Define IPIF interrupt mask */
+#define XPCI_IPIF_INT_MASK (XPCI_IPIF_INT_PCI | XPCI_IPIF_INT_DMA)
+
+/* Define value of IPIF DIIR register when no interrupt is pending */
+#define XPCI_IPIF_INT_NONE_PENDING 128 /**< no interrupts are pending */
+
+#define XPCI_IPIF_GINTR_ENABLE_MASK 0x80000000UL
+#define XPCI_IPIF_RESET_MASK 0xAUL
+
+/** Local bus master address register bitmaps and masks
+ *
+ * Bit definitions for the local bus master address definition register.
+ * This register defines the meaning of the address stored in the local bus
+ * master read (XPCI_LMA_R_OFFSET) and master write error (XPCI_LMA_W_OFFSET)
+ * registers.
+ */
+#define XPCI_LMADDR_MASK 0x000007FF /**< Mask of all bits */
+#define XPCI_LMADDR_SERR_R 0x00000001 /**< Master read SERR */
+#define XPCI_LMADDR_PERR_R 0x00000002 /**< Master read PERR */
+#define XPCI_LMADDR_TA_R 0x00000004 /**< Master read target abort */
+#define XPCI_LMADDR_SERR_W 0x00000008 /**< Master write SERR */
+#define XPCI_LMADDR_PERR_W 0x00000010 /**< Master write PERR */
+#define XPCI_LMADDR_TA_W 0x00000020 /**< Master write target abort */
+#define XPCI_LMADDR_MA_W 0x00000040 /**< Master abort write */
+#define XPCI_LMADDR_BR_W 0x00000080 /**< Master burst write retry */
+#define XPCI_LMADDR_BRD_W 0x00000100 /**< Master burst write retry disconnect */
+#define XPCI_LMADDR_BRT_W 0x00000200 /**< Master burst write retry timeout */
+#define XPCI_LMADDR_PFT_R 0x00000400 /**< Master burst prefetch time out */
+
+//extern void bios_fixup(struct pci_controller *, struct pcil0_regs *);
+extern int microblaze_map_irq(struct pci_dev *dev, unsigned char idsel,
+ unsigned char pin);
+
+int microblaze_pci_exclude_device(unsigned char bus, unsigned char devfn)
+{
+ /* We prevent us from seeing ourselves to avoid having
+ * the kernel try to remap our BAR #1 and fuck up bus
+ * master from external PCI devices
+ */
+ return (bus == 0 && devfn == 0);
+}
+
+/* This is called from kernel/setup.c:arch_setup() */
+void microblaze_find_bridges(void)
+{
+ struct pci_controller *hose_a;
+ unsigned int tmp_addr;
+ unsigned int tmp_size;
+ unsigned int reg_index;
+ unsigned int new_pmm_max = 0;
+ unsigned int new_pmm_min = 0;
+
+ isa_io_base = 0;
+ isa_mem_base = 0;
+ pci_dram_offset = 0;
+
+ /* Setup PCI32 hose */
+ hose_a = pcibios_alloc_controller();
+ if (!hose_a)
+ return;
+
+ setup_indirect_pci(hose_a, MICROBLAZE_PCI_CONFIG_ADDR,
+ MICROBLAZE_PCI_CONFIG_DATA);
+
+ new_pmm_min = MICROBLAZE_PCI_LOWER_MEM;
+ new_pmm_max = MICROBLAZE_PCI_UPPER_MEM;
+
+ hose_a->first_busno = 0;
+ hose_a->last_busno = 0xff;
+ hose_a->pci_mem_offset = 0;
+
+ /* Setup bridge memory/IO ranges & resources
+ * TODO: Handle firmwares setting up a legacy ISA mem base
+ */
+ hose_a->io_space.start = MICROBLAZE_PCI_LOWER_IO;
+ hose_a->io_space.end = MICROBLAZE_PCI_UPPER_IO;
+ hose_a->mem_space.start = new_pmm_min;
+ hose_a->mem_space.end = new_pmm_max;
+ hose_a->io_base_phys = MICROBLAZE_PCI_PHY_IO_BASE;
+ hose_a->io_base_virt = ioremap(hose_a->io_base_phys, 0x10000);
+ hose_a->io_resource.start = 0;
+ hose_a->io_resource.end =
+ MICROBLAZE_PCI_UPPER_IO - MICROBLAZE_PCI_LOWER_IO;
+ hose_a->io_resource.flags = IORESOURCE_IO;
+ hose_a->io_resource.name = "PCI I/O";
+ hose_a->mem_resources[0].start = new_pmm_min;
+ hose_a->mem_resources[0].end = new_pmm_max;
+ hose_a->mem_resources[0].flags = IORESOURCE_MEM;
+ hose_a->mem_resources[0].name = "PCI Memory";
+ isa_io_base = (int) hose_a->io_base_virt;
+ isa_mem_base = 0; /* ISA not implemented */
+ ISA_DMA_THRESHOLD = 0x00ffffff; /* ??? ISA not implemented */
+
+ /* Scan busses & initial setup by pci_auto */
+ hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno);
+ hose_a->last_busno = 0;
+
+#if 0
+ /* FIXME */
+ /* Setup ppc_md */
+ ppc_md.pcibios_fixup = NULL;
+ ppc_md.pci_exclude_device = microblaze_exclude_device;
+ ppc_md.pcibios_fixup_resources =
+ microblaze_pcibios_fixup_resources;
+ ppc_md.pci_swizzle = common_swizzle;
+ ppc_md.pci_map_irq = microblaze_map_irq;
+#endif
+}
+
+u32 __init early_pci_read_config(u32 bus, u32 dev, u32 func, u32 offset)
+{
+ out_le32((volatile unsigned *) (MICROBLAZE_PCI_CONFIG_ADDR),
+ (0x80000000 | bus << 16 | PCI_DEVFN(dev, func) << 8 |
+ (offset & 0xfc)));
+
+ return in_le32((volatile unsigned *) (MICROBLAZE_PCI_CONFIG_DATA));
+
+}
+
+void __init early_pci_write_config(u32 bus, u32 dev, u32 func,
+ u32 offset, u32 val)
+{
+ out_le32((volatile unsigned *) (MICROBLAZE_PCI_CONFIG_ADDR),
+ (0x80000000 | bus << 16 | PCI_DEVFN(dev, func) << 8 |
+ (offset & 0xfc)));
+
+ out_le32((volatile unsigned *) (MICROBLAZE_PCI_CONFIG_DATA),val);
+}
+
+void __init early_pci_scan(void)
+{
+ u32 bus = 0, dev, func, offset;
+ u32 val;
+
+ for (dev = 0; dev < 2; dev++) {
+ for (func = 0; func < 1; func++) {
+ printk("%02x:%02x:%02x", bus, dev, func);
+ for (offset = 0; offset < 64; offset += 4) {
+ val=early_pci_read_config(0,dev,func,offset);
+ if (offset == 0 && val == 0xFFFFFFFF) {
+ printk("\nABSENT");
+ break;
+ }
+ if(!(offset % 0x10)) {
+ printk("\n%04x: ", offset);
+ }
+ printk("%08x ", val);
+ }
+ printk("\n");
+ }
+ }
+}
+
+
+/* This is called from mm/init.c */
+void __init xilinx_pci_enable_bridge(void)
+{
+#if 0
+ /* Reset the core */
+ out_be32((volatile unsigned *) (MICROBLAZE_PCI_CTRL_BASE
+ + XPCI_IPIF_RESETR_OFFSET),XPCI_IPIF_RESET_MASK);
+#endif
+
+ /* Disable interrupts at the global level */
+ out_be32((volatile unsigned *) (MICROBLAZE_PCI_CTRL_BASE
+ + XPCI_IPIF_DGIER_OFFSET),0);
+
+ /* Disable and clear all pending interrupts at the device level */
+ out_be32((volatile unsigned *) (MICROBLAZE_PCI_CTRL_BASE
+ + XPCI_IPIF_DIER_OFFSET),0);
+ out_be32((volatile unsigned *) (MICROBLAZE_PCI_CTRL_BASE
+ + XPCI_IPIF_DISR_OFFSET),0);
+
+ /* Disable and clear all pending interrupts at the PCI level */
+ out_be32((volatile unsigned *) (MICROBLAZE_PCI_CTRL_BASE
+ + XPCI_IPIF_IIER_OFFSET),0);
+ out_be32((volatile unsigned *) (MICROBLAZE_PCI_CTRL_BASE
+ + XPCI_IPIF_IISR_OFFSET),0);
+/*
+ * Enble the PCI initiator functions in the PCI core by writing
+ * to the self-configuration space as described in the
+ * Configuration section of the OPB to PCI Bridge chapter of the
+ * Virtex-II Pro Platform FPGA Developer's Kit manual.
+ */
+ /* enable bridge */
+ early_pci_write_config(0,0,0,PCI_COMMAND,
+ (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
+ PCI_COMMAND_SERR | 0xffff0000));
+
+ /* set max lat timer on bridge */
+ early_pci_write_config(0,0,0,0x0c, 0x0000ff00);
+
+ /*
+ * Set the bus and subordinate bus numbers (0 and 0 respectively)
+ */
+ out_be32((volatile unsigned *) (MICROBLAZE_PCI_CTRL_BASE
+ + XPCI_BUSNO_OFFSET),0);
+
+ /*
+ * Set BAR0 of our controller to point to the main memory
+ */
+ early_pci_write_config(0,0,0,PCI_BASE_ADDRESS_0,CONFIG_XILINX_ERAM_START);
+#if 0
+ printk("Command reg:0x%08x\n", early_pci_read_config(0,0,0,PCI_COMMAND));
+ printk("Latency Reg:0x%08x\n", early_pci_read_config(0,0,0,0x0c));
+
+ printk("Doing early_pci_scan\n");
+ early_pci_scan();
+#endif
+
+}
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/platform/common/Makefile
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/platform/common/Makefile
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/platform/common/Makefile
@@ -18,6 +18,8 @@ platobj-$(CONFIG_SERIAL_UARTLITE) += xu
platobj-$(CONFIG_SERIAL_8250) += x16550.o
platobj-$(CONFIG_XILINX_SYSACE) += xsysace.o
platobj-$(CONFIG_INPUT_KEYPADDEV) += xbtn_decoder.o
+platobj-$(CONFIG_PCI) += xpci.o
+
obj-y += $(platobj-y) $(platobj-m)
@@ -34,4 +36,6 @@ $(obj)/x16550.o: .config
$(obj)/xgpio.o: .config
$(obj)/xspi.o: .config
$(obj)/xbtn_decoder.o: .config
+$(obj)/xpci.o: .config
+
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/platform/Kconfig.platform
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/platform/Kconfig.platform
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/platform/Kconfig.platform
@@ -2,6 +2,17 @@
# platform selection Kconfig menu for MicroBlaze targets
#
menu "Platform options"
+
+config PCI
+ bool "PCI support"
+ default n
+ help
+ Find out whether your system includes a PCI bus. PCI is the name of
+ a bus system, i.e. the way the CPU talks to the other stuff inside
+ your box. If you say Y here, the kernel will include drivers and
+ infrastructure code to support PCI bus devices. You must also ensure
+ that your system contains a Xilinx PCI host bridge.
+
choice
prompt "Platform"
default PLATFORM_MICROBLAZE_AUTO
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/mm/init.c
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/mm/init.c
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/mm/init.c
@@ -19,6 +19,11 @@
char *klimit = _end;
+extern int page_is_ram(unsigned long pfn) {
+ return pfn < max_low_pfn;
+}
+
+
void __init setup_memory(void)
{
unsigned long m_start, m_end, map_size;
@@ -39,6 +44,9 @@ void __init setup_memory(void)
map_size = init_bootmem_node(NODE_DATA(0), PFN_UP(m_start), min_low_pfn, max_mapnr);
free_bootmem(m_start+map_size, m_end - (m_start+map_size));
+#ifdef CONFIG_PCI
+ xilinx_pci_enable_bridge();
+#endif
}
void __init paging_init(void)
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/mm/consistent.c
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/mm/consistent.c
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/mm/consistent.c
@@ -175,3 +175,8 @@ void consistent_sync_page(struct page *p
unsigned long start = (unsigned long)page_address(page) + offset;
consistent_sync((void *)start, size, direction);
}
+
+EXPORT_SYMBOL(consistent_alloc);
+EXPORT_SYMBOL(consistent_free);
+EXPORT_SYMBOL(consistent_sync);
+
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/pci/pci_auto.c
===================================================================
--- /dev/null
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/pci/pci_auto.c
@@ -0,0 +1,516 @@
+/*
+ * PCI autoconfiguration library
+ *
+ * Author: Matt Porter <mporter@xxxxxxxxxx>
+ *
+ * 2001 (c) MontaVista, Software, Inc. 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.
+ */
+
+/*
+ * The CardBus support is very preliminary. Preallocating space is
+ * the way to go but will require some change in card services to
+ * make it useful. Eventually this will ensure that we can put
+ * multiple CB bridges behind multiple P2P bridges. For now, at
+ * least it ensures that we place the CB bridge BAR and assigned
+ * initial bus numbers. I definitely need to do something about
+ * the lack of 16-bit I/O support. -MDP
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/pci-bridge.h>
+
+#define PCIAUTO_IDE_MODE_MASK 0x05
+
+//#undef DEBUG
+#define DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif /* DEBUG */
+
+static int pciauto_upper_iospc;
+static int pciauto_upper_memspc;
+
+void __init pciauto_setup_bars(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int bar_limit)
+{
+ int bar_response, bar_size, bar_value;
+ int bar, addr_mask;
+ int * upper_limit;
+ int found_mem64 = 0;
+
+ DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n",
+ current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) );
+
+ for (bar = PCI_BASE_ADDRESS_0; bar <= bar_limit; bar+=4) {
+ /* Tickle the BAR and get the response */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ 0xffffffff);
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ &bar_response);
+
+ /* If BAR is not implemented go to the next BAR */
+ if (!bar_response)
+ continue;
+
+ /* Check the BAR type and set our address mask */
+ if (bar_response & PCI_BASE_ADDRESS_SPACE) {
+ addr_mask = PCI_BASE_ADDRESS_IO_MASK;
+ upper_limit = &pciauto_upper_iospc;
+ DBG("PCI Autoconfig: BAR 0x%x, I/O, ", bar);
+ } else {
+ if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+ PCI_BASE_ADDRESS_MEM_TYPE_64)
+ found_mem64 = 1;
+
+ addr_mask = PCI_BASE_ADDRESS_MEM_MASK;
+ upper_limit = &pciauto_upper_memspc;
+ DBG("PCI Autoconfig: BAR 0x%x, Mem ", bar);
+ }
+
+ /* Calculate requested size */
+ bar_size = ~(bar_response & addr_mask) + 1;
+
+ /* Allocate a base address */
+ bar_value = (*upper_limit - bar_size) & ~(bar_size - 1);
+
+ /* Write it out and update our limit */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ bar_value);
+
+ *upper_limit = bar_value;
+
+ /*
+ * If we are a 64-bit decoder then increment to the
+ * upper 32 bits of the bar and force it to locate
+ * in the lower 4GB of memory.
+ */
+ if (found_mem64) {
+ bar += 4;
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ 0x00000000);
+ found_mem64 = 0;
+ }
+
+ DBG("size=0x%x, address=0x%x\n",
+ bar_size, bar_value);
+ }
+
+}
+
+void __init pciauto_prescan_setup_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ /* Configure bus number registers */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PRIMARY_BUS,
+ current_bus);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SECONDARY_BUS,
+ sub_bus + 1);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ 0xff);
+
+ /* Round memory allocator to 1MB boundary */
+ pciauto_upper_memspc &= ~(0x100000 - 1);
+ *memsave = pciauto_upper_memspc;
+
+ /* Round I/O allocator to 4KB boundary */
+ pciauto_upper_iospc &= ~(0x1000 - 1);
+ *iosave = pciauto_upper_iospc;
+
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_MEMORY_LIMIT,
+ ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_LIMIT,
+ ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8);
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_LIMIT_UPPER16,
+ ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16);
+
+ /* Zero upper 32 bits of prefetchable base/limit */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_BASE_UPPER32,
+ 0);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_LIMIT_UPPER32,
+ 0);
+}
+
+void __init pciauto_postscan_setup_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ int cmdstat;
+
+ /* Configure bus number registers */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ sub_bus);
+
+ /*
+ * Round memory allocator to 1MB boundary.
+ * If no space used, allocate minimum.
+ */
+ pciauto_upper_memspc &= ~(0x100000 - 1);
+ if (*memsave == pciauto_upper_memspc)
+ pciauto_upper_memspc -= 0x00100000;
+
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_MEMORY_BASE,
+ pciauto_upper_memspc >> 16);
+
+ /* Allocate 1MB for pre-fretch */
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_MEMORY_LIMIT,
+ ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
+
+ pciauto_upper_memspc -= 0x100000;
+
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_MEMORY_BASE,
+ pciauto_upper_memspc >> 16);
+
+ /* Round I/O allocator to 4KB boundary */
+ pciauto_upper_iospc &= ~(0x1000 - 1);
+ if (*iosave == pciauto_upper_iospc)
+ pciauto_upper_iospc -= 0x1000;
+
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_BASE,
+ (pciauto_upper_iospc & 0x0000f000) >> 8);
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_BASE_UPPER16,
+ pciauto_upper_iospc >> 16);
+
+ /* Enable memory and I/O accesses, enable bus master */
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ &cmdstat);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+}
+
+void __init pciauto_prescan_setup_cardbus_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ /* Configure bus number registers */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PRIMARY_BUS,
+ current_bus);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SECONDARY_BUS,
+ sub_bus + 1);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ 0xff);
+
+ /* Round memory allocator to 4KB boundary */
+ pciauto_upper_memspc &= ~(0x1000 - 1);
+ *memsave = pciauto_upper_memspc;
+
+ /* Round I/O allocator to 4 byte boundary */
+ pciauto_upper_iospc &= ~(0x4 - 1);
+ *iosave = pciauto_upper_iospc;
+
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x20,
+ pciauto_upper_memspc - 1);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x30,
+ pciauto_upper_iospc - 1);
+}
+
+void __init pciauto_postscan_setup_cardbus_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ int cmdstat;
+
+ /*
+ * Configure subordinate bus number. The PCI subsystem
+ * bus scan will renumber buses (reserving three additional
+ * for this PCI<->CardBus bridge for the case where a CardBus
+ * adapter contains a P2P or CB2CB bridge.
+ */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ sub_bus);
+
+ /*
+ * Reserve an additional 4MB for mem space and 16KB for
+ * I/O space. This should cover any additional space
+ * requirement of unusual CardBus devices with
+ * additional bridges that can consume more address space.
+ *
+ * Although pcmcia-cs currently will reprogram bridge
+ * windows, the goal is to add an option to leave them
+ * alone and use the bridge window ranges as the regions
+ * that are searched for free resources upon hot-insertion
+ * of a device. This will allow a PCI<->CardBus bridge
+ * configured by this routine to happily live behind a
+ * P2P bridge in a system.
+ */
+ pciauto_upper_memspc -= 0x00400000;
+ pciauto_upper_iospc -= 0x00004000;
+
+ /* Round memory allocator to 4KB boundary */
+ pciauto_upper_memspc &= ~(0x1000 - 1);
+
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x1c,
+ pciauto_upper_memspc);
+
+ /* Round I/O allocator to 4 byte boundary */
+ pciauto_upper_iospc &= ~(0x4 - 1);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x2c,
+ pciauto_upper_iospc);
+
+ /* Enable memory and I/O accesses, enable bus master */
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ &cmdstat);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+}
+
+int __init pciauto_bus_scan(struct pci_controller *hose, int current_bus)
+{
+ int sub_bus, pci_devfn, pci_class, cmdstat, found_multi = 0;
+ unsigned short vid;
+ unsigned char header_type;
+
+ /*
+ * Fetch our I/O and memory space upper boundaries used
+ * to allocated base addresses on this hose.
+ */
+ if (current_bus == hose->first_busno) {
+ pciauto_upper_iospc = hose->io_space.end + 1;
+ pciauto_upper_memspc = hose->mem_space.end + 1;
+ }
+
+ sub_bus = current_bus;
+
+ for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
+ /* Skip our host bridge */
+ if ( (current_bus == hose->first_busno) && (pci_devfn == 0) )
+ continue;
+
+ if (PCI_FUNC(pci_devfn) && !found_multi)
+ continue;
+
+ /* If config space read fails from this device, move on */
+ if (early_read_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_HEADER_TYPE,
+ &header_type))
+ continue;
+
+ if (!PCI_FUNC(pci_devfn))
+ found_multi = header_type & 0x80;
+
+ early_read_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_VENDOR_ID,
+ &vid);
+
+ if (vid != 0xffff) {
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_CLASS_REVISION, &pci_class);
+ if ( (pci_class >> 16) == PCI_CLASS_BRIDGE_PCI ) {
+ int iosave, memsave;
+
+ DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn));
+ /* Allocate PCI I/O and/or memory space */
+ pciauto_setup_bars(hose,
+ current_bus,
+ pci_devfn,
+ PCI_BASE_ADDRESS_1);
+
+ pciauto_prescan_setup_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ sub_bus = pciauto_bus_scan(hose, sub_bus+1);
+ pciauto_postscan_setup_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ } else if ((pci_class >> 16) == PCI_CLASS_BRIDGE_CARDBUS) {
+ int iosave, memsave;
+
+ DBG("PCI Autoconfig: Found CardBus bridge, device %d function %d\n", PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn));
+ /* Place CardBus Socket/ExCA registers */
+ pciauto_setup_bars(hose,
+ current_bus,
+ pci_devfn,
+ PCI_BASE_ADDRESS_0);
+
+ pciauto_prescan_setup_cardbus_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ sub_bus = pciauto_bus_scan(hose, sub_bus+1);
+ pciauto_postscan_setup_cardbus_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ } else {
+ if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) {
+ unsigned char prg_iface;
+
+ early_read_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_CLASS_PROG,
+ &prg_iface);
+ if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) {
+ DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n");
+ continue;
+ }
+ }
+ /* Allocate PCI I/O and/or memory space */
+ pciauto_setup_bars(hose,
+ current_bus,
+ pci_devfn,
+ PCI_BASE_ADDRESS_5);
+
+ /*
+ * Enable some standard settings
+ */
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ &cmdstat);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_LATENCY_TIMER,
+ 0x80);
+ }
+ }
+ }
+ return sub_bus;
+}
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/pci/pci.c
===================================================================
--- /dev/null
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/pci/pci.c
@@ -0,0 +1,1320 @@
+/*
+ * Common 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 <asm/processor.h>
+#include <asm/io.h>
+#include <asm/page.h>
+//#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/pci-bridge.h>
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+//#include <asm/machdep.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+unsigned long isa_io_base = 0;
+unsigned long isa_mem_base = 0;
+unsigned long pci_dram_offset = 0;
+int pcibios_assign_bus_offset = 1;
+
+void pcibios_make_OF_bus_map(void);
+
+static int pci_relocate_bridge_resource(struct pci_bus *bus, int i);
+static int probe_resource(struct pci_bus *parent, struct resource *pr,
+ struct resource *res, struct resource **conflict);
+static void update_bridge_base(struct pci_bus *bus, int i);
+static void pcibios_fixup_resources(struct pci_dev* dev);
+static void fixup_broken_pcnet32(struct pci_dev* dev);
+static int reparent_resources(struct resource *parent, struct resource *res);
+static void fixup_cpc710_pci64(struct pci_dev* dev);
+
+/* By default, we don't re-assign bus numbers.
+ */
+int pci_assign_all_buses;
+
+struct pci_controller* hose_head;
+struct pci_controller** hose_tail = &hose_head;
+
+static int pci_bus_count;
+
+static void
+fixup_broken_pcnet32(struct pci_dev* dev)
+{
+ if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) {
+ dev->vendor = PCI_VENDOR_ID_AMD;
+ pci_write_config_word(dev, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD);
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32);
+
+static void
+fixup_cpc710_pci64(struct pci_dev* dev)
+{
+ /* Hide the PCI64 BARs from the kernel as their content doesn't
+ * fit well in the resource management
+ */
+ dev->resource[0].start = dev->resource[0].end = 0;
+ dev->resource[0].flags = 0;
+ dev->resource[1].start = dev->resource[1].end = 0;
+ dev->resource[1].flags = 0;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CPC710_PCI64, fixup_cpc710_pci64);
+
+static void
+pcibios_fixup_resources(struct pci_dev *dev)
+{
+ struct pci_controller* hose = (struct pci_controller *)dev->sysdata;
+ int i;
+ unsigned long offset;
+
+ if (!hose) {
+ printk(KERN_ERR "No hose 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;
+ if (res->end == 0xffffffff) {
+ DBG("PCI:%s Resource %d [%016llx-%016llx] is unassigned\n",
+ pci_name(dev), i,
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
+ res->end -= res->start;
+ res->start = 0;
+ res->flags |= IORESOURCE_UNSET;
+ continue;
+ }
+ offset = 0;
+ if (res->flags & IORESOURCE_MEM) {
+ offset = hose->pci_mem_offset;
+ } else if (res->flags & IORESOURCE_IO) {
+ offset = (unsigned long) hose->io_base_virt
+ - isa_io_base;
+ }
+ if (offset != 0) {
+ res->start += offset;
+ res->end += offset;
+#ifdef DEBUG
+ printk("Fixup res %d (%lx) of dev %s: %lx -> %lx\n",
+ i, res->flags, pci_name(dev),
+ res->start - offset, res->start);
+#endif
+ }
+ }
+
+ /* Call machine specific resource fixup */
+/* FIXME
+ if (ppc_md.pcibios_fixup_resources)
+ ppc_md.pcibios_fixup_resources(dev);
+*/
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources);
+
+void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
+ struct resource *res)
+{
+ unsigned long offset = 0;
+ struct pci_controller *hose = dev->sysdata;
+
+ if (hose && res->flags & IORESOURCE_IO)
+ offset = (unsigned long)hose->io_base_virt - isa_io_base;
+ else if (hose && res->flags & IORESOURCE_MEM)
+ offset = hose->pci_mem_offset;
+ region->start = res->start - offset;
+ region->end = res->end - offset;
+}
+EXPORT_SYMBOL(pcibios_resource_to_bus);
+
+void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
+ struct pci_bus_region *region)
+{
+ unsigned long offset = 0;
+ struct pci_controller *hose = dev->sysdata;
+
+ if (hose && res->flags & IORESOURCE_IO)
+ offset = (unsigned long)hose->io_base_virt - isa_io_base;
+ else if (hose && res->flags & IORESOURCE_MEM)
+ offset = hose->pci_mem_offset;
+ res->start = region->start + offset;
+ res->end = region->end + offset;
+}
+EXPORT_SYMBOL(pcibios_bus_to_resource);
+
+/*
+ * 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 (size > 0x100) {
+ printk(KERN_ERR "PCI: I/O Region %s/%d too large"
+ " (%lld bytes)\n", pci_name(dev),
+ dev->resource - res, (unsigned long long)size);
+ }
+
+ if (start & 0x300) {
+ start = (start + 0x3ff) & ~0x3ff;
+ res->start = start;
+ }
+ }
+}
+EXPORT_SYMBOL(pcibios_align_resource);
+
+/*
+ * 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.
+ */
+
+static void __init
+pcibios_allocate_bus_resources(struct list_head *bus_list)
+{
+ struct pci_bus *bus;
+ int i;
+ struct resource *res, *pr;
+
+ /* Depth-First Search on bus tree */
+ list_for_each_entry(bus, bus_list, node) {
+ for (i = 0; i < 4; ++i) {
+ if ((res = bus->resource[i]) == NULL || !res->flags
+ || res->start > res->end)
+ continue;
+ if (bus->parent == NULL)
+ pr = (res->flags & IORESOURCE_IO)?
+ &ioport_resource: &iomem_resource;
+ else {
+ 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;
+ }
+ }
+
+ DBG("PCI: bridge rsrc %llx..%llx (%lx), parent %p\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end, res->flags, pr);
+ if (pr) {
+ 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_ERR "PCI: Cannot allocate resource region "
+ "%d of PCI bridge %d\n", i, bus->number);
+ if (pci_relocate_bridge_resource(bus, i))
+ bus->resource[i] = NULL;
+ }
+ pcibios_allocate_bus_resources(&bus->children);
+ }
+}
+
+/*
+ * 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;
+ DBG(KERN_INFO "PCI: reparented %s [%llx..%llx] under %s\n",
+ p->name, (unsigned long long)p->start,
+ (unsigned long long)p->end, res->name);
+ }
+ return 0;
+}
+
+/*
+ * A bridge has been allocated a range which is outside the range
+ * of its parent bridge, so it needs to be moved.
+ */
+static int __init
+pci_relocate_bridge_resource(struct pci_bus *bus, int i)
+{
+ struct resource *res, *pr, *conflict;
+ unsigned long try, size;
+ int j;
+ struct pci_bus *parent = bus->parent;
+
+ if (parent == NULL) {
+ /* shouldn't ever happen */
+ printk(KERN_ERR "PCI: can't move host bridge resource\n");
+ return -1;
+ }
+ res = bus->resource[i];
+ if (res == NULL)
+ return -1;
+ pr = NULL;
+ for (j = 0; j < 4; j++) {
+ struct resource *r = parent->resource[j];
+ if (!r)
+ continue;
+ if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))
+ continue;
+ if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) {
+ pr = r;
+ break;
+ }
+ if (res->flags & IORESOURCE_PREFETCH)
+ pr = r;
+ }
+ if (pr == NULL)
+ return -1;
+ size = res->end - res->start;
+ if (pr->start > pr->end || size > pr->end - pr->start)
+ return -1;
+ try = pr->end;
+ for (;;) {
+ res->start = try - size;
+ res->end = try;
+ if (probe_resource(bus->parent, pr, res, &conflict) == 0)
+ break;
+ if (conflict->start <= pr->start + size)
+ return -1;
+ try = conflict->start - 1;
+ }
+ if (request_resource(pr, res)) {
+ DBG(KERN_ERR "PCI: huh? couldn't move to %llx..%llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
+ return -1; /* "can't happen" */
+ }
+ update_bridge_base(bus, i);
+ printk(KERN_INFO "PCI: bridge %d resource %d moved to %llx..%llx\n",
+ bus->number, i, (unsigned long long)res->start,
+ (unsigned long long)res->end);
+ return 0;
+}
+
+static int __init
+probe_resource(struct pci_bus *parent, struct resource *pr,
+ struct resource *res, struct resource **conflict)
+{
+ struct pci_bus *bus;
+ struct pci_dev *dev;
+ struct resource *r;
+ int i;
+
+ for (r = pr->child; r != NULL; r = r->sibling) {
+ if (r->end >= res->start && res->end >= r->start) {
+ *conflict = r;
+ return 1;
+ }
+ }
+ list_for_each_entry(bus, &parent->children, node) {
+ for (i = 0; i < 4; ++i) {
+ if ((r = bus->resource[i]) == NULL)
+ continue;
+ if (!r->flags || r->start > r->end || r == res)
+ continue;
+ if (pci_find_parent_resource(bus->self, r) != pr)
+ continue;
+ if (r->end >= res->start && res->end >= r->start) {
+ *conflict = r;
+ return 1;
+ }
+ }
+ }
+ list_for_each_entry(dev, &parent->devices, bus_list) {
+ for (i = 0; i < 6; ++i) {
+ r = &dev->resource[i];
+ if (!r->flags || (r->flags & IORESOURCE_UNSET))
+ continue;
+ if (pci_find_parent_resource(dev, r) != pr)
+ continue;
+ if (r->end >= res->start && res->end >= r->start) {
+ *conflict = r;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static void __init
+update_bridge_base(struct pci_bus *bus, int i)
+{
+ struct resource *res = bus->resource[i];
+ u8 io_base_lo, io_limit_lo;
+ u16 mem_base, mem_limit;
+ u16 cmd;
+ unsigned long start, end, off;
+ struct pci_dev *dev = bus->self;
+ struct pci_controller *hose = dev->sysdata;
+
+ if (!hose) {
+ printk("update_bridge_base: no hose?\n");
+ return;
+ }
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, PCI_COMMAND,
+ cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+ if (res->flags & IORESOURCE_IO) {
+ off = (unsigned long) hose->io_base_virt - isa_io_base;
+ start = res->start - off;
+ end = res->end - off;
+ io_base_lo = (start >> 8) & PCI_IO_RANGE_MASK;
+ io_limit_lo = (end >> 8) & PCI_IO_RANGE_MASK;
+ if (end > 0xffff) {
+ pci_write_config_word(dev, PCI_IO_BASE_UPPER16,
+ start >> 16);
+ pci_write_config_word(dev, PCI_IO_LIMIT_UPPER16,
+ end >> 16);
+ io_base_lo |= PCI_IO_RANGE_TYPE_32;
+ } else
+ io_base_lo |= PCI_IO_RANGE_TYPE_16;
+ pci_write_config_byte(dev, PCI_IO_BASE, io_base_lo);
+ pci_write_config_byte(dev, PCI_IO_LIMIT, io_limit_lo);
+
+ } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH))
+ == IORESOURCE_MEM) {
+ off = hose->pci_mem_offset;
+ mem_base = ((res->start - off) >> 16) & PCI_MEMORY_RANGE_MASK;
+ mem_limit = ((res->end - off) >> 16) & PCI_MEMORY_RANGE_MASK;
+ pci_write_config_word(dev, PCI_MEMORY_BASE, mem_base);
+ pci_write_config_word(dev, PCI_MEMORY_LIMIT, mem_limit);
+
+ } else if ((res->flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH))
+ == (IORESOURCE_MEM | IORESOURCE_PREFETCH)) {
+ off = hose->pci_mem_offset;
+ mem_base = ((res->start - off) >> 16) & PCI_PREF_RANGE_MASK;
+ mem_limit = ((res->end - off) >> 16) & PCI_PREF_RANGE_MASK;
+ pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, mem_base);
+ pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, mem_limit);
+
+ } else {
+ DBG(KERN_ERR "PCI: ugh, bridge %s res %d has flags=%lx\n",
+ pci_name(dev), i, res->flags);
+ }
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+}
+
+static inline void alloc_resource(struct pci_dev *dev, int idx)
+{
+ struct resource *pr, *r = &dev->resource[idx];
+
+ DBG("PCI:%s: Resource %d: %016llx-%016llx (f=%lx)\n",
+ pci_name(dev), idx, (unsigned long long)r->start,
+ (unsigned long long)r->end, r->flags);
+ pr = pci_find_parent_resource(dev, r);
+ if (!pr || request_resource(pr, r) < 0) {
+ printk(KERN_ERR "PCI: Cannot allocate resource region %d"
+ " of device %s\n", idx, pci_name(dev));
+ if (pr)
+ DBG("PCI: parent is %p: %016llx-%016llx (f=%lx)\n",
+ pr, (unsigned long long)pr->start,
+ (unsigned long long)pr->end, 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 < 6; idx++) {
+ r = &dev->resource[idx];
+ if (r->parent) /* Already allocated */
+ continue;
+ if (!r->flags || (r->flags & IORESOURCE_UNSET))
+ continue; /* Not assigned at all */
+ 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 & IORESOURCE_ROM_ENABLE) {
+ /* Turn the ROM off, leave the resource region, but keep it unregistered. */
+ u32 reg;
+ DBG("PCI: Switching off ROM of %s\n", pci_name(dev));
+ r->flags &= ~IORESOURCE_ROM_ENABLE;
+ pci_read_config_dword(dev, dev->rom_base_reg, ®);
+ pci_write_config_dword(dev, dev->rom_base_reg,
+ reg & ~PCI_ROM_ADDRESS_ENABLE);
+ }
+ }
+}
+
+static void __init
+pcibios_assign_resources(void)
+{
+ struct pci_dev *dev = NULL;
+ int idx;
+ struct resource *r;
+
+ for_each_pci_dev(dev) {
+ int class = dev->class >> 8;
+
+ /* Don't touch classless devices and host bridges */
+ if (!class || class == PCI_CLASS_BRIDGE_HOST)
+ continue;
+
+ for (idx = 0; idx < 6; idx++) {
+ r = &dev->resource[idx];
+
+ /*
+ * We shall assign a new address to this resource,
+ * either because the BIOS (sic) forgot to do so
+ * or because we have decided the old address was
+ * unusable for some reason.
+ */
+#if 0
+ if ((r->flags & IORESOURCE_UNSET) && r->end &&
+ (!ppc_md.pcibios_enable_device_hook ||
+ !ppc_md.pcibios_enable_device_hook(dev, 1))) {
+ r->flags &= ~IORESOURCE_UNSET;
+ pci_assign_resource(dev, idx);
+ }
+#else
+ if ((r->flags & IORESOURCE_UNSET) && r->end) {
+ r->flags &= ~IORESOURCE_UNSET;
+ pci_assign_resource(dev, idx);
+ }
+#endif
+ }
+
+#if 0 /* don't assign ROMs */
+ r = &dev->resource[PCI_ROM_RESOURCE];
+ r->end -= r->start;
+ r->start = 0;
+ if (r->end)
+ pci_assign_resource(dev, PCI_ROM_RESOURCE);
+#endif
+ }
+}
+
+
+int
+pcibios_enable_resources(struct pci_dev *dev, int mask)
+{
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for (idx=0; idx<6; idx++) {
+ /* Only set up the requested stuff */
+ if (!(mask & (1<<idx)))
+ continue;
+
+ r = &dev->resource[idx];
+ if (r->flags & IORESOURCE_UNSET) {
+ printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev));
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ cmd |= PCI_COMMAND_MEMORY;
+ if (cmd != old_cmd) {
+ printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+static int next_controller_index;
+
+struct pci_controller * __init
+pcibios_alloc_controller(void)
+{
+ struct pci_controller *hose;
+
+ hose = (struct pci_controller *)alloc_bootmem(sizeof(*hose));
+ memset(hose, 0, sizeof(struct pci_controller));
+
+ *hose_tail = hose;
+ hose_tail = &hose->next;
+
+ hose->index = next_controller_index++;
+
+ return hose;
+}
+
+void pcibios_make_OF_bus_map(void)
+{
+}
+
+/* Add sysfs properties */
+void pcibios_add_platform_entries(struct pci_dev *pdev)
+{
+}
+
+
+static int __init
+pcibios_init(void)
+{
+ struct pci_controller *hose;
+ struct pci_bus *bus;
+ int next_busno;
+
+ printk(KERN_INFO "PCI: Probing PCI hardware\n");
+
+ /* Scan all of the recorded PCI controllers. */
+ for (next_busno = 0, hose = hose_head; hose; hose = hose->next) {
+ if (pci_assign_all_buses)
+ hose->first_busno = next_busno;
+ hose->last_busno = 0xff;
+ bus = pci_scan_bus(hose->first_busno, hose->ops, hose);
+ hose->last_busno = bus->subordinate;
+ 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.
+ */
+/* FIXME
+ if (pci_assign_all_buses && have_of)
+ pcibios_make_OF_bus_map();
+*/
+
+ /* Do machine dependent PCI interrupt routing */
+ /* These two funcs are declared weak so test to see if they are
+ provided, otherwise don't call them */
+ if (microblaze_pci_swizzle && microblaze_pci_map_irq)
+ pci_fixup_irqs(microblaze_pci_swizzle, microblaze_pci_map_irq);
+
+ /* Call machine dependent fixup */
+/* FIXME
+ if (ppc_md.pcibios_fixup)
+ ppc_md.pcibios_fixup();
+*/
+
+ /* Allocate and assign resources */
+ pcibios_allocate_bus_resources(&pci_root_buses);
+ pcibios_allocate_resources(0);
+ pcibios_allocate_resources(1);
+ pcibios_assign_resources();
+
+ /* Call machine dependent post-init code */
+/* FIXME
+ if (ppc_md.pcibios_after_init)
+ ppc_md.pcibios_after_init();
+*/
+
+ return 0;
+}
+
+subsys_initcall(pcibios_init);
+
+unsigned char __init
+common_swizzle(struct pci_dev *dev, unsigned char *pinp)
+{
+ struct pci_controller *hose = dev->sysdata;
+
+ if (dev->bus->number != hose->first_busno) {
+ u8 pin = *pinp;
+ do {
+ pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
+ /* Move up the chain of bridges. */
+ dev = dev->bus->self;
+ } while (dev->bus->self);
+ *pinp = pin;
+
+ /* The slot is the idsel of the last bridge. */
+ }
+ return PCI_SLOT(dev->devfn);
+}
+
+unsigned long resource_fixup(struct pci_dev * dev, struct resource * res,
+ unsigned long start, unsigned long size)
+{
+ return start;
+}
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+ struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+ unsigned long io_offset;
+ struct resource *res;
+ int i;
+
+ io_offset = (unsigned long)hose->io_base_virt - isa_io_base;
+ if (bus->parent == NULL) {
+ /* This is a host bridge - fill in its resources */
+ hose->bus = bus;
+
+ bus->resource[0] = res = &hose->io_resource;
+ if (!res->flags) {
+ if (io_offset)
+ printk(KERN_ERR "I/O resource not set for host"
+ " bridge %d\n", hose->index);
+ res->start = 0;
+ res->end = IO_SPACE_LIMIT;
+ res->flags = IORESOURCE_IO;
+ }
+ res->start += io_offset;
+ res->end += io_offset;
+
+ for (i = 0; i < 3; ++i) {
+ res = &hose->mem_resources[i];
+ if (!res->flags) {
+ if (i > 0)
+ continue;
+ printk(KERN_ERR "Memory resource not set for "
+ "host bridge %d\n", hose->index);
+ res->start = hose->pci_mem_offset;
+ res->end = ~0U;
+ res->flags = IORESOURCE_MEM;
+ }
+ bus->resource[i+1] = res;
+ }
+ } else {
+ /* This is a subordinate bridge */
+ pci_read_bridge_bases(bus);
+
+ for (i = 0; i < 4; ++i) {
+ if ((res = bus->resource[i]) == NULL)
+ continue;
+ if (!res->flags)
+ continue;
+ if (io_offset && (res->flags & IORESOURCE_IO)) {
+ res->start += io_offset;
+ res->end += io_offset;
+ } else if (hose->pci_mem_offset
+ && (res->flags & IORESOURCE_MEM)) {
+ res->start += hose->pci_mem_offset;
+ res->end += hose->pci_mem_offset;
+ }
+ }
+ }
+
+/* FIXME
+ if (ppc_md.pcibios_fixup_bus)
+ ppc_md.pcibios_fixup_bus(bus);
+*/
+}
+
+char __init *pcibios_setup(char *str)
+{
+ return str;
+}
+
+/* the next one is stolen from the alpha port... */
+void __init
+pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+ /* XXX FIXME - update OF device tree node interrupt property */
+}
+
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+ u16 cmd, old_cmd;
+ int idx;
+ struct resource *r;
+
+/* FIXME
+ if (ppc_md.pcibios_enable_device_hook)
+ if (ppc_md.pcibios_enable_device_hook(dev, 0))
+ return -EINVAL;
+*/
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ old_cmd = cmd;
+ for (idx=0; idx<6; idx++) {
+ r = &dev->resource[idx];
+ if (r->flags & IORESOURCE_UNSET) {
+ printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev));
+ return -EINVAL;
+ }
+ if (r->flags & IORESOURCE_IO)
+ cmd |= PCI_COMMAND_IO;
+ if (r->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ if (cmd != old_cmd) {
+ printk("PCI: Enabling device %s (%04x -> %04x)\n",
+ pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+struct pci_controller*
+pci_bus_to_hose(int bus)
+{
+ struct pci_controller* hose = hose_head;
+
+ for (; hose; hose = hose->next)
+ if (bus >= hose->first_busno && bus <= hose->last_busno)
+ return hose;
+ return NULL;
+}
+
+void __iomem *
+pci_bus_io_base(unsigned int bus)
+{
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_hose(bus);
+ if (!hose)
+ return NULL;
+ return hose->io_base_virt;
+}
+
+unsigned long
+pci_bus_io_base_phys(unsigned int bus)
+{
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_hose(bus);
+ if (!hose)
+ return 0;
+ return hose->io_base_phys;
+}
+
+unsigned long
+pci_bus_mem_base_phys(unsigned int bus)
+{
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_hose(bus);
+ if (!hose)
+ return 0;
+ return hose->pci_mem_offset;
+}
+
+unsigned long
+pci_resource_to_bus(struct pci_dev *pdev, struct resource *res)
+{
+ /* Hack alert again ! See comments in chrp_pci.c
+ */
+ struct pci_controller* hose =
+ (struct pci_controller *)pdev->sysdata;
+ if (hose && res->flags & IORESOURCE_MEM)
+ return res->start - hose->pci_mem_offset;
+ /* We may want to do something with IOs here... */
+ return res->start;
+}
+
+
+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_hose(dev->bus->number);
+ 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 = 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)
+{
+ unsigned long prot = pgprot_val(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;
+ }
+
+ /* XXX would be nice to have a way to ask for write-through */
+ prot |= _PAGE_NO_CACHE;
+ if (write_combine)
+ prot &= ~_PAGE_GUARDED;
+ else
+ prot |= _PAGE_GUARDED;
+
+ printk("PCI map for %s:%llx, prot: %lx\n", pci_name(dev),
+ (unsigned long long)rp->start, prot);
+
+ return __pgprot(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 protection)
+{
+ struct pci_dev *pdev = NULL;
+ struct resource *found = NULL;
+ unsigned long prot = pgprot_val(protection);
+ unsigned long offset = pfn << PAGE_SHIFT;
+ int i;
+
+ if (page_is_ram(pfn))
+ return __pgprot(prot);
+
+ prot |= _PAGE_NO_CACHE | _PAGE_GUARDED;
+
+ 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 &= ~_PAGE_GUARDED;
+ pci_dev_put(pdev);
+ }
+
+ DBG("non-PCI map for %lx, prot: %lx\n", offset, prot);
+
+ return __pgprot(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 = 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;
+}
+
+/* Obsolete functions. Should be removed once the symbios driver
+ * is fixed
+ */
+unsigned long
+phys_to_bus(unsigned long pa)
+{
+ struct pci_controller *hose;
+ int i;
+
+ for (hose = hose_head; hose; hose = hose->next) {
+ for (i = 0; i < 3; ++i) {
+ if (pa >= hose->mem_resources[i].start
+ && pa <= hose->mem_resources[i].end) {
+ /*
+ * XXX the hose->pci_mem_offset really
+ * only applies to mem_resources[0].
+ * We need a way to store an offset for
+ * the others. -- paulus
+ */
+ if (i == 0)
+ pa -= hose->pci_mem_offset;
+ return pa;
+ }
+ }
+ }
+ /* hmmm, didn't find it */
+ return 0;
+}
+
+unsigned long
+pci_phys_to_bus(unsigned long pa, int busnr)
+{
+ struct pci_controller* hose = pci_bus_to_hose(busnr);
+ if (!hose)
+ return pa;
+ return pa - hose->pci_mem_offset;
+}
+
+unsigned long
+pci_bus_to_phys(unsigned int ba, int busnr)
+{
+ struct pci_controller* hose = pci_bus_to_hose(busnr);
+ if (!hose)
+ return ba;
+ return ba + hose->pci_mem_offset;
+}
+
+/* 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;
+}
+
+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_hose(dev->bus->number);
+ 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;
+}
+
+void __init pci_init_resource(struct resource *res, resource_size_t start,
+ resource_size_t end, int flags, char *name)
+{
+ res->start = start;
+ res->end = end;
+ res->flags = flags;
+ res->name = name;
+ res->parent = NULL;
+ res->sibling = NULL;
+ res->child = NULL;
+}
+
+void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
+{
+ unsigned long start = pci_resource_start(dev, bar);
+ unsigned long 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) {
+ /* FIXME */
+ printk("FIXME ioport_map\n");
+ return NULL;
+ //return ioport_map(start, len);
+ }
+ if (flags & IORESOURCE_MEM)
+ /* Not checking IORESOURCE_CACHEABLE because microblaze does
+ * not currently distinguish between ioremap and
+ * ioremap_nocache.
+ */
+ return ioremap(start, len);
+ /* What? */
+ return NULL;
+}
+
+void pci_iounmap(struct pci_dev *dev, void __iomem *addr)
+{
+ /* Nothing to do */
+}
+EXPORT_SYMBOL(pci_iomap);
+EXPORT_SYMBOL(pci_iounmap);
+
+unsigned long pci_address_to_pio(phys_addr_t address)
+{
+ struct pci_controller* hose = hose_head;
+
+ for (; hose; hose = hose->next) {
+ unsigned int size = hose->io_resource.end -
+ hose->io_resource.start + 1;
+ if (address >= hose->io_base_phys &&
+ address < (hose->io_base_phys + size)) {
+ unsigned long base =
+ (unsigned long)hose->io_base_virt - _IO_BASE;
+ return base + (address - hose->io_base_phys);
+ }
+ }
+ return (unsigned int)-1;
+}
+EXPORT_SYMBOL(pci_address_to_pio);
+
+/*
+ * 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 =
+{
+ null_read_config,
+ 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 == 0) {
+ hose = pci_bus_to_hose(busnr);
+ if (hose == 0)
+ 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)
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/pci/Makefile
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/pci/Makefile
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/pci/Makefile
@@ -1,5 +1,6 @@
#
# arch/microblaze/kernel/pci/Makefile
#
-# Empty Makefile to keep make clean happy
+obj-$(CONFIG_PCI)+=pci.o pci_auto.o indirect_pci.o
+
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/pci/indirect_pci.c
===================================================================
--- /dev/null
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/pci/indirect_pci.c
@@ -0,0 +1,172 @@
+/*
+ * 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>
+//#include <asm/machdep.h>
+
+#ifdef CONFIG_PPC_INDIRECT_PCI_BE
+#define PCI_CFG_OUT out_be32
+#else
+#define PCI_CFG_OUT out_le32
+#endif
+
+static int
+indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
+ int len, u32 *val)
+{
+ struct pci_controller *hose = bus->sysdata;
+ volatile void __iomem *cfg_data;
+ u8 cfg_type = 0;
+ u32 tmp_val;
+
+// printk("Hello from indirect_read_config(%04x,%04x,%d)\n",
+// devfn,offset,len);
+/* FIXME
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+*/
+ if (microblaze_pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (bus->number != hose->first_busno)
+ cfg_type = 1;
+
+// printk("cfg_type is %d\n",cfg_type);
+
+ tmp_val= (0x80000000 | ((bus->number - hose->bus_offset) << 16)
+ | (devfn << 8) | ((offset & 0xfc) | cfg_type));
+
+// printk("calling PCI_CFG_OUT(0x%08x,0x%08x)\n",hose->cfg_addr,tmp_val);
+
+ PCI_CFG_OUT(hose->cfg_addr,
+ (0x80000000 | ((bus->number - hose->bus_offset) << 16)
+ | (devfn << 8) | ((offset & 0xfc) | cfg_type)));
+
+ /*
+ * Note: the caller has already checked that offset is
+ * suitably aligned and that len is 1, 2 or 4.
+ */
+ /* Xilinx PCI controller only supports word read/writes. */
+// printk("calling in_le32(0x%08x)\n",hose->cfg_data);
+ tmp_val = in_le32(hose->cfg_data);
+
+// printk("offset:%d, Shift = %d\n",offset, (offset & 3) << 3);
+
+ switch (len) {
+ case 1:
+ *val = (tmp_val >> ((offset & 3) << 3)) & 0xFF;
+ break;
+ case 2:
+ *val = (tmp_val >> ((offset & 3) << 3)) & 0xFFFF;
+ break;
+ default:
+ *val = tmp_val;
+ 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 = bus->sysdata;
+ volatile void __iomem *cfg_data;
+ u8 cfg_type = 0;
+ u32 tmp_val, mask;
+ int shift;
+
+ //printk("Hello from indirect_write_config(%04x,%04x,%d,%08x)\n",
+// devfn,offset,len,val);
+/* FIXME
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+*/
+ if (microblaze_pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (bus->number != hose->first_busno)
+ cfg_type = 1;
+
+ PCI_CFG_OUT(hose->cfg_addr,
+ (0x80000000 | ((bus->number - hose->bus_offset) << 16)
+ | (devfn << 8) | ((offset & 0xfc) | cfg_type)));
+
+ /*
+ * Note: the caller has already checked that offset is
+ * suitably aligned and that len is 1, 2 or 4.
+ */
+ /* Xilinx Controller only supports 32 bit ops, so do read/modify/write
+ for smaller ops */
+ cfg_data = hose->cfg_data;
+ switch (len) {
+ case 1:
+ tmp_val = in_le32(cfg_data);
+ shift = (offset & 3)<<3;
+ mask=~(0xff << shift);
+ tmp_val &= mask;
+ tmp_val |= val << shift;
+ break;
+ case 2:
+ tmp_val = in_le32(cfg_data);
+ shift = (offset & 3)<<3;
+ mask=~(0xffff << shift);
+ tmp_val &= mask;
+ tmp_val |= val << shift;
+ break;
+ default:
+ tmp_val=val;
+ break;
+ }
+ out_le32(cfg_data, tmp_val);
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops indirect_pci_ops =
+{
+ indirect_read_config,
+ indirect_write_config
+};
+
+void __init
+setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr,
+ void __iomem * cfg_data)
+{
+ hose->cfg_addr = cfg_addr;
+ hose->cfg_data = cfg_data;
+ hose->ops = &indirect_pci_ops;
+}
+
+void __init
+setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
+{
+ unsigned long base = cfg_addr & PAGE_MASK;
+ void __iomem *mbase, *addr, *data;
+
+ mbase = ioremap(base, PAGE_SIZE);
+ addr = mbase + (cfg_addr & ~PAGE_MASK);
+ if ((cfg_data & PAGE_MASK) != base)
+ mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
+ data = mbase + (cfg_data & ~PAGE_MASK);
+ setup_indirect_pci_nomap(hose, addr, data);
+}
Index: b/software/linux-2.6.x-petalogix/arch/microblaze/Makefile
===================================================================
--- a/software/linux-2.6.x-petalogix/arch/microblaze/Makefile
+++ b/software/linux-2.6.x-petalogix/arch/microblaze/Makefile
@@ -51,7 +51,7 @@ core-y += arch/microblaze/kernel/ arch/
ifdef CONFIG_PCI
-core-y += pci/
+core-y += arch/microblaze/pci/
endif
boot := arch/microblaze/boot
Index: b/software/linux-2.6.x-petalogix/drivers/pci/proc.c
===================================================================
--- a/software/linux-2.6.x-petalogix/drivers/pci/proc.c
+++ b/software/linux-2.6.x-petalogix/drivers/pci/proc.c
@@ -13,6 +13,8 @@
#include <linux/seq_file.h>
#include <linux/smp_lock.h>
+/* FIXME JW Why are we having to include this - no one else does */
+#include <linux/capability.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include "pci.h"
Index: b/software/linux-2.6.x-petalogix/drivers/pci/Makefile
===================================================================
--- a/software/linux-2.6.x-petalogix/drivers/pci/Makefile
+++ b/software/linux-2.6.x-petalogix/drivers/pci/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_PPC32) += setup-irq.o
obj-$(CONFIG_PPC64) += setup-bus.o
obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
obj-$(CONFIG_X86_VISWS) += setup-irq.o
+obj-$(CONFIG_MICROBLAZE) += setup-irq.o
#
# ACPI Related PCI FW Functions