[U-Boot] [PATCH v3 06/10] MIPS: qemu-malta: add PCI support

Gabor Juhos juhosg at openwrt.org
Wed May 22 15:57:42 CEST 2013


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 at openwrt.org>
Cc: Daniel Schwierzeck <daniel.schwierzeck at googlemail.com>
---
Changes since v2:
 - move the PCI driver to drivers/pci
 - fix checkpatch warnings
 - rebased against the master branch of git.denx.de/u-boot.git

Changes since v1:
 - rebased against mips/testing

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

Screenshot:

  U-Boot 2013.04-00242-g8960ff8 (May 22 2013 - 13:25:13)

  Board: MIPS Malta CoreLV (Qemu)
  DRAM:  256 MiB
  pflash_write: Unimplemented flash cmd sequence (offset 0000000000000000, wcycle 0x0 cmd 0x0 value 0xf0)
  Flash: 4 MiB
  Using default environment

  In:    serial
  Out:   serial
  Err:   serial
  qemu-malta # pci
  Scanning PCI devices on bus 0
  BusDevFun  VendorId   DeviceId   Device Class       Sub-Class
  _____________________________________________________________
  00.00.00   0x11ab     0x4620     Bridge device           0x00
  00.0a.00   0x8086     0x7110     Bridge device           0x01
  00.0a.01   0x8086     0x7111     Mass storage controller 0x01
  00.0a.02   0x8086     0x7112     Serial bus controller   0x03
  00.0a.03   0x8086     0x7113     Bridge device           0x80
  00.0b.00   0x1022     0x2000     Network controller      0x00
  qemu-malta #
---
 board/qemu-malta/qemu-malta.c |   12 +++
 drivers/pci/Makefile          |    1 +
 drivers/pci/pci_gt64120.c     |  178 +++++++++++++++++++++++++++++++++++++++++
 include/configs/qemu-malta.h  |    6 ++
 include/pci_gt64120.h         |   19 +++++
 5 files changed, 216 insertions(+)
 create mode 100644 drivers/pci/pci_gt64120.c
 create mode 100644 include/pci_gt64120.h

diff --git a/board/qemu-malta/qemu-malta.c b/board/qemu-malta/qemu-malta.c
index 449da9c..e3a733f 100644
--- a/board/qemu-malta/qemu-malta.c
+++ b/board/qemu-malta/qemu-malta.c
@@ -8,8 +8,10 @@
 
 #include <common.h>
 
+#include <asm/addrspace.h>
 #include <asm/io.h>
 #include <asm/malta.h>
+#include <pci_gt64120.h>
 
 phys_size_t initdram(int board_type)
 {
@@ -29,3 +31,13 @@ void _machine_restart(void)
 	reset_base = (void __iomem *)CKSEG1ADDR(MALTA_RESET_BASE);
 	__raw_writel(GORESET, reset_base);
 }
+
+void pci_init_board(void)
+{
+	set_io_port_base(CKSEG1ADDR(MALTA_IO_PORT_BASE));
+
+	gt64120_pci_init((void *)CKSEG1ADDR(MALTA_GT_BASE),
+			 0x00000000, 0x00000000, CONFIG_SYS_MEM_SIZE,
+			 0x10000000, 0x10000000, 128 * 1024 * 1024,
+			 0x00000000, 0x00000000, 0x20000);
+}
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 1ae35d3..02d2309 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -27,6 +27,7 @@ LIB	:= $(obj)libpci.o
 
 COBJS-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o
 COBJS-$(CONFIG_PCI) += pci.o pci_auto.o pci_indirect.o
+COBJS-$(CONFIG_PCI_GT64120) += pci_gt64120.o
 COBJS-$(CONFIG_FTPCI100) += pci_ftpci100.o
 COBJS-$(CONFIG_IXP_PCI) += pci_ixp.o
 COBJS-$(CONFIG_SH4_PCI) += pci_sh4.o
diff --git a/drivers/pci/pci_gt64120.c b/drivers/pci/pci_gt64120.c
new file mode 100644
index 0000000..c2f2049
--- /dev/null
+++ b/drivers/pci/pci_gt64120.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013 Gabor Juhos <juhosg at openwrt.org>
+ *
+ * Based on the Linux implementation.
+ *   Copyright (C) 1999, 2000, 2004  MIPS Technologies, Inc.
+ *   Authors: Carsten Langgaard <carstenl at mips.com>
+ *            Maciej W. Rozycki <macro at 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 <gt64120.h>
+#include <pci.h>
+#include <pci_gt64120.h>
+
+#include <asm/io.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;
+};
+
+struct gt64120_pci_controller {
+	struct pci_controller hose;
+	struct gt64120_regs *regs;
+};
+
+static inline struct gt64120_pci_controller *
+hose_to_gt64120(struct pci_controller *hose)
+{
+	return container_of(hose, struct gt64120_pci_controller, hose);
+}
+
+#define GT_INTRCAUSE_ABORT_BITS	\
+		(GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)
+
+static int gt_config_access(struct gt64120_pci_controller *gt,
+			    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 */
+	writel(~GT_INTRCAUSE_ABORT_BITS, &gt->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 */
+	writel(addr, &gt->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);
+		}
+
+		writel(val, &gt->regs->pci0_cfgdata);
+	} else {
+		val = readl(&gt->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 = readl(&gt->regs->intrcause);
+	if (intr & GT_INTRCAUSE_ABORT_BITS) {
+		/* Error occurred, clear abort bits */
+		writel(~GT_INTRCAUSE_ABORT_BITS, &gt->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)
+{
+	struct gt64120_pci_controller *gt = hose_to_gt64120(hose);
+
+	*value = 0xffffffff;
+	return gt_config_access(gt, PCI_ACCESS_READ, dev, where, value);
+}
+
+static int gt_write_config_dword(struct pci_controller *hose, pci_dev_t dev,
+				 int where, u32 value)
+{
+	struct gt64120_pci_controller *gt = hose_to_gt64120(hose);
+	u32 data = value;
+
+	return gt_config_access(gt, PCI_ACCESS_WRITE, dev, where, &data);
+}
+
+int gt64120_pci_init(void *regs, unsigned long sys_bus, unsigned long sys_phys,
+		     unsigned long sys_size, unsigned long mem_bus,
+		     unsigned long mem_phys, unsigned long mem_size,
+		     unsigned long io_bus, unsigned long io_phys,
+		     unsigned long io_size)
+{
+	static struct gt64120_pci_controller global_gt;
+	struct gt64120_pci_controller *gt;
+	struct pci_controller *hose;
+
+	gt = &global_gt;
+	gt->regs = regs;
+
+	hose = &gt->hose;
+
+	hose->first_busno = 0;
+	hose->last_busno = 0;
+
+	/* System memory space */
+	pci_set_region(&hose->regions[0], sys_bus, sys_phys, sys_size,
+		       PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+
+	/* PCI memory space */
+	pci_set_region(&hose->regions[1], mem_bus, mem_phys, mem_size,
+		       PCI_REGION_MEM);
+
+	/* PCI I/O space */
+	pci_set_region(&hose->regions[2], io_bus, io_phys, io_size,
+		       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 436bb49..ef44d3d 100644
--- a/include/configs/qemu-malta.h
+++ b/include/configs/qemu-malta.h
@@ -17,6 +17,10 @@
  */
 #define CONFIG_QEMU_MALTA
 
+#define CONFIG_PCI
+#define CONFIG_PCI_GT64120
+#define CONFIG_PCI_PNP
+
 /*
  * CPU Configuration
  */
@@ -104,6 +108,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 */
diff --git a/include/pci_gt64120.h b/include/pci_gt64120.h
new file mode 100644
index 0000000..aae6b33
--- /dev/null
+++ b/include/pci_gt64120.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 Gabor Juhos <juhosg at openwrt.org>
+ *
+ * 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.
+ */
+
+#ifndef _PCI_GT64120_H
+#define _PCI_GT64120_H
+
+int gt64120_pci_init(void *regs, unsigned long sys_bus, unsigned long sys_phys,
+		     unsigned long sys_size, unsigned long mem_bus,
+		     unsigned long mem_phys, unsigned long mem_size,
+		     unsigned long io_bus, unsigned long io_phys,
+		     unsigned long io_size);
+
+
+#endif /* _PCI_GT64120_H */
-- 
1.7.10



More information about the U-Boot mailing list