From 784be92e398ef9b1c26caaf617af0f3bfc2ee91b Mon Sep 17 00:00:00 2001
From: Gabor Juhos <juhosg@openwrt.org>
Date: Tue, 14 Feb 2012 17:32:42 +0000
Subject: [PATCH] ramips: rt3883: add PCI support

SVN-Revision: 30527
---
 .../mips/include/asm/mach-ralink/rt3883.h     |  22 +
 .../include/asm/mach-ralink/rt3883_regs.h     |   8 +
 .../ramips/files/arch/mips/pci/pci-rt3883.c   | 487 ++++++++++++++++++
 .../ramips/files/arch/mips/ralink/Kconfig     |   1 +
 .../106-rt3883-pci-support.patch              |  10 +
 .../patches-3.2/106-rt3883-pci-support.patch  |  10 +
 target/linux/ramips/rt3883/config-2.6.39      |   4 +
 target/linux/ramips/rt3883/config-3.2         |   7 +-
 8 files changed, 548 insertions(+), 1 deletion(-)
 create mode 100644 target/linux/ramips/files/arch/mips/pci/pci-rt3883.c
 create mode 100644 target/linux/ramips/patches-2.6.39/106-rt3883-pci-support.patch
 create mode 100644 target/linux/ramips/patches-3.2/106-rt3883-pci-support.patch

diff --git a/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883.h b/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883.h
index 004acfb73b..a7a4b074ab 100644
--- a/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883.h
+++ b/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883.h
@@ -26,6 +26,9 @@ void rt3883_detect_sys_type(void);
 #define RT3883_INTC_IRQ_BASE	(RT3883_CPU_IRQ_BASE + RT3883_CPU_IRQ_COUNT)
 #define RT3883_INTC_IRQ_COUNT	32
 #define RT3883_GPIO_IRQ_BASE	(RT3883_INTC_IRQ_BASE + RT3883_INTC_IRQ_COUNT)
+#define RT3883_GPIO_IRQ_COUNT	96
+#define RT3883_PCI_IRQ_BASE	(RT3883_GPIO_IRQ_BASE + RT3883_GPIO_IRQ_COUNT)
+#define RT3883_PCI_IRQ_COUNT	3
 
 #define RT3883_CPU_IRQ_INTC	(RT3883_CPU_IRQ_BASE + 2)
 #define RT3883_CPU_IRQ_PCI	(RT3883_CPU_IRQ_BASE + 4)
@@ -48,6 +51,10 @@ void rt3883_detect_sys_type(void);
 #define RT3883_INTC_IRQ_UHST	(RT3883_INTC_IRQ_BASE + 18)
 #define RT3883_INTC_IRQ_UDEV	(RT3883_INTC_IRQ_BASE + 19)
 
+#define RT3883_PCI_IRQ_PCI0	(RT3883_PCI_IRQ_BASE + 0)
+#define RT3883_PCI_IRQ_PCI1	(RT3883_PCI_IRQ_BASE + 1)
+#define RT3883_PCI_IRQ_PCIE	(RT3883_PCI_IRQ_BASE + 2)
+
 extern void __iomem *rt3883_sysc_base;
 extern void __iomem *rt3883_memc_base;
 
@@ -130,4 +137,19 @@ static inline u32 rt3883_memc_rr(unsigned reg)
 
 void rt3883_gpio_init(u32 mode);
 
+#define RT3883_PCI_MODE_PCI	0x01
+#define RT3883_PCI_MODE_PCIE	0x02
+#define RT3883_PCI_MODE_BOTH	(RT3883_PCI_MODE_PCI | RT3883_PCI_MODE_PCIE)
+
+struct pci_dev;
+
+#ifdef CONFIG_PCI
+void rt3883_pci_init(unsigned mode);
+void rt3883_pci_set_plat_dev_init(int (*f)(struct pci_dev *));
+#else
+static inline void rt3883_pci_init(unsigned mode) {}
+static inline void rt3883_pci_set_plat_dev_init(int (*f)(struct pci_dev *)) {}
+}
+#endif /* CONFIG_PCI */
+
 #endif /* _RT3883_H_ */
diff --git a/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883_regs.h b/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883_regs.h
index 7149e6fafd..b36cabe858 100644
--- a/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883_regs.h
+++ b/target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt3883_regs.h
@@ -80,6 +80,9 @@
 #define RT3883_SYSC_REG_RSTSTAT		0x38	/* Reset Status*/
 #define RT3883_SYSC_REG_USB_PS		0x5c	/* USB Power saving control */
 #define RT3883_SYSC_REG_GPIO_MODE	0x60	/* GPIO Purpose Select */
+#define RT3883_SYSC_REG_PCIE_CLK_GEN0	0x7c
+#define RT3883_SYSC_REG_PCIE_CLK_GEN1	0x80
+#define RT3883_SYSC_REG_PCIE_CLK_GEN2	0x84
 #define RT3883_SYSC_REG_PMU		0x88
 #define RT3883_SYSC_REG_PMU1		0x8c
 
@@ -96,9 +99,14 @@
 #define RT3883_SYSCFG0_CPUCLK_500	0x3
 
 #define RT3883_SYSCFG1_USB0_HOST_MODE	BIT(10)
+#define RT3883_SYSCFG1_PCIE_RC_MODE	BIT(8)
+#define RT3883_SYSCFG1_PCI_HOST_MODE	BIT(7)
+#define RT3883_SYSCFG1_PCI_66M_MODE	BIT(6)
 #define RT3883_SYSCFG1_GPIO2_AS_WDT_OUT	BIT(2)
 
+#define RT3883_CLKCFG1_PCIE_CLK_EN	BIT(21)
 #define RT3883_CLKCFG1_UPHY1_CLK_EN	BIT(20)
+#define RT3883_CLKCFG1_PCI_CLK_EN	BIT(19)
 #define RT3883_CLKCFG1_UPHY0_CLK_EN	BIT(18)
 
 #define RT3883_GPIO_MODE_I2C		BIT(0)
diff --git a/target/linux/ramips/files/arch/mips/pci/pci-rt3883.c b/target/linux/ramips/files/arch/mips/pci/pci-rt3883.c
new file mode 100644
index 0000000000..8a4c8ce61a
--- /dev/null
+++ b/target/linux/ramips/files/arch/mips/pci/pci-rt3883.c
@@ -0,0 +1,487 @@
+/*
+ *  Ralink RT3883 SoC PCI support
+ *
+ *  Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  Parts of this file are based on Ralink's 2.6.21 BSP
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <asm/mach-ralink/rt3883.h>
+#include <asm/mach-ralink/rt3883_regs.h>
+
+#define RT3883_MEMORY_BASE		0x00000000
+#define RT3883_MEMORY_SIZE		0x02000000
+
+#define RT3883_PCI_MEM_BASE		0x20000000
+#define RT3883_PCI_MEM_SIZE		0x10000000
+#define RT3883_PCI_IO_BASE		0x10160000
+#define RT3883_PCI_IO_SIZE		0x00010000
+
+#define RT3883_PCI_REG_PCICFG_ADDR	0x00
+#define RT3883_PCI_REG_PCIRAW_ADDR	0x04
+#define RT3883_PCI_REG_PCIINT_ADDR	0x08
+#define RT3883_PCI_REG_PCIMSK_ADDR	0x0c
+#define   RT3833_PCI_PCIINT_PCIE	BIT(20)
+#define   RT3833_PCI_PCIINT_PCI1	BIT(19)
+#define   RT3833_PCI_PCIINT_PCI0	BIT(18)
+
+#define RT3883_PCI_REG_CONFIG_ADDR	0x20
+#define RT3883_PCI_REG_CONFIG_DATA	0x24
+#define RT3883_PCI_REG_MEMBASE		0x28
+#define RT3883_PCI_REG_IOBASE		0x2c
+#define RT3883_PCI_REG_ARBCTL		0x80
+
+#define RT3883_PCI_REG_BASE(_x)		(0x1000 + (_x) * 0x1000)
+#define RT3883_PCI_REG_BAR0SETUP_ADDR(_x) (RT3883_PCI_REG_BASE((_x)) + 0x10)
+#define RT3883_PCI_REG_IMBASEBAR0_ADDR(_x) (RT3883_PCI_REG_BASE((_x)) + 0x18)
+#define RT3883_PCI_REG_ID(_x)		(RT3883_PCI_REG_BASE((_x)) + 0x30)
+#define RT3883_PCI_REG_CLASS(_x)	(RT3883_PCI_REG_BASE((_x)) + 0x34)
+#define RT3883_PCI_REG_SUBID(_x)	(RT3883_PCI_REG_BASE((_x)) + 0x38)
+#define RT3883_PCI_REG_STATUS(_x)	(RT3883_PCI_REG_BASE((_x)) + 0x50)
+
+static int (*rt3883_pci_plat_dev_init)(struct pci_dev *dev);
+static void __iomem *rt3883_pci_base;
+static DEFINE_SPINLOCK(rt3883_pci_lock);
+
+static inline u32 rt3883_pci_rr(unsigned reg)
+{
+	return readl(rt3883_pci_base + reg);
+}
+
+static inline void rt3883_pci_wr(u32 val, unsigned reg)
+{
+	writel(val, rt3883_pci_base + reg);
+}
+
+static inline u32 rt3883_pci_get_cfgaddr(unsigned int bus, unsigned int slot,
+					 unsigned int func, unsigned int where)
+{
+	return ((bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) |
+		0x80000000);
+}
+
+static u32 rt3883_pci_read_u32(unsigned bus, unsigned slot,
+			       unsigned func, unsigned reg)
+{
+	unsigned long flags;
+	u32 address;
+	u32 ret;
+
+	address = rt3883_pci_get_cfgaddr(bus, slot, func, reg);
+
+	spin_lock_irqsave(&rt3883_pci_lock, flags);
+	rt3883_pci_wr(address, RT3883_PCI_REG_CONFIG_ADDR);
+	ret = rt3883_pci_rr(RT3883_PCI_REG_CONFIG_DATA);
+	spin_unlock_irqrestore(&rt3883_pci_lock, flags);
+
+	return ret;
+}
+
+static void rt3883_pci_write_u32(unsigned bus, unsigned slot,
+				 unsigned func, unsigned reg, u32 val)
+{
+	unsigned long flags;
+	u32 address;
+
+	address = rt3883_pci_get_cfgaddr(bus, slot, func, reg);
+
+	spin_lock_irqsave(&rt3883_pci_lock, flags);
+	rt3883_pci_wr(address, RT3883_PCI_REG_CONFIG_ADDR);
+	rt3883_pci_wr(val, RT3883_PCI_REG_CONFIG_DATA);
+	spin_unlock_irqrestore(&rt3883_pci_lock, flags);
+}
+
+static void rt3883_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	u32 pending;
+
+	pending = rt3883_pci_rr(RT3883_PCI_REG_PCIINT_ADDR) &
+		  rt3883_pci_rr(RT3883_PCI_REG_PCIMSK_ADDR);
+
+	if (!pending) {
+		spurious_interrupt();
+		return;
+	}
+
+	if (pending & RT3833_PCI_PCIINT_PCI0)
+		generic_handle_irq(RT3883_PCI_IRQ_PCI0);
+
+	if (pending & RT3833_PCI_PCIINT_PCI1)
+		generic_handle_irq(RT3883_PCI_IRQ_PCI1);
+
+	if (pending & RT3833_PCI_PCIINT_PCIE)
+		generic_handle_irq(RT3883_PCI_IRQ_PCIE);
+}
+
+static void rt3883_pci_irq_unmask(struct irq_data *d)
+{
+	int irq = d->irq;
+	u32 mask;
+	u32 t;
+
+	switch (irq) {
+	case RT3883_PCI_IRQ_PCI0:
+		mask = RT3833_PCI_PCIINT_PCI0;
+		break;
+	case RT3883_PCI_IRQ_PCI1:
+		mask = RT3833_PCI_PCIINT_PCI1;
+		break;
+	case RT3883_PCI_IRQ_PCIE:
+		mask = RT3833_PCI_PCIINT_PCIE;
+		break;
+	default:
+		BUG();
+	}
+
+	t = rt3883_pci_rr(RT3883_PCI_REG_PCIMSK_ADDR);
+	rt3883_pci_wr(t | mask, RT3883_PCI_REG_PCIMSK_ADDR);
+	/* flush write */
+	rt3883_pci_rr(RT3883_PCI_REG_PCIMSK_ADDR);
+}
+
+static void rt3883_pci_irq_mask(struct irq_data *d)
+{
+	int irq = d->irq;
+	u32 mask;
+	u32 t;
+
+	switch (irq) {
+	case RT3883_PCI_IRQ_PCI0:
+		mask = RT3833_PCI_PCIINT_PCI0;
+		break;
+	case RT3883_PCI_IRQ_PCI1:
+		mask = RT3833_PCI_PCIINT_PCI1;
+		break;
+	case RT3883_PCI_IRQ_PCIE:
+		mask = RT3833_PCI_PCIINT_PCIE;
+		break;
+	default:
+		BUG();
+	}
+
+	t = rt3883_pci_rr(RT3883_PCI_REG_PCIMSK_ADDR);
+	rt3883_pci_wr(t & ~mask, RT3883_PCI_REG_PCIMSK_ADDR);
+	/* flush write */
+	rt3883_pci_rr(RT3883_PCI_REG_PCIMSK_ADDR);
+}
+
+static struct irq_chip rt3883_pci_irq_chip = {
+	.name		= "RT3883 PCI",
+	.irq_mask	= rt3883_pci_irq_mask,
+	.irq_unmask	= rt3883_pci_irq_unmask,
+	.irq_mask_ack	= rt3883_pci_irq_mask,
+};
+
+static void __init rt3883_pci_irq_init(void)
+{
+	int i;
+
+	/* disable all interrupts */
+	rt3883_pci_wr(0, RT3883_PCI_REG_PCIMSK_ADDR);
+
+	for (i = RT3883_PCI_IRQ_BASE;
+	     i < RT3883_PCI_IRQ_BASE + RT3883_PCI_IRQ_COUNT; i++) {
+		irq_set_chip_and_handler(i, &rt3883_pci_irq_chip,
+					 handle_level_irq);
+	}
+
+	irq_set_chained_handler(RT3883_CPU_IRQ_PCI, rt3883_pci_irq_handler);
+}
+
+static int rt3883_pci_config_read(struct pci_bus *bus, unsigned int devfn,
+				  int where, int size, u32 *val)
+{
+	unsigned long flags;
+	u32 address;
+	u32 data;
+
+	address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn),
+					 PCI_FUNC(devfn), where);
+
+	spin_lock_irqsave(&rt3883_pci_lock, flags);
+	rt3883_pci_wr(address, RT3883_PCI_REG_CONFIG_ADDR);
+	data = rt3883_pci_rr(RT3883_PCI_REG_CONFIG_DATA);
+	spin_unlock_irqrestore(&rt3883_pci_lock, flags);
+
+	switch (size) {
+	case 1:
+		*val = (data >> ((where & 3) << 3)) & 0xff;
+		break;
+	case 2:
+		*val = (data >> ((where & 3) << 3)) & 0xffff;
+		break;
+	case 4:
+		*val = data;
+		break;
+	}
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rt3883_pci_config_write(struct pci_bus *bus, unsigned int devfn,
+				   int where, int size, u32 val)
+{
+	unsigned long flags;
+	u32 address;
+	u32 data;
+
+	address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn),
+					 PCI_FUNC(devfn), where);
+
+	spin_lock_irqsave(&rt3883_pci_lock, flags);
+	rt3883_pci_wr(address, RT3883_PCI_REG_CONFIG_ADDR);
+	data = rt3883_pci_rr(RT3883_PCI_REG_CONFIG_DATA);
+
+	switch (size) {
+	case 1:
+		data = (data & ~(0xff << ((where & 3) << 3))) |
+		       (val << ((where & 3) << 3));
+		break;
+	case 2:
+		data = (data & ~(0xffff << ((where & 3) << 3))) |
+		       (val << ((where & 3) << 3));
+		break;
+	case 4:
+		data = val;
+		break;
+	}
+
+	rt3883_pci_wr(data, RT3883_PCI_REG_CONFIG_DATA);
+	spin_unlock_irqrestore(&rt3883_pci_lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops rt3883_pci_ops = {
+	.read	= rt3883_pci_config_read,
+	.write	= rt3883_pci_config_write,
+};
+
+static struct resource rt3883_pci_mem_resource = {
+	.name	= "PCI MEM space",
+	.start	= RT3883_PCI_MEM_BASE,
+	.end	= RT3883_PCI_MEM_BASE + RT3883_PCI_MEM_SIZE - 1,
+	.flags	= IORESOURCE_MEM,
+};
+
+static struct resource rt3883_pci_io_resource = {
+	.name	= "PCI IO space",
+	.start	= RT3883_PCI_IO_BASE,
+	.end	= RT3883_PCI_IO_BASE + RT3883_PCI_IO_SIZE - 1,
+	.flags	= IORESOURCE_IO,
+};
+
+static struct pci_controller rt3883_pci_controller = {
+	.pci_ops	= &rt3883_pci_ops,
+	.mem_resource	= &rt3883_pci_mem_resource,
+	.io_resource	= &rt3883_pci_io_resource,
+};
+
+static void rt3883_pci_preinit(unsigned mode)
+{
+	u32 syscfg1;
+	u32 rstctrl;
+	u32 clkcfg1;
+
+	if (mode & RT3883_PCI_MODE_PCIE) {
+		u32 val;
+
+		val = rt3883_sysc_rr(RT3883_SYSC_REG_SYSCFG1);
+		val &= ~(0x30);
+		val |= (2 << 4);
+		rt3883_sysc_wr(val, RT3883_SYSC_REG_SYSCFG1);
+
+		val = rt3883_sysc_rr(RT3883_SYSC_REG_PCIE_CLK_GEN0);
+		val &= ~BIT(31);
+		rt3883_sysc_wr(val, RT3883_SYSC_REG_PCIE_CLK_GEN0);
+
+		val = rt3883_sysc_rr(RT3883_SYSC_REG_PCIE_CLK_GEN1);
+		val &= 0x80ffffff;
+		rt3883_sysc_wr(val, RT3883_SYSC_REG_PCIE_CLK_GEN1);
+
+		val = rt3883_sysc_rr(RT3883_SYSC_REG_PCIE_CLK_GEN1);
+		val |= 0xa << 24;
+		rt3883_sysc_wr(val, RT3883_SYSC_REG_PCIE_CLK_GEN1);
+
+		val = rt3883_sysc_rr(RT3883_SYSC_REG_PCIE_CLK_GEN0);
+		val |= BIT(31);
+		rt3883_sysc_wr(val, RT3883_SYSC_REG_PCIE_CLK_GEN0);
+
+		msleep(50);
+	}
+
+	syscfg1 = rt3883_sysc_rr(RT3883_SYSC_REG_SYSCFG1);
+	syscfg1 &= ~(RT3883_SYSCFG1_PCIE_RC_MODE |
+		     RT3883_SYSCFG1_PCI_HOST_MODE);
+
+	rstctrl = rt3883_sysc_rr(RT3883_SYSC_REG_RSTCTRL);
+	rstctrl |= (RT3883_RSTCTRL_PCI | RT3883_RSTCTRL_PCIE);
+
+	clkcfg1 = rt3883_sysc_rr(RT3883_SYSC_REG_CLKCFG1);
+	clkcfg1 &= ~(RT3883_CLKCFG1_PCI_CLK_EN |
+		     RT3883_CLKCFG1_PCIE_CLK_EN);
+
+	if (mode & RT3883_PCI_MODE_PCI) {
+		syscfg1 |= RT3883_SYSCFG1_PCI_HOST_MODE;
+		clkcfg1 |= RT3883_CLKCFG1_PCI_CLK_EN;
+		rstctrl &= ~RT3883_RSTCTRL_PCI;
+	}
+	if (mode & RT3883_PCI_MODE_PCIE) {
+		syscfg1 |= RT3883_SYSCFG1_PCI_HOST_MODE |
+			   RT3883_SYSCFG1_PCIE_RC_MODE;
+		clkcfg1 |= RT3883_CLKCFG1_PCIE_CLK_EN;
+		rstctrl &= ~RT3883_RSTCTRL_PCIE;
+	}
+
+	rt3883_sysc_wr(syscfg1, RT3883_SYSC_REG_SYSCFG1);
+	rt3883_sysc_wr(rstctrl, RT3883_SYSC_REG_RSTCTRL);
+	rt3883_sysc_wr(clkcfg1, RT3883_SYSC_REG_CLKCFG1);
+
+	msleep(500);
+}
+
+static int rt3883_pcie_ready(void)
+{
+	u32 status;
+
+	msleep(500);
+
+	status = rt3883_pci_rr(RT3883_PCI_REG_STATUS(1));
+	if (status & BIT(0))
+		return 0;
+
+	/* TODO: reset PCIe and turn off PCIe clock */
+
+	return -ENODEV;
+}
+
+void __init rt3883_pci_init(unsigned mode)
+{
+	u32 val;
+	int err;
+
+	rt3883_pci_preinit(mode);
+
+	rt3883_pci_base = ioremap(RT3883_PCI_BASE, PAGE_SIZE);
+	if (rt3883_pci_base == NULL) {
+		pr_err("failed to ioremap PCI registers\n");
+		return;
+	}
+
+	rt3883_pci_wr(0, RT3883_PCI_REG_PCICFG_ADDR);
+	if (mode & RT3883_PCI_MODE_PCI)
+		rt3883_pci_wr(BIT(16), RT3883_PCI_REG_PCICFG_ADDR);
+
+	msleep(500);
+
+	if (mode & RT3883_PCI_MODE_PCIE) {
+		err = rt3883_pcie_ready();
+		if (err)
+			return;
+	}
+
+	if (mode & RT3883_PCI_MODE_PCI)
+		rt3883_pci_wr(0x79, RT3883_PCI_REG_ARBCTL);
+
+	rt3883_pci_wr(RT3883_PCI_MEM_BASE, RT3883_PCI_REG_MEMBASE);
+	rt3883_pci_wr(RT3883_PCI_IO_BASE, RT3883_PCI_REG_IOBASE);
+
+	/* PCI */
+	rt3883_pci_wr(0x03ff0000, RT3883_PCI_REG_BAR0SETUP_ADDR(0));
+	rt3883_pci_wr(RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0_ADDR(0));
+	rt3883_pci_wr(0x08021814, RT3883_PCI_REG_ID(0));
+	rt3883_pci_wr(0x00800001, RT3883_PCI_REG_CLASS(0));
+	rt3883_pci_wr(0x28801814, RT3883_PCI_REG_SUBID(0));
+
+	/* PCIe */
+	rt3883_pci_wr(0x01ff0000, RT3883_PCI_REG_BAR0SETUP_ADDR(1));
+	rt3883_pci_wr(RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0_ADDR(1));
+	rt3883_pci_wr(0x08021814, RT3883_PCI_REG_ID(1));
+	rt3883_pci_wr(0x06040001, RT3883_PCI_REG_CLASS(1));
+	rt3883_pci_wr(0x28801814, RT3883_PCI_REG_SUBID(1));
+
+	rt3883_pci_irq_init();
+
+	/* PCIe */
+	val = rt3883_pci_read_u32(0, 0x01, 0, PCI_COMMAND);
+	val |= 0x7;
+	rt3883_pci_write_u32(0, 0x01, 0, PCI_COMMAND, val);
+
+	/* PCI */
+	val = rt3883_pci_read_u32(0, 0x00, 0, PCI_COMMAND);
+	val |= 0x7;
+	rt3883_pci_write_u32(0, 0x00, 0, PCI_COMMAND, val);
+
+	ioport_resource.start = rt3883_pci_io_resource.start;
+	ioport_resource.end = rt3883_pci_io_resource.end;
+
+	register_pci_controller(&rt3883_pci_controller);
+}
+
+int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+	int irq = -1;
+
+	switch (dev->bus->number) {
+	case 0:
+		switch (PCI_SLOT(dev->devfn)) {
+		case 0x00:
+			rt3883_pci_wr(0x03ff0001,
+					     RT3883_PCI_REG_BAR0SETUP_ADDR(0));
+			rt3883_pci_wr(0x03ff0001,
+					     RT3883_PCI_REG_BAR0SETUP_ADDR(1));
+
+			rt3883_pci_write_u32(0, 0x00, 0, PCI_BASE_ADDRESS_0,
+					     RT3883_MEMORY_BASE);
+			rt3883_pci_read_u32(0, 0x00, 0, PCI_BASE_ADDRESS_0);
+
+			irq = RT3883_CPU_IRQ_PCI;
+			break;
+		case 0x01:
+			rt3883_pci_write_u32(0, 0x01, 0, PCI_IO_BASE,
+					     0x00000101);
+			break;
+		case 0x11:
+			irq = RT3883_PCI_IRQ_PCI0;
+			break;
+		case 0x12:
+			irq = RT3883_PCI_IRQ_PCI1;
+			break;
+		}
+		break;
+
+	case 1:
+		irq = RT3883_PCI_IRQ_PCIE;
+		break;
+
+	default:
+		dev_err(&dev->dev, "no IRQ specified\n");
+		return irq;
+	}
+
+	return irq;
+}
+
+void __init rt3883_pci_set_plat_dev_init(int (*f)(struct pci_dev *dev))
+{
+	rt3883_pci_plat_dev_init = f;
+}
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+	if (rt3883_pci_plat_dev_init)
+		return rt3883_pci_plat_dev_init(dev);
+
+	return 0;
+}
diff --git a/target/linux/ramips/files/arch/mips/ralink/Kconfig b/target/linux/ramips/files/arch/mips/ralink/Kconfig
index a7e7f21f97..721fac6959 100644
--- a/target/linux/ramips/files/arch/mips/ralink/Kconfig
+++ b/target/linux/ramips/files/arch/mips/ralink/Kconfig
@@ -69,6 +69,7 @@ config SOC_RT3883
 	select MIPS_MACHINE
 	select USB_ARCH_HAS_OHCI
 	select USB_ARCH_HAS_EHCI
+	select HW_HAS_PCI
 
 config RALINK_DEV_GPIO_BUTTONS
 	def_bool n
diff --git a/target/linux/ramips/patches-2.6.39/106-rt3883-pci-support.patch b/target/linux/ramips/patches-2.6.39/106-rt3883-pci-support.patch
new file mode 100644
index 0000000000..59ed6edca6
--- /dev/null
+++ b/target/linux/ramips/patches-2.6.39/106-rt3883-pci-support.patch
@@ -0,0 +1,10 @@
+--- a/arch/mips/pci/Makefile
++++ b/arch/mips/pci/Makefile
+@@ -56,6 +56,7 @@ obj-$(CONFIG_WR_PPMC)		+= fixup-wrppmc.o
+ obj-$(CONFIG_MIKROTIK_RB532)	+= pci-rc32434.o ops-rc32434.o fixup-rc32434.o
+ obj-$(CONFIG_CPU_CAVIUM_OCTEON)	+= pci-octeon.o pcie-octeon.o
+ obj-$(CONFIG_SOC_RT288X)	+= pci-rt288x.o
++obj-$(CONFIG_SOC_RT3883)	+= pci-rt3883.o
+ 
+ ifdef CONFIG_PCI_MSI
+ obj-$(CONFIG_CPU_CAVIUM_OCTEON)	+= msi-octeon.o
diff --git a/target/linux/ramips/patches-3.2/106-rt3883-pci-support.patch b/target/linux/ramips/patches-3.2/106-rt3883-pci-support.patch
new file mode 100644
index 0000000000..88c94eda47
--- /dev/null
+++ b/target/linux/ramips/patches-3.2/106-rt3883-pci-support.patch
@@ -0,0 +1,10 @@
+--- a/arch/mips/pci/Makefile
++++ b/arch/mips/pci/Makefile
+@@ -20,6 +20,7 @@ obj-$(CONFIG_BCM63XX)		+= pci-bcm63xx.o
+ 					ops-bcm63xx.o
+ obj-$(CONFIG_MIPS_ALCHEMY)	+= pci-alchemy.o
+ obj-$(CONFIG_SOC_RT288X)	+= pci-rt288x.o
++obj-$(CONFIG_SOC_RT3883)	+= pci-rt3883.o
+ 
+ #
+ # These are still pretty much in the old state, watch, go blind.
diff --git a/target/linux/ramips/rt3883/config-2.6.39 b/target/linux/ramips/rt3883/config-2.6.39
index 118305e24a..1993f90d8c 100644
--- a/target/linux/ramips/rt3883/config-2.6.39
+++ b/target/linux/ramips/rt3883/config-2.6.39
@@ -59,6 +59,7 @@ CONFIG_HAVE_IDE=y
 CONFIG_HAVE_IRQ_WORK=y
 CONFIG_HAVE_OPROFILE=y
 CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HW_HAS_PCI=y
 CONFIG_HW_RANDOM=m
 CONFIG_IMAGE_CMDLINE_HACK=y
 CONFIG_INITRAMFS_SOURCE=""
@@ -79,6 +80,9 @@ CONFIG_MTD_PHYSMAP=y
 CONFIG_NEED_DMA_MAP_STATE=y
 CONFIG_NEED_PER_CPU_KM=y
 CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PCI=y
+CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
+CONFIG_PCI_DOMAINS=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PHYLIB=y
 # CONFIG_PREEMPT_RCU is not set
diff --git a/target/linux/ramips/rt3883/config-3.2 b/target/linux/ramips/rt3883/config-3.2
index 568f86a3d0..206162dc87 100644
--- a/target/linux/ramips/rt3883/config-3.2
+++ b/target/linux/ramips/rt3883/config-3.2
@@ -57,6 +57,7 @@ CONFIG_HAVE_IDE=y
 CONFIG_HAVE_IRQ_WORK=y
 CONFIG_HAVE_OPROFILE=y
 CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HW_HAS_PCI=y
 CONFIG_HW_RANDOM=m
 CONFIG_IMAGE_CMDLINE_HACK=y
 CONFIG_INITRAMFS_SOURCE=""
@@ -72,12 +73,16 @@ CONFIG_MIPS_MACHINE=y
 CONFIG_MIPS_MT_DISABLED=y
 CONFIG_MIPS_RALINK=y
 CONFIG_MIPS_RAMIPS_NET=y
+# CONFIG_MLX4_CORE is not set
 # CONFIG_MTD_CFI_INTELEXT is not set
 CONFIG_MTD_CMDLINE_PARTS=y
 CONFIG_MTD_PHYSMAP=y
 CONFIG_NEED_DMA_MAP_STATE=y
 CONFIG_NEED_PER_CPU_KM=y
 CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PCI=y
+CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
+CONFIG_PCI_DOMAINS=y
 CONFIG_PERF_USE_VMALLOC=y
 CONFIG_PHYLIB=y
 # CONFIG_PREEMPT_RCU is not set
@@ -105,7 +110,7 @@ CONFIG_SYS_HAS_EARLY_PRINTK=y
 CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
 CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
 CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
-# CONFIG_USB_ARCH_HAS_XHCI is not set
+CONFIG_USB_ARCH_HAS_XHCI=y
 CONFIG_USB_SUPPORT=y
 CONFIG_WATCHDOG_CORE=y
 CONFIG_XZ_DEC=y
-- 
2.30.2