
Qemu emulates the Galileo GT64120 System Controller which provides a CPU bus to PCI bus bridge.
The patch adds driver for this bridge and enables PCI support for the emulated Malta board.
Signed-off-by: Gabor Juhos juhosg@openwrt.org Cc: Daniel Schwierzeck daniel.schwierzeck@googlemail.com --- Changes since RFC: - use a C struct to define the register layout instead of using a base address plus offset notation - remove custom IO accessors --- board/qemu-malta/Makefile | 5 +- board/qemu-malta/pci.c | 168 ++++++++++++++++++++++++++++++++++++++++++ include/configs/qemu-malta.h | 6 ++ 3 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 board/qemu-malta/pci.c
diff --git a/board/qemu-malta/Makefile b/board/qemu-malta/Makefile index 6251bb8..59c1b1d 100644 --- a/board/qemu-malta/Makefile +++ b/board/qemu-malta/Makefile @@ -25,7 +25,10 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).o
-COBJS = $(BOARD).o +COBJS-y += $(BOARD).o +COBJS-$(CONFIG_PCI) += pci.o + +COBJS := $(COBJS-y) SOBJS = lowlevel_init.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) diff --git a/board/qemu-malta/pci.c b/board/qemu-malta/pci.c new file mode 100644 index 0000000..fd9193b --- /dev/null +++ b/board/qemu-malta/pci.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2013 Gabor Juhos juhosg@openwrt.org + * + * Based on the Linux implementation. + * Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc. + * Authors: Carsten Langgaard carstenl@mips.com + * Maciej W. Rozycki macro@mips.com + * + * 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 <common.h> +#include <asm/addrspace.h> +#include <asm/gt64120.h> +#include <asm/malta.h> +#include <asm/io.h> +#include <pci.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +struct gt64120_regs { + u8 unused_000[0xc18]; + u32 intrcause; + u8 unused_c1c[0x0dc]; + u32 pci0_cfgaddr; + u32 pci0_cfgdata; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static struct gt64120_regs *gt_regs; + +/* + * PCI controller "hose" value + */ +static struct pci_controller hose; + +#define GT_INTRCAUSE_ABORT_BITS \ + (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT) + +static int gt_config_access(unsigned char access_type, pci_dev_t bdf, + int where, u32 *data) +{ + unsigned int bus = PCI_BUS(bdf); + unsigned int dev = PCI_DEV(bdf); + unsigned int devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf); + u32 intr; + u32 addr; + u32 val; + + if (bus == 0 && dev >= 31) { + /* Because of a bug in the galileo (for slot 31). */ + return -1; + } + + if (access_type == PCI_ACCESS_WRITE) + debug("PCI WR %02x:%02x.%x reg:%02d data:%08x\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), + where, *data); + + /* Clear cause register bits */ + __raw_writel(~GT_INTRCAUSE_ABORT_BITS, >_regs->intrcause); + + addr = GT_PCI0_CFGADDR_CONFIGEN_BIT; + addr |= bus << GT_PCI0_CFGADDR_BUSNUM_SHF; + addr |= devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF; + addr |= (where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF; + + /* Setup address */ + __raw_writel(addr, >_regs->pci0_cfgaddr); + + if (access_type == PCI_ACCESS_WRITE) { + if (bus == 0 && dev == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + val = *data; + } else + val = cpu_to_le32(*data); + + __raw_writel(val, >_regs->pci0_cfgdata); + } else { + val = __raw_readl(>_regs->pci0_cfgdata); + + if (bus == 0 && dev == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + *data = val; + } else + *data = le32_to_cpu(val); + } + + /* Check for master or target abort */ + intr = __raw_readl(>_regs->intrcause); + if (intr & GT_INTRCAUSE_ABORT_BITS) { + /* Error occurred, clear abort bits */ + __raw_writel(~GT_INTRCAUSE_ABORT_BITS, >_regs->intrcause); + return -1; + } + + if (access_type == PCI_ACCESS_READ) + debug("PCI RD %02x:%02x.%x reg:%02d data:%08x\n", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data); + + return 0; +} + +static int gt_read_config_dword(struct pci_controller *hose, pci_dev_t dev, + int where, u32 *value) +{ + *value = 0xffffffff; + return gt_config_access(PCI_ACCESS_READ, dev, where, value); +} + +static int gt_write_config_dword(struct pci_controller *hose, pci_dev_t dev, + int where, u32 value) +{ + u32 data = value; + + return gt_config_access(PCI_ACCESS_WRITE, dev, where, &data); +} + +void pci_init_board(void) +{ + set_io_port_base(CKSEG1ADDR(MALTA_IO_PORT_BASE)); + + gt_regs = (struct gt64120_regs *) CKSEG1ADDR(MALTA_GT_BASE); + + hose.first_busno = 0; + hose.last_busno = 0xff; + + /* System memory space */ + pci_set_region(&hose.regions[0], + 0x00000000, 0x00000000, + CONFIG_SYS_MEM_SIZE, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + /* PCI memory space */ + pci_set_region(&hose.regions[1], + 0x10000000, 0x10000000, + 128 * 1024 * 1024, + PCI_REGION_MEM); + + /* PCI I/O space */ + pci_set_region(&hose.regions[2], + 0x0000000, 0x0000000, + 0x20000, + PCI_REGION_IO); + + hose.region_count = 3; + + pci_set_ops(&hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + gt_read_config_dword, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + gt_write_config_dword); + + pci_register_hose(&hose); + hose.last_busno = pci_hose_scan(&hose); +} diff --git a/include/configs/qemu-malta.h b/include/configs/qemu-malta.h index 881c15d..36b584a 100644 --- a/include/configs/qemu-malta.h +++ b/include/configs/qemu-malta.h @@ -17,6 +17,9 @@ */ #define CONFIG_QEMU_MALTA
+#define CONFIG_PCI +#define CONFIG_PCI_PNP + /* * CPU Configuration */ @@ -30,6 +33,7 @@
#define CONFIG_SWAP_IO_SPACE
+ /* * Memory map */ @@ -108,6 +112,8 @@ #undef CONFIG_CMD_NET #undef CONFIG_CMD_NFS
+#define CONFIG_CMD_PCI + #define CONFIG_SYS_LONGHELP /* verbose help, undef to save memory */
#endif /* _QEMU_MALTA_CONFIG_H */