Quellcode durchsuchen

MIPS: qemu-malta: add PCI support

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>
Gabor Juhos vor 11 Jahren
Ursprung
Commit
feaa606627

+ 12 - 0
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);
+}

+ 1 - 0
drivers/pci/Makefile

@@ -12,6 +12,7 @@ LIB	:= $(obj)libpci.o
 COBJS-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o
 COBJS-$(CONFIG_PCI) += pci.o pci_auto.o
 COBJS-$(CONFIG_PCI_INDIRECT_BRIDGE) += 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

+ 178 - 0
drivers/pci/pci_gt64120.c

@@ -0,0 +1,178 @@
+/*
+ * 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 <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);
+}
+
+void 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);
+}

+ 6 - 0
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 */

+ 19 - 0
include/pci_gt64120.h

@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 Gabor Juhos <juhosg@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
+
+void 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 */