From 68b24a13e4bdfd573c614df41ceb506403c6a754 Mon Sep 17 00:00:00 2001
From: Tien Hock Loh <thloh@pg-thloh-lv.altera.com>
Date: Tue, 12 Aug 2014 14:58:36 +0800
Subject: [PATCH] pcie patch

---
 .../recipes-core/images/altera-pcie-image.bb       |   14 +
 .../files/0001-Add-PCIe-RP-and-MSI-drivers.patch   | 1172 ++++++++++++++++++++
 .../recipes-kernel/linux/linux-altera-dist.bb      |    3 +-
 meta-altera/recipes-kernel/linux/linux-altera.inc  |    3 +-
 .../recipes-pcie/dmaxfer/dmaxfer-altera_1.0.bb     |   22 +
 meta-altera/recipes-pcie/dmaxfer/files/Makefile    |   37 +
 meta-altera/recipes-pcie/dmaxfer/files/dmaxfer.c   |  460 ++++++++
 meta-altera/recipes-pcie/pcie-ed/files/Makefile    |   13 +
 .../recipes-pcie/pcie-ed/files/altera_epde.c       |  616 ++++++++++
 .../recipes-pcie/pcie-ed/files/altera_epde.h       |   43 +
 .../recipes-pcie/pcie-ed/files/altera_rpde.c       |  545 +++++++++
 .../recipes-pcie/pcie-ed/files/altera_rpde.h       |  129 +++
 .../recipes-pcie/pcie-ed/pcie-ed-altera_1.0.bb     |   17 +
 meta-altera/scripts/altera-setup-builddir          |    2 +-
 14 files changed, 3073 insertions(+), 3 deletions(-)
 create mode 100755 meta-altera/recipes-core/images/altera-pcie-image.bb
 create mode 100644 meta-altera/recipes-kernel/linux/files/0001-Add-PCIe-RP-and-MSI-drivers.patch
 create mode 100644 meta-altera/recipes-pcie/dmaxfer/dmaxfer-altera_1.0.bb
 create mode 100644 meta-altera/recipes-pcie/dmaxfer/files/Makefile
 create mode 100644 meta-altera/recipes-pcie/dmaxfer/files/dmaxfer.c
 create mode 100644 meta-altera/recipes-pcie/pcie-ed/files/Makefile
 create mode 100644 meta-altera/recipes-pcie/pcie-ed/files/altera_epde.c
 create mode 100644 meta-altera/recipes-pcie/pcie-ed/files/altera_epde.h
 create mode 100644 meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.c
 create mode 100644 meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.h
 create mode 100644 meta-altera/recipes-pcie/pcie-ed/pcie-ed-altera_1.0.bb

diff --git a/meta-altera/recipes-core/images/altera-pcie-image.bb b/meta-altera/recipes-core/images/altera-pcie-image.bb
new file mode 100755
index 0000000..ef34200
--- /dev/null
+++ b/meta-altera/recipes-core/images/altera-pcie-image.bb
@@ -0,0 +1,14 @@
+
+DESCRIPTION = "The set of packages for development and testing provided by Altera"
+
+LICENSE = "MIT"
+
+ALTERA_IMAGE_INSTALL ?= "initramfs-altera kernel-modules netbase busybox base-passwd base-files tinylogin sysvinit initscripts e2fsprogs mtd-utils gdb gdbserver bash strace openssh openssl elfutils sysfsutils usbutils dtc gawk ethtool grep lighttpd iputils make pciutils portmap sed setserial wget autoconf diffutils perl minicom i2c-tools iptables oprofile net-tools gator openssh-sftp-server pcie-ed-altera dmaxfer-altera util-linux"
+IMAGE_INSTALL ?= "${ALTERA_IMAGE_INSTALL}"
+
+inherit core-image
+
+# altera-image.inc must be included after inherit core-image to override functionality
+
+include altera-image.inc
+
diff --git a/meta-altera/recipes-kernel/linux/files/0001-Add-PCIe-RP-and-MSI-drivers.patch b/meta-altera/recipes-kernel/linux/files/0001-Add-PCIe-RP-and-MSI-drivers.patch
new file mode 100644
index 0000000..85d5249
--- /dev/null
+++ b/meta-altera/recipes-kernel/linux/files/0001-Add-PCIe-RP-and-MSI-drivers.patch
@@ -0,0 +1,1172 @@
+From d2be826d622f7c03eeec82e5a6f391a6c52b9681 Mon Sep 17 00:00:00 2001
+From: Tien Hock Loh <thloh@pg-thloh-lv.altera.com>
+Date: Tue, 12 Aug 2014 14:57:37 +0800
+Subject: [PATCH] Add PCIe RP and MSI drivers
+
+---
+ .../devicetree/bindings/pci/altera-pcie-msi.txt    |   29 +
+ .../devicetree/bindings/pci/altera-pcie.txt        |   30 +
+ arch/arm/configs/socfpga_defconfig                 |    4 +
+ arch/arm/mach-socfpga/Kconfig                      |    2 +
+ drivers/pci/Kconfig                                |    2 +
+ drivers/pci/Makefile                               |    3 +
+ drivers/pci/host/Kconfig                           |   16 +
+ drivers/pci/host/Makefile                          |    2 +
+ drivers/pci/host/pci-altera-msi.c                  |  340 ++++++++++++
+ drivers/pci/host/pci-altera.c                      |  562 ++++++++++++++++++++
+ drivers/pci/host/pci-altera.h                      |   56 ++
+ drivers/pci/msi.c                                  |    2 +-
+ 12 files changed, 1047 insertions(+), 1 deletions(-)
+ create mode 100644 Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+ create mode 100644 Documentation/devicetree/bindings/pci/altera-pcie.txt
+ create mode 100644 drivers/pci/host/Kconfig
+ create mode 100644 drivers/pci/host/Makefile
+ create mode 100644 drivers/pci/host/pci-altera-msi.c
+ create mode 100644 drivers/pci/host/pci-altera.c
+ create mode 100644 drivers/pci/host/pci-altera.h
+
+diff --git a/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt b/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+new file mode 100644
+index 0000000..b0e6cec
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/altera-pcie-msi.txt
+@@ -0,0 +1,29 @@
++* Altera MSI-to-GIC controller
++
++Required properties:
++- compatible : should contain "altr,msi-1.0"
++- reg: specifies the physical base address of the controller and
++     the length of the memory mapped region.
++- reg-names: Must include the following entries:
++  "csr": CSR registers
++  "vector_slave": vectors region
++-interrupts: specifies the interrupt source of the parent interrupt
++     controller. The format of the interrupt specifier depends on the
++     parent interrupt controller.
++- num-vectors: Number of vectors, range 1 to 32.
++- msi-controller: set to <1>
++
++
++Example
++msi0: msi@0xFF200000 {
++	compatible = "altr,msi-1.0";
++	reg = < 0xFF200000 0x00000010
++		0xFF200010 0x00000080 >;
++	reg-names = "csr", "vector_slave";
++	interrupt-parent = < &hps_0_arm_gic_0 >;
++	interrupts = < 0 42 4 >;
++	msi-controller = < 1 >;
++	num-vectors = < 32 >;
++};
++
++
+diff --git a/Documentation/devicetree/bindings/pci/altera-pcie.txt b/Documentation/devicetree/bindings/pci/altera-pcie.txt
+new file mode 100644
+index 0000000..7c8fb96
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pci/altera-pcie.txt
+@@ -0,0 +1,30 @@
++* Altera PCIe controller
++
++Required properties:
++- compatible : should contain "altr,pcie-root-port-1.0"
++- reg: A list of physical base address and length for TXS and CRA.
++- reg-names: Must include the following entries:
++  "Txs": TXS region
++  "Cra": Cra registers
++-interrupts: specifies the interrupt source of the parent interrupt
++     controller. The format of the interrupt specifier depends on the
++     parent interrupt controller.
++- device_type: must be "pci"
++
++Optional properties:
++- altr,msi = phandle to the MSI component
++
++Example
++	pcie_0: pcie@0x0 {
++		compatible = "altr,pcie-root-port-1.0";
++		reg = < 0xc0000000 0x20000000
++			0xFF240000 0x00004000  >;
++		reg-names = “Txs”, “Cra”;
++		interrupt-parent = < &hps_0_arm_gic_0 >;
++		interrupts = < 0 43 4 >;
++		interrupt-controller;
++		#interrupt-cells = < 1 >;
++		altr,msi = <&msi_0>;
++		device_type = "pci";
++	};
++
+diff --git a/arch/arm/configs/socfpga_defconfig b/arch/arm/configs/socfpga_defconfig
+index dab0039..08e2946 100644
+--- a/arch/arm/configs/socfpga_defconfig
++++ b/arch/arm/configs/socfpga_defconfig
+@@ -160,3 +160,7 @@ CONFIG_FTRACE_MCOUNT_RECORD=y
+ CONFIG_OLD_MCOUNT=y
+ CONFIG_FPGA_BRIDGE=y
+ CONFIG_ALTERA_SOCFPGA_BRIDGE=y
++CONFIG_PCI_MSI=y
++CONFIG_PCI_ALTERA=y
++CONFIG_PCI_ALTERA_MSI=y
++CONFIG_E1000E=y
+diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/Kconfig
+index 6c4d040..0892120 100644
+--- a/arch/arm/mach-socfpga/Kconfig
++++ b/arch/arm/mach-socfpga/Kconfig
+@@ -1,5 +1,6 @@
+ config ARCH_SOCFPGA
+ 	bool "Altera SOCFPGA family" if ARCH_MULTI_V7
++	select ARCH_SUPPORTS_MSI
+ 	select ARCH_WANT_OPTIONAL_GPIOLIB
+ 	select ARM_AMBA
+ 	select ARM_GIC
+@@ -20,6 +21,7 @@ config ARCH_SOCFPGA
+ 	select ARM_ERRATA_754322
+ 	select ARM_ERRATA_764369 if SMP
+ 	select ARM_ERRATA_775420
++	select PCI
+ 	select PL310_ERRATA_588369
+ 	select PL310_ERRATA_727915
+ 	select PL310_ERRATA_753970
+diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
+index 6d51aa6..ac45398 100644
+--- a/drivers/pci/Kconfig
++++ b/drivers/pci/Kconfig
+@@ -119,3 +119,5 @@ config PCI_IOAPIC
+ config PCI_LABEL
+ 	def_bool y if (DMI || ACPI)
+ 	select NLS
++
++source "drivers/pci/host/Kconfig"
+diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
+index 0c3efcf..6ebf5bf 100644
+--- a/drivers/pci/Makefile
++++ b/drivers/pci/Makefile
+@@ -67,3 +67,6 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
+ obj-$(CONFIG_OF) += of.o
+ 
+ ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
++
++# PCI host controller drivers
++obj-y += host/
+diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
+new file mode 100644
+index 0000000..67d1107
+--- /dev/null
++++ b/drivers/pci/host/Kconfig
+@@ -0,0 +1,16 @@
++menu "PCI host controller drivers"
++	depends on PCI
++
++config PCI_ALTERA
++	bool "Altera PCIe controller"
++	depends on ARCH_SOCFPGA
++	help
++	  The Altera PCIe Root Port driver.
++
++config PCI_ALTERA_MSI
++	bool "Altera MSI-to-GIC support"
++	depends on PCI_MSI && PCI_ALTERA
++	help
++	  This selects support for Altera MSI to GIC controller IP. Select
++	  this option if the IP is present on system.
++endmenu
+diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
+new file mode 100644
+index 0000000..0b9c5aa
+--- /dev/null
++++ b/drivers/pci/host/Makefile
+@@ -0,0 +1,2 @@
++obj-$(CONFIG_PCI_ALTERA)	+= pci-altera.o
++obj-$(CONFIG_PCI_ALTERA_MSI)	+= pci-altera-msi.o
+diff --git a/drivers/pci/host/pci-altera-msi.c b/drivers/pci/host/pci-altera-msi.c
+new file mode 100644
+index 0000000..ef7496e
+--- /dev/null
++++ b/drivers/pci/host/pci-altera-msi.c
+@@ -0,0 +1,340 @@
++/*
++ * Copyright Altera Corporation (C) 2013. All rights reserved
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
++ * more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program.  If not, see <http://www.gnu.org/licenses/>.
++ */
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include <linux/of_irq.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/msi.h>
++#include "pci-altera.h"
++
++#define MSI_STATUS		0x0
++#define MSI_ERROR		0x4
++#define MSI_INTMASK		0x8
++
++static DEFINE_SPINLOCK(list_lock);	/* protect msi_list */
++static LIST_HEAD(msi_list);
++
++static inline void msi_writel(struct altera_pci_msi *msi, u32 value, u32 reg)
++{
++	writel(value, msi->csr_base + reg);
++}
++
++static inline u32 msi_readl(struct altera_pci_msi *msi, u32 reg)
++{
++	return readl(msi->csr_base + reg);
++}
++
++static int altera_pci_msi_alloc(struct altera_pci_msi *msi)
++{
++	int bit;
++
++	mutex_lock(&msi->lock);
++
++	bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
++	if (bit < msi->num_of_vectors)
++		set_bit(bit, msi->used);
++	else
++		bit = -ENOSPC;
++
++	mutex_unlock(&msi->lock);
++
++	return bit;
++}
++
++static void altera_pci_msi_irq_free(struct altera_pci_msi *msi,
++	unsigned long irq)
++{
++	struct device *dev = &msi->pdev->dev;
++	u32 mask;
++
++	mutex_lock(&msi->lock);
++
++	if (!test_bit(irq, msi->used))
++		dev_err(dev, "trying to free unused MSI#%lu\n", irq);
++	else {
++		clear_bit(irq, msi->used);
++		mask = msi_readl(msi, MSI_INTMASK);
++		mask &= ~(1 << irq);
++		msi_writel(msi, mask, MSI_INTMASK);
++	}
++
++	mutex_unlock(&msi->lock);
++}
++
++static irqreturn_t altera_pci_msi_isr(int irq, void *data)
++{
++	struct altera_pci_msi *msi = data;
++	unsigned long status;
++	u32 num_of_vectors = msi->num_of_vectors;
++	u32 processed = 0;
++	u32 offset;
++
++	do {
++		status = msi_readl(msi, MSI_STATUS);
++		if (!status)
++			break;
++
++		do {
++			offset = find_first_bit(&status, num_of_vectors);
++			/* Dummy read from vector to clear the interrupt */
++			readl(msi->vector_base + (offset * sizeof(u32)));
++
++			irq = irq_find_mapping(msi->domain, offset);
++			if (irq) {
++				if (test_bit(offset, msi->used))
++					generic_handle_irq(irq);
++			}
++
++			/* Clear the bit from status and repeat without reading
++			 * again status register. */
++			clear_bit(offset, &status);
++			processed++;
++		} while (status);
++	} while (1);
++
++	return processed > 0 ? IRQ_HANDLED : IRQ_NONE;
++}
++
++static struct irq_chip altera_pci_msi_irq_chip = {
++	.name = "altera-pci-msi",
++	.irq_enable = unmask_msi_irq,
++	.irq_disable = mask_msi_irq,
++	.irq_mask = mask_msi_irq,
++	.irq_unmask = unmask_msi_irq,
++};
++
++static int altera_pci_msi_map(struct irq_domain *domain, unsigned int irq,
++			 irq_hw_number_t hwirq)
++{
++	irq_set_chip_and_handler(irq, &altera_pci_msi_irq_chip,
++		handle_simple_irq);
++	irq_set_chip_data(irq, domain->host_data);
++	set_irq_flags(irq, IRQF_VALID);
++
++	return 0;
++}
++
++static const struct irq_domain_ops msi_domain_ops = {
++	.map = altera_pci_msi_map,
++};
++
++int altera_pci_msi_probe(struct platform_device *pdev)
++{
++	struct altera_pci_msi *msi;
++	struct device_node *np = pdev->dev.of_node;
++	struct resource *res;
++	int ret;
++
++	msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_pci_msi),
++		GFP_KERNEL);
++	if (!msi)
++		return -ENOMEM;
++
++	mutex_init(&msi->lock);
++	msi->pdev = pdev;
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
++	msi->csr_base = devm_request_and_ioremap(&pdev->dev, res);
++	if (!msi->csr_base) {
++		dev_err(&pdev->dev, "get csr resource failed\n");
++		return -EADDRNOTAVAIL;
++	}
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++			"vector_slave");
++	msi->vector_base = devm_request_and_ioremap(&pdev->dev, res);
++	if (!msi->vector_base) {
++		dev_err(&pdev->dev, "get vector slave resource failed\n");
++		return -EADDRNOTAVAIL;
++	}
++
++	msi->vector_phy = res->start;
++
++	if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
++		dev_err(&pdev->dev, "failed to parse the number of vectors\n");
++		return -EINVAL;
++	}
++
++	msi->domain = irq_domain_add_linear(np, msi->num_of_vectors,
++					    &msi_domain_ops, msi);
++	if (!msi->domain) {
++		dev_err(&pdev->dev, "failed to create IRQ domain\n");
++		return -ENODEV;
++	}
++
++	msi->irq = platform_get_irq(pdev, 0);
++	if (msi->irq <= 0) {
++		dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq);
++		ret = -ENODEV;
++		goto err;
++	}
++
++	ret = devm_request_irq(&pdev->dev, msi->irq, altera_pci_msi_isr, 0,
++				altera_pci_msi_irq_chip.name, msi);
++	if (ret) {
++		dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret);
++		goto err;
++	}
++
++	spin_lock(&list_lock);
++	list_add_tail(&msi->list, &msi_list);
++	spin_unlock(&list_lock);
++
++	platform_set_drvdata(pdev, msi);
++	return 0;
++
++err:
++	irq_domain_remove(msi->domain);
++	return ret;
++}
++
++static int altera_pci_msi_remove(struct platform_device *pdev)
++{
++	struct altera_pci_msi *msi = platform_get_drvdata(pdev);
++
++	msi_writel(msi, 0, MSI_INTMASK);
++
++	if (msi->domain)
++		irq_domain_remove(msi->domain);
++
++	spin_lock(&list_lock);
++	list_del(&msi->list);
++	spin_unlock(&list_lock);
++
++	platform_set_drvdata(pdev, NULL);
++	return 0;
++}
++
++struct altera_pci_msi *altera_pci_msi_request(struct device_node *msi_np)
++{
++	struct altera_pci_msi *msi;
++
++	spin_lock(&list_lock);
++	list_for_each_entry(msi, &msi_list, list) {
++		if (msi_np == msi->pdev->dev.of_node) {
++			if (!msi->requested) {
++				msi->requested = true;
++				spin_unlock(&list_lock);
++				return msi;
++			} else {
++				pr_info("MSI device is in use.\n");
++				spin_unlock(&list_lock);
++				return NULL;
++			}
++		}
++	}
++	spin_unlock(&list_lock);
++	pr_info("MSI device not found!\n");
++	return NULL;
++}
++
++int altera_pci_msi_free(struct altera_pci_msi *msi)
++{
++	if (!msi || !msi->requested)
++		return -EINVAL;
++
++	spin_lock(&list_lock);
++	msi->requested = false;
++	spin_unlock(&list_lock);
++
++	return 0;
++}
++
++/* Arch hooks */
++int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
++{
++	struct altera_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
++	struct altera_pci_msi *msi = pcie->msi;
++	struct msi_msg msg;
++	u32 irq;
++	u32 mask;
++	int hwirq;
++
++	hwirq = altera_pci_msi_alloc(msi);
++	if (hwirq < 0)
++		return hwirq;
++
++	irq = irq_create_mapping(msi->domain, hwirq);
++	if (!irq)
++		return -EINVAL;
++
++	irq_set_msi_desc(irq, desc);
++
++	msg.address_lo = msi->vector_phy + (hwirq * sizeof(u32));
++	msg.address_hi = 0;		/* 32 bit address only */
++	msg.data = hwirq;
++
++	write_msi_msg(irq, &msg);
++
++	mask = msi_readl(msi, MSI_INTMASK);
++	mask |= 1 << hwirq;
++	msi_writel(msi, mask, MSI_INTMASK);
++	dev_dbg(&pdev->dev, "msi#%d virq %d address_lo 0x%x\n", hwirq, irq,
++		msg.address_lo);
++	return 0;
++}
++
++void arch_teardown_msi_irq(unsigned int irq)
++{
++	struct msi_desc *entry = irq_get_msi_desc(irq);
++	struct pci_dev *pdev = entry->dev;
++	struct altera_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
++	struct altera_pci_msi *msi = pcie->msi;
++	struct irq_data *d = irq_get_irq_data(irq);
++
++	altera_pci_msi_irq_free(msi, d->hwirq);
++	irq_set_msi_desc(entry->irq, NULL);
++	irq_dispose_mapping(entry->irq);
++}
++
++int arch_msi_check_device(struct pci_dev *pdev, int nvec, int type)
++{
++	/* Doesn't support MSIX and multiple MSI */
++	if ((type == PCI_CAP_ID_MSIX) || (type == PCI_CAP_ID_MSI && nvec > 1))
++		return 1;
++	return 0;
++}
++
++
++static const struct of_device_id altera_pci_msi_of_match[] = {
++	{ .compatible = "altr,msi-1.0", NULL },
++	{ },
++};
++MODULE_DEVICE_TABLE(of, altera_pci_msi_of_match);
++
++static struct platform_driver altera_pci_msi_driver = {
++	.driver = {
++		.name = "altera-pci-msi",
++		.owner = THIS_MODULE,
++		.of_match_table = of_match_ptr(altera_pci_msi_of_match),
++	},
++	.probe = altera_pci_msi_probe,
++	.remove = altera_pci_msi_remove,
++};
++
++static int __init altera_pci_msi_init(void)
++{
++	return platform_driver_register(&altera_pci_msi_driver);
++}
++arch_initcall_sync(altera_pci_msi_init);
++
++MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
++MODULE_DESCRIPTION("Altera PCIe MSI support");
++MODULE_LICENSE("GPL v2");
++
+diff --git a/drivers/pci/host/pci-altera.c b/drivers/pci/host/pci-altera.c
+new file mode 100644
+index 0000000..0938d8d
+--- /dev/null
++++ b/drivers/pci/host/pci-altera.c
+@@ -0,0 +1,562 @@
++/*
++ * Copyright Altera Corporation (C) 2013. All rights reserved
++ *
++ * Bits taken from arch/arm/mach-tegra/pcie.c
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
++ * more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program.  If not, see <http://www.gnu.org/licenses/>.
++ */
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include <linux/of_irq.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++
++#include "pci-altera.h"
++
++/* Registers offset */
++#define A2P_ADDR_MAP_LO0  0x1000
++#define A2P_ADDR_MAP_HI0  0x1004
++#define RP_TX_REG0        0x2000
++#define RP_TX_REG1        0x2004
++#define RP_TX_CNTRL       0x2008
++#define RP_TX_EOP         0x2
++#define RP_TX_SOP         0x1
++#define RP_RXCPL_STATUS   0x2010
++#define RP_RXCPL_EOP      0x2
++#define RP_RXCPL_SOP      0x1
++#define RP_RXCPL_REG0     0x2014
++#define RP_RXCPL_REG1     0x2018
++#define P2A_INT_STATUS    0x3060
++#define P2A_INT_STS_ALL   0x0F
++#define RPRX_CPL_REV      0x10
++#define P2A_INT_ENABLE    0x3070
++#define P2A_INT_ENA_ALL   0x0F
++
++/* TLP CFG RD WR */
++#define TLP_FMTTYPE_CFGRD0 0x04  /* Configuration Read  Type 0 */
++#define TLP_FMTTYPE_CFGWR0 0x44  /* Configuration Write Type 0 */
++#define TLP_FMTTYPE_CFGRD1 0x05  /* Configuration Read  Type 1 */
++#define TLP_FMTTYPE_CFGWR1 0x45  /* Configuration Write Type 1 */
++#define TLP_PAYLOAD_SIZE   0x01  /* TLP Data Payload Size      */
++#define TLP_CFG_DW0(fmttype)            ((fmttype << 24) | TLP_PAYLOAD_SIZE)
++#define TLP_CFG_DW1(reqid, tag)         ((reqid << 16) | (tag << 8) | 0xF)
++#define TLP_READ_TAG       0x1D  /* statically define */
++#define TLP_WRITE_TAG      0x10  /* statically define */
++#define TLP_CFG_DW2(bus, devfn, offset) ((bus << 24) | (devfn << 16) | offset)
++
++/* Address translation table entry size */
++#define ATT_ENTRY_SIZE		8
++
++#define SOP_LOOP	1
++#define HEADER_LOOP	2
++#define DETECTION_LOOP	3
++
++#define DWORD_MASK	3
++
++struct tlp_rp_regpair_t {
++	u32 rp_ctrl;
++	u32 rp_reg0;
++	u32 rp_reg1;
++};
++
++/*
++ * The Root Port is boot/initialized with bus 0 device 0
++ * pribus use TYPE0 CFG, secbus use TYPE1 CFG, subbus use TYPE1 CFG
++ */
++#define ALTRPCIERP_DEVFN 0
++static u8 rp_pribus;
++static u8 rp_secbus;
++
++static void altera_pcie_retrain(struct pci_dev *dev)
++{
++	u16 linkcap, linkstat;
++
++	/*
++	 * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
++	 * current speed is 2.5 GB/s.
++	 */
++	pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap);
++
++	if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
++		return;
++
++	pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
++	if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB)
++		pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
++					PCI_EXP_LNKCTL_RL);
++
++	return;
++}
++DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, altera_pcie_retrain);
++
++static inline void cra_writel(struct altera_pcie *pcie, u32 value, u32 reg)
++{
++	writel(value, pcie->cra_base + reg);
++}
++
++static inline u32 cra_readl(struct altera_pcie *pcie, u32 reg)
++{
++	return readl(pcie->cra_base + reg);
++}
++
++/* TLP packet RX pair */
++static void tlp_read_rp_rx(struct altera_pcie *pcie,
++			   struct tlp_rp_regpair_t *tlp_rp_regdata)
++{
++	tlp_rp_regdata->rp_ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
++	tlp_rp_regdata->rp_reg0 = cra_readl(pcie, RP_RXCPL_REG0);
++	tlp_rp_regdata->rp_reg1 = cra_readl(pcie, RP_RXCPL_REG1);
++}
++
++/* TLP packet TX pair */
++static void tlp_write_rp_tx(struct altera_pcie *pcie,
++			    struct tlp_rp_regpair_t *tlp_rp_regdata)
++{
++	cra_writel(pcie, tlp_rp_regdata->rp_reg0, RP_TX_REG0);
++	cra_writel(pcie, tlp_rp_regdata->rp_reg1, RP_TX_REG1);
++	cra_writel(pcie, tlp_rp_regdata->rp_ctrl, RP_TX_CNTRL);
++}
++
++/* TLP packet detect SOP and EOP */
++static int tlp_read_packet(struct altera_pcie *pcie, u32 *value)
++{
++	int err = PCIBIOS_SUCCESSFUL;
++	u8 loop;
++	struct tlp_rp_regpair_t *tlp_rp_regdata;
++
++	/* allocate tlp_rp_regdata */
++	tlp_rp_regdata = kzalloc(sizeof(struct tlp_rp_regpair_t), GFP_KERNEL);
++	if (!tlp_rp_regdata) {
++		dev_err(pcie->dev, "out of memory !!!\n");
++		err = -ENOMEM;
++		goto ret_error;
++	}
++
++	/*
++	 * Detect RP_RXCPL_SOP is not require,
++	 * read till EOP to avoid completion packet misalignment.
++	 * Detect RP_RXCPL_EOP with loop of
++	 * TLP_PAYLOAD_SIZE + 1(SOP) + 2(header) + 3(detection loop)
++	 */
++	for (loop = (TLP_PAYLOAD_SIZE + SOP_LOOP + HEADER_LOOP +
++		DETECTION_LOOP); loop > 0; loop--) {
++		tlp_read_rp_rx(pcie, tlp_rp_regdata);
++		/* detect EOP */
++		if (tlp_rp_regdata->rp_ctrl & RP_RXCPL_EOP)
++			break;
++	}
++	/* if no EOP detected */
++	if (loop == 0) {
++		err = -ENOENT;
++		goto ret_freemem;
++	}
++
++	/* read data */
++	if (value)
++		*value = tlp_rp_regdata->rp_reg0;
++
++ret_freemem:
++	kfree(tlp_rp_regdata);
++ret_error:
++	return err;
++}
++
++/* pci_ops read using TLP packet receive */
++static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
++			      int where, u32 *value)
++{
++	struct tlp_rp_regpair_t *tlp_rp_regdata;
++	int ret;
++
++	/* if not DWORD align */
++	if (!IS_ALIGNED(where, sizeof(u32))) {
++		dev_err(pcie->dev, "data not DWORD align\n");
++		return PCIBIOS_BAD_REGISTER_NUMBER;
++	}
++
++	/*
++	 * Implement the device number filtering at the lowest level layer.
++	 * Per PCIe spec section 7.3.1 - Configuration Requests
++	 * targeting the Bus Number associated with a Link specifying
++	 * Device Number 0 are delivered to the device attached to the Link;
++	 * Configuration Requests specifying all other Device Numbers (1-31)
++	 * must be terminated by the Switch Downstream Port or the Root Port
++	 * with an Unsupported Request Completion Status.
++	 */
++	if (((bus == rp_pribus) || (bus == rp_secbus)) &&
++		(PCI_SLOT(devfn) > 0)) {
++		*value = ~0UL;
++		return PCIBIOS_SUCCESSFUL;
++	}
++
++	/* allocate tlp_rp_regdata */
++	tlp_rp_regdata = kzalloc(sizeof(struct tlp_rp_regpair_t), GFP_KERNEL);
++	if (!tlp_rp_regdata) {
++		dev_err(pcie->dev, "out of memory !!!\n");
++		return -ENOMEM;
++	}
++
++	/* sending TLP packet */
++	if (bus == rp_pribus)
++		tlp_rp_regdata->rp_reg0 = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD0);
++	else
++		tlp_rp_regdata->rp_reg0 = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
++	tlp_rp_regdata->rp_reg1 = TLP_CFG_DW1(
++				  ((rp_pribus << 8) | ALTRPCIERP_DEVFN),
++				  TLP_READ_TAG);
++	tlp_rp_regdata->rp_ctrl = RP_TX_SOP;
++	tlp_write_rp_tx(pcie, tlp_rp_regdata);
++	tlp_rp_regdata->rp_reg0 = TLP_CFG_DW2(bus, devfn, where);
++	tlp_rp_regdata->rp_reg1 = 0x0;
++	tlp_rp_regdata->rp_ctrl = RP_TX_EOP;
++	tlp_write_rp_tx(pcie, tlp_rp_regdata);
++
++	/* read data from completion package */
++	ret = tlp_read_packet(pcie, value);
++	if (ret)
++		*value = ~0UL;	/* return 0xFFFFFFFF if error */
++
++	kfree(tlp_rp_regdata);
++	return ret;
++}
++
++/* pci_ops write using TLP packet send */
++static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
++			       int where, u32 value)
++{
++	struct tlp_rp_regpair_t *tlp_rp_regdata;
++
++	/* if not DWORD align */
++	if (!IS_ALIGNED(where, sizeof(u32))) {
++		dev_err(pcie->dev, "data not DWORD align\n");
++		return PCIBIOS_BAD_REGISTER_NUMBER;
++	}
++
++	/*
++	 * Implement the device number filtering at the lowest level layer.
++	 * Per PCIe spec section 7.3.1 - Configuration Requests
++	 * targeting the Bus Number associated with a Link specifying
++	 * Device Number 0 are delivered to the device attached to the Link;
++	 * Configuration Requests specifying all other Device Numbers (1-31)
++	 * must be terminated by the Switch Downstream Port or the Root Port
++	 * with an Unsupported Request Completion Status.
++	 */
++	if (((bus == rp_pribus) || (bus == rp_secbus)) &&
++		(PCI_SLOT(devfn) > 0))
++		return PCIBIOS_SET_FAILED;
++
++	/* allocate tlp_rp_regdata */
++	tlp_rp_regdata = kzalloc(sizeof(struct tlp_rp_regpair_t), GFP_KERNEL);
++	if (!tlp_rp_regdata) {
++		dev_err(pcie->dev, "out of memory !!!\n");
++		return -ENOMEM;
++	}
++
++	/* sending TLP packet */
++	if (bus == rp_pribus)
++		tlp_rp_regdata->rp_reg0 = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR0);
++	else
++		tlp_rp_regdata->rp_reg0 = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
++	tlp_rp_regdata->rp_reg1 = TLP_CFG_DW1(
++				  ((rp_pribus << 8) | ALTRPCIERP_DEVFN),
++				  TLP_WRITE_TAG);
++	tlp_rp_regdata->rp_ctrl = RP_TX_SOP;
++	tlp_write_rp_tx(pcie, tlp_rp_regdata);
++	tlp_rp_regdata->rp_reg0 = TLP_CFG_DW2(bus, devfn, where);
++	tlp_rp_regdata->rp_reg1 = value;
++	tlp_rp_regdata->rp_ctrl = RP_TX_EOP;
++	tlp_write_rp_tx(pcie, tlp_rp_regdata);
++
++	/* TLP read response */
++	tlp_read_packet(pcie, NULL);
++
++	/* if change altrpcierp bus number, update variable... */
++	if ((bus == rp_pribus) && (devfn == ALTRPCIERP_DEVFN) &&
++	    (where == PCI_PRIMARY_BUS)) {
++		rp_pribus = (u8)(value);
++		rp_secbus = (u8)(value >> 8);
++	}
++
++	kfree(tlp_rp_regdata);
++	return PCIBIOS_SUCCESSFUL;
++}
++
++/* pci_ops read */
++static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
++			       int where, int size, u32 *value)
++{
++	struct altera_pcie *pcie = sys_to_pcie(bus->sysdata);
++	int ret;
++
++	/* if cross DWORD boundary */
++	if (((where & DWORD_MASK) + size) > 4) {
++		dev_err(pcie->dev, "Data cross DWORD boundary\n");
++		return PCIBIOS_BAD_REGISTER_NUMBER;
++	}
++
++	/* TLP read */
++	ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
++		(where & ~DWORD_MASK), value);
++	if (ret != PCIBIOS_SUCCESSFUL)
++		return ret;
++
++	*value = *value >> ((where & DWORD_MASK) << 3);  /* position */
++	*value = *value & (~0UL >> ((4 - size) << 3));  /* mask */
++	return PCIBIOS_SUCCESSFUL;
++}
++
++/* pci_ops write */
++static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
++				int where, int size, u32 value)
++{
++	struct altera_pcie *pcie = sys_to_pcie(bus->sysdata);
++	u32 data32 = value;
++	int ret;
++
++	/* if cross DWORD boundary */
++	if (((where & DWORD_MASK) + size) > 4) {
++		dev_err(pcie->dev, "Data cross DWORD boundary\n");
++		return PCIBIOS_BAD_REGISTER_NUMBER;
++	}
++
++	/* TLP read and merge DWORD */
++	if (IS_ALIGNED(where, sizeof(u32)) || (size != sizeof(u32))) {
++		ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
++			where & ~DWORD_MASK, &data32);
++		if (ret)
++			return ret;
++		data32 = data32 & ~((~0UL >> ((4 - size) << 3))
++				    << ((where & DWORD_MASK) << 3));
++		data32 = data32 | (value << ((where & DWORD_MASK) << 3));
++	}
++
++	/* TLP write */
++	return tlp_cfg_dword_write(pcie, bus->number, devfn,
++		(where & ~DWORD_MASK), data32);
++}
++
++/* pci_ops */
++static struct pci_ops altera_pcie_ops = {
++	.read = altera_pcie_cfg_read,
++	.write = altera_pcie_cfg_write,
++};
++
++/* hw_pci setup */
++static int altera_pcie_setup(int nr, struct pci_sys_data *sys)
++{
++	struct altera_pcie *pcie = sys_to_pcie(sys);
++	int err;
++	u32  pages, i;
++	u32 translation_size;
++	u32 txs_size;
++	struct resource *res;
++
++	/* get translation table size */
++	cra_writel(pcie, ~0UL, A2P_ADDR_MAP_LO0);
++	translation_size = (~(cra_readl(pcie, A2P_ADDR_MAP_LO0)
++		& 0xFFFFFFFC)) + 1;
++
++	txs_size = resource_size(pcie->txs);
++	/* get number of translation table pages */
++	pages = txs_size / translation_size;
++
++	/* allocate resource memory */
++	res = kzalloc(sizeof(struct resource) * pages, GFP_KERNEL);
++	if (!res) {
++		dev_err(pcie->dev, "out of memory !!!\n");
++		err = -ENOMEM;
++		goto ret_error;
++	}
++	pcie->table_res = res;
++	/* IORESOURCE_MEM use A2P_ADDR_MAP_LO0 */
++	if (pages >= 1) {
++		res[0].name  = "ALTERA PCIE RP MEM";
++		res[0].start = pcie->txs->start;
++		res[0].end   = res[0].start + translation_size - 1;
++		res[0].flags = IORESOURCE_MEM;
++		if (request_resource(&iomem_resource, &res[0])) {
++			dev_err(pcie->dev, "out of memory !!!\n");
++			err = -ENOMEM;
++			goto ret_freeres;
++		}
++		pci_add_resource_offset(&sys->resources, &res[0],
++					sys->mem_offset);
++	}
++
++	/* IORESOURCE_MEM | IORESOURCE_PREFETCH */
++	if (pages >= 2) {
++		res[1].name  = "ALTERA PCIE RP PREF MEM";
++		res[1].start = res[0].end + 1;
++		res[1].end   = res[1].start + translation_size - 1;
++		res[1].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
++		if (request_resource(&iomem_resource, &res[1])) {
++			dev_err(pcie->dev, "out of memory !!!\n");
++			err = -ENOMEM;
++			goto ret_relres;
++		}
++		pci_add_resource_offset(&sys->resources, &res[1],
++					sys->mem_offset);
++	}
++
++	/* Configure Avalon-MM-to-PCI Express Address Translation Table */
++	for (i = 0; i < pages; i++) {
++		cra_writel(pcie, res[i].start,
++			A2P_ADDR_MAP_LO0 + (ATT_ENTRY_SIZE * i));
++		cra_writel(pcie, 0, A2P_ADDR_MAP_HI0 + (ATT_ENTRY_SIZE * i));
++	}
++
++	return 1;
++
++ret_relres:
++	release_resource(res);
++ret_freeres:
++	kfree(res);
++ret_error:
++	return err;
++}
++
++/* hw_pci map_irq */
++static int altera_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
++{
++	struct altera_pcie *pcie = sys_to_pcie(pdev->bus->sysdata);
++	return pcie->hwirq;
++}
++
++/* hw_pci */
++static struct hw_pci altera_pcie_hw __initdata = {
++#ifdef CONFIG_PCI_DOMAINS
++	.domain			= 0,
++#endif
++	.nr_controllers		= 1,
++	.ops			= &altera_pcie_ops,
++	.setup			= altera_pcie_setup,
++	.map_irq		= altera_pcie_map_irq,
++};
++
++/* RP ISR - clear status */
++static irqreturn_t altera_pcie_isr(int irq, void *arg)
++{
++	struct altera_pcie *pcie = arg;
++	cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
++	return IRQ_HANDLED;
++}
++
++/* RP device init and resources, based on Device Tree */
++static int altera_pcie_probe(struct platform_device *pdev)
++{
++	struct altera_pcie *pcie;
++	struct resource *cra;
++	int ret;
++
++	pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
++	if (!pcie) {
++		dev_err(&pdev->dev, "no memory for altera pcie\n");
++		return -ENOMEM;
++	}
++	pcie->dev = &pdev->dev;
++
++	cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
++	pcie->cra_base = devm_request_and_ioremap(&pdev->dev, cra);
++	if (!pcie->cra_base) {
++		dev_err(&pdev->dev, "get Cra resource failed\n");
++		return -EADDRNOTAVAIL;
++	}
++
++	pcie->txs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Txs");
++	if (!pcie->txs) {
++		dev_err(&pdev->dev, "get Txs resource failed\n");
++		return PTR_ERR(pcie->txs);
++	}
++
++	/* setup IRQ */
++	pcie->hwirq = platform_get_irq(pdev, 0);
++	if (pcie->hwirq <= 0) {
++		dev_err(&pdev->dev, "failed to get IRQ: %d\n", pcie->hwirq);
++		return -EINVAL;
++	}
++	ret = devm_request_irq(&pdev->dev, pcie->hwirq, altera_pcie_isr,
++			IRQF_SHARED, pdev->name, pcie);
++
++	if (ret) {
++		dev_err(&pdev->dev, "failed to register legacy IRQ\n");
++		return ret;
++	}
++
++#ifdef CONFIG_PCI_ALTERA_MSI
++	{
++		struct device_node *np_msi;
++		np_msi = of_parse_phandle(pdev->dev.of_node, "altr,msi", 0);
++		if (!np_msi)
++			return -EINVAL;
++
++		pcie->msi = altera_pci_msi_request(np_msi);
++		if (!pcie->msi) {
++			dev_err(&pdev->dev, "failed to request MSI support\n");
++			return -EINVAL;
++		}
++	}
++#endif
++	/* clear PCIe to AvMM IRQ status */
++	cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
++	/* enable PCIe to AvMM IRQ */
++	cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
++
++	altera_pcie_hw.private_data = (void **)&pcie;
++	pci_common_init(&altera_pcie_hw);
++
++	platform_set_drvdata(pdev, pcie);
++	return 0;
++}
++
++static int __exit altera_pcie_remove(struct platform_device *pdev)
++{
++	struct altera_pcie *pcie = platform_get_drvdata(pdev);
++#ifdef CONFIG_PCI_MSI
++	altera_pci_msi_free(pcie->msi);
++#endif
++	free_irq(pcie->hwirq, pcie);
++	release_resource(pcie->table_res);
++	kfree(pcie->table_res);
++	platform_set_drvdata(pdev, NULL);
++	return 0;
++}
++
++static const struct of_device_id altera_pcie_of_match[] = {
++	{ .compatible = "ALTR,root-port-13.0", },
++	{ .compatible = "ALTR,pcie-root-port-13.0", },
++	{ .compatible = "altr,pcie-root-port-1.0", },
++	{},
++};
++MODULE_DEVICE_TABLE(of, altera_pcie_of_match);
++
++static struct platform_driver altera_pcie_driver = {
++	.probe		= altera_pcie_probe,
++	.remove		= altera_pcie_remove,
++	.driver = {
++		.name	= "altera-pcie",
++		.owner	= THIS_MODULE,
++		.of_match_table = of_match_ptr(altera_pcie_of_match),
++	},
++};
++
++static int __init altera_pcie_init(void)
++{
++	return platform_driver_register(&altera_pcie_driver);
++}
++subsys_initcall(altera_pcie_init);
++
++MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
++MODULE_DESCRIPTION("Altera PCIe host controller driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/pci/host/pci-altera.h b/drivers/pci/host/pci-altera.h
+new file mode 100644
+index 0000000..03afd36
+--- /dev/null
++++ b/drivers/pci/host/pci-altera.h
+@@ -0,0 +1,56 @@
++/*
++ * Copyright Altera Corporation (C) 2013. All rights reserved
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ *
++ * This program is distributed in the hope it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
++ * more details.
++ *
++ * You should have received a copy of the GNU General Public License along with
++ * this program.  If not, see <http://www.gnu.org/licenses/>.
++ */
++#ifndef PCI_ALTERA_H
++#define PCI_ALTERA_H
++
++#ifdef CONFIG_PCI_MSI
++
++#define MAX_MSI_VECTORS		32
++struct altera_pci_msi {
++	DECLARE_BITMAP(used, MAX_MSI_VECTORS);
++	struct mutex		lock;	/* proctect used variable */
++	struct list_head	list;
++	struct platform_device	*pdev;
++	struct irq_domain	*domain;
++	void __iomem		*csr_base;
++	void __iomem		*vector_base;
++	u32			vector_phy;
++	u32			num_of_vectors;
++	int			irq;
++	bool			requested;
++};
++
++struct altera_pci_msi *altera_pci_msi_request(struct device_node *msi_np);
++int altera_pci_msi_free(struct altera_pci_msi *msi);
++
++#endif /* CONFIG_PCI_MSI */
++
++struct altera_pcie {
++	struct device		*dev;
++	struct resource		*txs;
++	struct resource		*table_res;
++	struct altera_pci_msi	*msi;
++	void __iomem		*cra_base;
++	int			hwirq;
++};
++
++static inline struct altera_pcie *sys_to_pcie(struct pci_sys_data *sys)
++{
++	return sys->private_data;
++}
++
++#endif /* PCI_ALTERA_H */
++
+diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
+index 00cc78c..97ad4d1 100644
+--- a/drivers/pci/msi.c
++++ b/drivers/pci/msi.c
+@@ -29,7 +29,7 @@ static int pci_msi_enable = 1;
+ /* Arch hooks */
+ 
+ #ifndef arch_msi_check_device
+-int arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
++int __weak arch_msi_check_device(struct pci_dev *dev, int nvec, int type)
+ {
+ 	return 0;
+ }
+-- 
+1.7.1
+
diff --git a/meta-altera/recipes-kernel/linux/linux-altera-dist.bb b/meta-altera/recipes-kernel/linux/linux-altera-dist.bb
index 1afbd40..05cf593 100755
--- a/meta-altera/recipes-kernel/linux/linux-altera-dist.bb
+++ b/meta-altera/recipes-kernel/linux/linux-altera-dist.bb
@@ -1,6 +1,7 @@
 require recipes-kernel/linux/linux-altera.inc
 
-SRC_URI = "http://www.rocketboards.org/linux-socfpga.tgz;md5sum=${ALTERA_LINUX_MD5SUM};sha256sum=${ALTERA_LINUX_SHA256SUM}"
+SRC_URI = "http://www.rocketboards.org/linux-socfpga.tgz;md5sum=${ALTERA_LINUX_MD5SUM};sha256sum=${ALTERA_LINUX_SHA256SUM} \
+		file://0001-Add-PCIe-RP-and-MSI-drivers.patch"
 
 SRCREV_pn-${PN} = "${AUTOREV}"
 
diff --git a/meta-altera/recipes-kernel/linux/linux-altera.inc b/meta-altera/recipes-kernel/linux/linux-altera.inc
index 55c888d..7cc9ac1 100755
--- a/meta-altera/recipes-kernel/linux/linux-altera.inc
+++ b/meta-altera/recipes-kernel/linux/linux-altera.inc
@@ -1,5 +1,6 @@
 PR = "r1"
-SRC_URI = "git://git.rocketboards.org/linux-socfpga.git;protocol=http;branch=socfpga-${PV}-rel"
+SRC_URI = "git://git.rocketboards.org/linux-socfpga.git;protocol=http;branch=socfpga-${PV}-rel \
+		file://0001-Add-PCIe-RP-and-MSI-drivers.patch"
 
 SRCREV_pn-${PN} = "${AUTOREV}"
 
diff --git a/meta-altera/recipes-pcie/dmaxfer/dmaxfer-altera_1.0.bb b/meta-altera/recipes-pcie/dmaxfer/dmaxfer-altera_1.0.bb
new file mode 100644
index 0000000..976fd44
--- /dev/null
+++ b/meta-altera/recipes-pcie/dmaxfer/dmaxfer-altera_1.0.bb
@@ -0,0 +1,22 @@
+DESCRIPTION = "Altera PCIe DMA transfer benchmarking application"
+LICENSE = "GPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
+
+PR = "r0"
+FILES_${PN} = "/home/root/altera/*"
+FILES_${PN}-dbg = "/usr /home/root/altera/.debug"
+
+SRC_URI = "file://Makefile \
+           file://dmaxfer.c \
+          "
+
+S = "${WORKDIR}"
+
+do_compile() {
+	oe_runmake all
+}
+
+do_install() {
+	install -d ${D}/home/root/altera
+	install -m 0755 dmaxfer ${D}/home/root/altera/dmaxfer
+}
diff --git a/meta-altera/recipes-pcie/dmaxfer/files/Makefile b/meta-altera/recipes-pcie/dmaxfer/files/Makefile
new file mode 100644
index 0000000..a155ee1
--- /dev/null
+++ b/meta-altera/recipes-pcie/dmaxfer/files/Makefile
@@ -0,0 +1,37 @@
+#
+# Copyright Altera 2013
+# All Rights Reserved.
+#
+
+C_SRC := dmaxfer.c
+CFLAGS := -g -O0 -Werror -Wall -lpthread
+
+CROSS_COMPILE ?= arm-poky-linux-gnueabi-
+CC ?= $(CROSS_COMPILE)gcc
+NM ?= $(CROSS_COMPILE)nm
+OD ?= $(CROSS_COMPILE)objdump
+
+ifeq ($(or $(COMSPEC),$(ComSpec)),)
+RM := rm -rf
+else
+RM := cs-rm -rf
+endif
+
+ELF ?= $(basename $(firstword $(C_SRC)))
+OBJ := $(patsubst %.c,%.o,$(C_SRC))
+
+.PHONY: all
+all: $(ELF)
+
+.PHONY:
+clean:
+	$(RM) $(ELF) $(OBJ) *.objdump *.map
+
+$(OBJ): %.o: %.c
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(ELF): $(OBJ)
+	$(CC) $(CFLAGS) $(OBJ) -o $@ $(LDFLAGS)
+#	$(OD) -d $@ > $@.objdump
+	$(NM) $@ > $@.map
+
diff --git a/meta-altera/recipes-pcie/dmaxfer/files/dmaxfer.c b/meta-altera/recipes-pcie/dmaxfer/files/dmaxfer.c
new file mode 100644
index 0000000..8b02e0c
--- /dev/null
+++ b/meta-altera/recipes-pcie/dmaxfer/files/dmaxfer.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright Altera Corporation (C) 2013. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+struct ioctl_t {
+	unsigned long  datlen;
+	unsigned long  timediff;
+};
+
+#define IOCTL_TYPE 'Z'
+#define GET_IOCTL _IOR(IOCTL_TYPE, 1, long)
+#define SET_IOCTL _IOW(IOCTL_TYPE, 2, long)
+#define PCI_RX_IOCTL _IOR(IOCTL_TYPE, 3, struct ioctl_t)
+#define PCI_TX_IOCTL _IOR(IOCTL_TYPE, 4, struct ioctl_t)
+#define SYS_RX_IOCTL _IOR(IOCTL_TYPE, 5, struct ioctl_t)
+#define SYS_TX_IOCTL _IOR(IOCTL_TYPE, 6, struct ioctl_t)
+
+#define AVERAGE_LOOP      3
+#define PATTERN_MASK_A    0x50
+#define PATTERN_MASK_B    0xA0
+
+/* #define PCIERP_DEBUG */
+#define PRINT_BYTES   0x100
+#define PCIERP_DEBUG
+#ifdef PCIERP_DEBUG
+void dump_mismatch(char *datasd, char *datarv, unsigned long datlen)
+{
+	int loop;
+	unsigned long mismatch = 0;
+	unsigned long firstmismatch;
+
+	/* find mismatch */
+	for (loop = 0; loop < datlen; loop++) {
+		if ((char)(*(datasd + loop)) != (char)(*(datarv + loop))) {
+			if (mismatch == 0) {
+				firstmismatch = loop;
+			}
+			mismatch++;
+		}
+	}
+
+	/* total mismatch */
+	printf("Total size 0x%lX and mismatch 0x%lX", datlen, mismatch);
+
+	/* print send data */
+	printf("\nFirst 0x%X mismatch sent data", PRINT_BYTES);
+	for (loop = firstmismatch; loop < firstmismatch + PRINT_BYTES; loop++) {
+		if ((loop & 0xF) == 0) {
+			printf("\nOffset %08X  ", loop);
+		}
+		printf("%02X ", (char)(*(datasd+loop)));
+	}
+
+	/* print receive data */
+	printf("\nFirst 0x%X mismatch received data", PRINT_BYTES);
+	for (loop = firstmismatch; loop < firstmismatch + PRINT_BYTES; loop++) {
+		if ((loop & 0xF) == 0) {
+			printf("\nOffset %08X  ", loop);
+		}
+		printf("%02X ", (char)(*(datarv+loop)));
+	}
+
+	printf("\n\n");
+}
+#endif
+
+/* get transfer size */
+int get_xfer_size(unsigned long *rpdatlen, unsigned long *epdatlen) {
+	int err = 0;
+	int fdrp = -1;
+	int fdep = -1;
+
+	/* read EP DMA transfer size */
+	if ((fdep = open("/dev/blkepde0", O_RDWR | O_SYNC)) < 0) {
+		printf("EP block fail to open\n");
+		return -1;
+	}
+	if (ioctl(fdep, GET_IOCTL, epdatlen) < 0) {
+		printf("fdep ioctl fail to read size\n");
+		err = -1;
+		goto err_close_fdep;
+	}
+
+	/* read RP DMA transfer size */
+	/* no error return, continue for Simplified and Small design */
+	/* Simplified and Small design will not load blkrpde0 */
+	if ((fdrp = open("/dev/blkrpde0", O_RDWR | O_SYNC)) >= 0) {
+		if (ioctl(fdrp, GET_IOCTL, rpdatlen) < 0) {
+			printf("fdrp ioctl fail to read size\n");
+			/* Simplified and Small design are same as EP */
+			*rpdatlen = *epdatlen;
+		}
+		close(fdrp);
+	} else {
+		printf("RP block fail to open\n");
+		/* Simplified and Small design are same as EP */
+		*rpdatlen = *epdatlen;
+	}
+
+err_close_fdep:
+	close(fdep);
+	return err;
+}
+
+/* RP DMA */
+unsigned long rpdma_xfer(unsigned int ioctlno, int loop, char ptn,
+	                 unsigned long datlen)
+{
+	int fdrp = -1;
+	char *datasd;
+	char *datarv;
+	char *xferdir;
+	struct ioctl_t elapse_time;
+
+	if (ioctlno == PCI_TX_IOCTL || ioctlno == SYS_TX_IOCTL)
+		xferdir = "RP-DMA write to EP ";
+	else
+		xferdir = "RP-DMA read from EP";
+
+	/* open and set command */
+	if ((fdrp = open("/dev/blkrpde0", O_RDWR | O_SYNC)) < 0) {
+		printf("RP block fail to open\n");
+		return 0;
+	}
+	if (ioctl(fdrp, SET_IOCTL, &ioctlno) < 0) {
+		printf("fdrp ioctl fail to set command\n");
+		elapse_time.timediff = 0;
+		goto err_close_fdrp;
+	}
+
+	datasd = malloc(datlen);
+	if (NULL == datasd) {
+		printf("Error allocate memory...\n");
+		elapse_time.timediff = 0;
+		goto err_close_fdrp;
+	}
+	datarv = malloc(datlen);
+	if (NULL == datarv) {
+		printf("Error allocate memory...\n");
+		elapse_time.timediff = 0;
+		goto err_close_datasd;
+	}
+
+	/* send buffer to RP on-chip RAM */
+	memset(datasd, ptn, datlen);
+	if (lseek(fdrp, 0, SEEK_SET) < 0)
+		/* no error return, continue throughput reading */
+		printf("fdrp fail to seek head\n");
+	if (write(fdrp, datasd, datlen) < 0)
+		/* no error return, continue throughput reading */
+		printf("fdrp fail to write\n");
+
+	/* initiate write and get the time elapse */
+	if (ioctl(fdrp, ioctlno, &elapse_time) < 0) {
+		printf("fdrp ioctl fail to TX\n");
+		elapse_time.timediff = 0;
+		goto err_close_datarv;
+	}
+
+	/* successfull DMA transfer */
+	if (elapse_time.timediff) {
+		/* read buffer from EP on-chip RAM */
+		if (lseek(fdrp, 0, SEEK_SET) < 0)
+			/* no error return, continue throughput reading */
+			printf("fdep fail to seek head\n");
+		if (read(fdrp, datarv, datlen) < 0)
+			/* no error return, continue throughput reading */
+			printf("fdep fail to read\n");
+
+		/* compare data */
+		if (memcmp(datasd, datarv, datlen)) {
+			printf("Loop #%02d: %s Data mismatch !!!\n",
+				loop, xferdir);
+			#ifdef PCIERP_DEBUG
+			dump_mismatch(datasd, datarv, datlen);
+			#endif
+		}
+
+		/* print result each loop (byte/usec = MB/s) */
+		printf("Loop #%02d: %s %ld Byte %ld MB/s\n",
+			loop, xferdir, datlen, datlen/elapse_time.timediff);
+	} else {
+		printf("Loop #%02d: %s transfer invalid\n", loop, xferdir);
+	}
+
+err_close_datarv:
+	free(datarv);
+err_close_datasd:
+	free(datasd);
+
+err_close_fdrp:
+	close(fdrp);
+
+	return elapse_time.timediff;
+}
+
+/* EP DMA */
+unsigned long epdma_xfer(unsigned int ioctlno, int loop, char ptn,
+	                 unsigned long datlen)
+{
+	int fdep = -1;
+	char *datasd;
+	char *datarv;
+	char *xferdir;
+	struct ioctl_t elapse_time;
+
+	if (ioctlno == PCI_TX_IOCTL || ioctlno == SYS_TX_IOCTL)
+		xferdir = "EP-DMA write to RP ";
+	else
+		xferdir = "EP-DMA read from RP";
+
+	/* open and set command */
+	if ((fdep = open("/dev/blkepde0", O_RDWR | O_SYNC)) < 0) {
+		printf("EP block fail to open\n");
+		return 0;
+	}
+	if (ioctl(fdep, SET_IOCTL, &ioctlno) < 0) {
+		printf("fdep ioctl fail to set command\n");
+		elapse_time.timediff = 0;
+		goto err_close_fdep;
+	}
+
+	datasd = malloc(datlen);
+	if (NULL == datasd) {
+		printf("Error allocate memory...\n");
+		elapse_time.timediff = 0;
+		goto err_close_fdep;
+	}
+	datarv = malloc(datlen);
+	if (NULL == datarv) {
+		printf("Error allocate memory...\n");
+		elapse_time.timediff = 0;
+		goto err_close_datasd;
+	}
+
+	/* send buffer to EP on-chip RAM */
+	memset(datasd, ptn, datlen);
+	if (lseek(fdep, 0, SEEK_SET) < 0)
+		/* no error return, continue throughput reading */
+		printf("fdep fail to seek head\n");
+	if (write(fdep, datasd, datlen) < 0)
+		/* no error return, continue throughput reading */
+		printf("fdep fail to write\n");
+
+	/* initiate write and get the time elapse */
+	if (ioctl(fdep, ioctlno, &elapse_time) < 0) {
+		printf("fdep ioctl fail to TX\n");
+		elapse_time.timediff = 0;
+		goto err_close_datarv;
+	}
+
+	/* successfull DMA transfer */
+	if (elapse_time.timediff) {
+		/* read buffer from RP on-chip RAM */
+		if (lseek(fdep, 0, SEEK_SET) < 0)
+			/* no error return, continue throughput reading */
+			printf("fdrp fail to seek head\n");
+		if (read(fdep, datarv, datlen) < 0)
+			/* no error return, continue throughput reading */
+			printf("fdrp fail to read\n");
+
+		/* compare data */
+		if (memcmp(datasd, datarv, datlen)) {
+			printf("Loop #%02d: %s Data mismatch !!!\n",
+				loop, xferdir);
+			#ifdef PCIERP_DEBUG
+			dump_mismatch(datasd, datarv, datlen);
+			#endif
+		}
+
+		/* print result each loop (byte/usec = MB/s) */
+		printf("Loop #%02d: %s %ld Byte %ld MB/s\n",
+			loop, xferdir, datlen, datlen/elapse_time.timediff);
+	} else {
+		printf("Loop #%02d: %s transfer invalid\n", loop, xferdir);
+	}
+
+err_close_datarv:
+	free(datarv);
+err_close_datasd:
+	free(datasd);
+
+err_close_fdep:
+	close(fdep);
+
+	return elapse_time.timediff;
+}
+
+int main(void)
+{
+	int loop;
+	unsigned long timediff;
+	unsigned long totalsd, totalsdtime;
+	unsigned long totalrv, totalrvtime;
+	unsigned long datlen, rpdatlen, epdatlen;
+
+	if (get_xfer_size(&rpdatlen, &epdatlen)) {
+		printf("Fail to get data transfer size\n");
+		return -1;
+	}
+	datlen = rpdatlen < epdatlen ? rpdatlen : epdatlen;
+
+	printf("\n");
+	printf("==================================================\n");
+	printf("PCIe throughput test between RP(OCM) <-> EP(OCM)\n");
+	printf("  RP(OCM) = Root Port On-Chip RAM\n");
+	printf("  EP(OCM) = End Point On-Chip RAM\n");
+	printf("==================================================\n");
+
+	totalsd = 0;
+	totalsdtime = 0;
+	totalrv = 0;
+	totalrvtime = 0;
+
+	for (loop = 1 ; loop <= AVERAGE_LOOP ; loop++) {
+		printf("\n");
+
+		timediff = rpdma_xfer(PCI_TX_IOCTL, loop,
+					  PATTERN_MASK_A + loop, datlen);
+		if (timediff) {
+			totalsd++;
+			totalsdtime += timediff;
+		}
+		timediff = epdma_xfer(PCI_TX_IOCTL, loop,
+					  PATTERN_MASK_B + loop, datlen);
+		if (timediff) {
+			totalrv++;
+			totalrvtime += timediff;
+		}
+	}
+	
+	printf("\n");
+	if (totalsdtime)
+		printf("Average RP(OCM) -> EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalsd) / totalsdtime);
+	if (totalrvtime)
+		printf("Average RP(OCM) <- EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalrv) / totalrvtime);
+	
+	totalsd = 0;
+	totalsdtime = 0;
+	totalrv = 0;
+	totalrvtime = 0;
+
+	for (loop = 1 ; loop <= AVERAGE_LOOP ; loop++) {
+		printf("\n");
+
+		timediff = rpdma_xfer(PCI_RX_IOCTL, loop,
+					  PATTERN_MASK_A+loop, datlen);
+		if (timediff) {
+			totalsd++;
+			totalsdtime += timediff;
+		}
+		timediff = epdma_xfer(PCI_RX_IOCTL, loop,
+					  PATTERN_MASK_B+loop, datlen);
+		if (timediff) {
+			totalrv++;
+			totalrvtime += timediff;
+		}
+	}
+
+	/* calculate throughput (byte/usec = MB/s) */
+	printf("\n");
+	if (totalsdtime)
+		printf("Average RP(OCM) <- EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalsd) / totalsdtime);
+	if (totalrvtime)
+		printf("Average RP(OCM) -> EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalrv) / totalrvtime);
+
+	printf("\n\n");
+	printf("==================================================\n");
+	printf("PCIe throughput test between RP(SYS) <-> EP(OCM)\n");
+	printf("  RP(SYS) = Root Port System Memory\n");
+	printf("  EP(OCM) = End Point On-Chip RAM\n");
+	printf("==================================================\n");
+	totalsd = 0;
+	totalsdtime = 0;
+	totalrv = 0;
+	totalrvtime = 0;
+
+	for (loop = 1 ; loop <= AVERAGE_LOOP ; loop++) {
+		printf("\n");
+		
+		timediff = rpdma_xfer(SYS_TX_IOCTL, loop,
+					  PATTERN_MASK_A+loop, datlen);
+		if (timediff) {
+			totalsd++;
+			totalsdtime += timediff;
+		}
+		timediff = epdma_xfer(SYS_TX_IOCTL, loop,
+					  PATTERN_MASK_B+loop, datlen);
+		if (timediff) {
+			totalrv++;
+			totalrvtime += timediff;
+		}
+	}
+
+	printf("\n");
+	if (totalsdtime)
+		printf("Average RP(SYS) -> EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalsd) / totalsdtime);
+	if (totalrvtime)
+		printf("Average RP(SYS) <- EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalrv) / totalrvtime);
+
+	totalsd = 0;
+	totalsdtime = 0;
+	totalrv = 0;
+	totalrvtime = 0;
+
+	for (loop = 1 ; loop <= AVERAGE_LOOP ; loop++) {
+		printf("\n");
+		
+		timediff = rpdma_xfer(SYS_RX_IOCTL, loop,
+					  PATTERN_MASK_A+loop, datlen);
+		if (timediff) {
+			totalsd++;
+			totalsdtime += timediff;
+		}
+		timediff = epdma_xfer(SYS_RX_IOCTL, loop,
+					  PATTERN_MASK_B+loop, datlen);
+		if (timediff) {
+			totalrv++;
+			totalrvtime += timediff;
+		}
+	}
+
+	/* calculate throughput (byte/usec = MB/s) */
+	printf("\n");
+	if (totalsdtime)
+		printf("Average RP(SYS) <- EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalsd) / totalsdtime);
+	if (totalrvtime)
+		printf("Average RP(SYS) -> EP(OCM) throughput %ld MB/s\n",
+			(datlen * totalrv) / totalrvtime);
+	printf("\n");
+
+	return 0;
+}
+
diff --git a/meta-altera/recipes-pcie/pcie-ed/files/Makefile b/meta-altera/recipes-pcie/pcie-ed/files/Makefile
new file mode 100644
index 0000000..c3c8a6a
--- /dev/null
+++ b/meta-altera/recipes-pcie/pcie-ed/files/Makefile
@@ -0,0 +1,13 @@
+obj-m += altera_rpde.o
+obj-m += altera_epde.o
+
+SRC := $(shell pwd)
+
+all:
+	$(MAKE) -C $(KERNEL_SRC) M=$(SRC)
+
+modules_install:
+	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
+
+clean:
+	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) clean
diff --git a/meta-altera/recipes-pcie/pcie-ed/files/altera_epde.c b/meta-altera/recipes-pcie/pcie-ed/files/altera_epde.c
new file mode 100644
index 0000000..52eb1c2
--- /dev/null
+++ b/meta-altera/recipes-pcie/pcie-ed/files/altera_epde.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/module.h>
+#include <linux/ide.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+
+#include "altera_rpde.h"
+#include "altera_epde.h"
+
+#define DRIVER_NAME             "altera-epde"
+#define MAJOR_NR                241
+#define DEVICE_NAME             "blkepde"
+
+struct block_device_t {
+	spinlock_t     lock;
+	void __iomem   *rpdata;
+	unsigned long  rpdata_avoff;
+	unsigned long  datlen;
+	void __iomem   *epdata;
+	void __iomem   *csrbar_base;
+	void __iomem   *ephipcra;
+	void __iomem   *dmacsr;
+	void __iomem   *dmades;
+	void __iomem   *perfcounter;
+	unsigned long  epdata_avoff;
+	char           *sys_buf;
+	unsigned long  sys_buf_phys;
+	unsigned long  translation_mask;
+	unsigned long  timediff;
+	unsigned long  perfcountertick;
+	unsigned int   dmaxfer_cmd;
+	struct completion dma_done;
+	struct gendisk *gd;
+	bool           is_msi;
+};
+static struct block_device_t block_device;
+
+/* Support only one DES */
+static void init_msgdma_xfer(void __iomem *dmades, unsigned long frphy,
+			     unsigned long tophy, u32 size32)
+{
+	writel(frphy, dmades + MSGDMA_READ_ADDR);
+	writel(tophy, dmades + MSGDMA_WRITE_ADDR);
+	writel(size32, dmades + MSGDMA_DES_LEN);
+
+	/* record start time */
+	/* reset */
+	writel(PERFCTR_GBL_CLK_CTR_LO_RST,
+		block_device.perfcounter + PERFCTR_GBL_CLK_CTR_LO);
+	/* start */
+	writel(PERFCTR_GBL_CLK_CTR_HI_START,
+		block_device.perfcounter + PERFCTR_GBL_CLK_CTR_HI);
+
+	/* start transfer */
+	writel(MSGDMA_DES_CTL_TX_CH   |
+	       MSGDMA_DES_CTL_TC_IRQ  |
+	       MSGDMA_DES_CTL_GO,
+	      dmades + MSGDMA_DES_CTL);
+}
+
+/* Simple SGDMA csr with interrupt */
+static void init_msgdma_csr(void __iomem *dmacsr)
+{
+	/* Stop and clear CSR */
+	MSGDMA_STOP_CTL(dmacsr);
+
+	/* Clear any SDGMA STS */
+	MSGDMA_CLR_STS(dmacsr);
+
+	/* Enable CSR */
+	MSGDMA_ENA_CTL(dmacsr);
+}
+
+/* MSGDMA transfer function
+ *   return timediff from performance counter
+ */
+static unsigned long msgdma_transfer(unsigned long frphy, unsigned long tophy,
+				     u32 size32)
+{
+	unsigned long rt;
+
+	/* Setup performance counter and barrier */
+	block_device.perfcountertick = 0;
+	init_completion(&block_device.dma_done);
+
+	/* Setting up SGDMA CSR */
+	init_msgdma_csr(block_device.dmacsr);
+
+	/* Init SGDMA transfer */
+	init_msgdma_xfer(block_device.dmades,
+			 frphy, tophy, size32);
+
+	/* wait DMA transfer complete */
+	wait_for_completion(&block_device.dma_done);
+
+	rt = (block_device.perfcountertick / EP_PERFCOUNTER_MFREQ);  /* usec */
+
+	return rt;
+}
+
+static int block_ioctl(struct block_device *bdev, fmode_t mode,
+		       unsigned int cmd, unsigned long arg)
+{
+	int rt;
+	struct ioctl_t ioctl_data;
+	unsigned long sys_buf_avoff;
+
+	block_device.rpdata_avoff = RP_DMA2OCR_OFF &
+					~block_device.translation_mask;
+	sys_buf_avoff = (block_device.sys_buf_phys + RP_DMA2SDRAM_OFF) &
+			~block_device.translation_mask;
+	/* PCIE TXS range is small, use Address Translation Table to rebase */
+	switch (cmd) {
+	case PCI_TX_IOCTL:
+	case PCI_RX_IOCTL:
+		writel(RP_DMA2OCR_OFF & block_device.translation_mask,
+			block_device.ephipcra + A2P_ADDR_MAP_LO0);
+		break;
+	case SYS_TX_IOCTL:
+	case SYS_RX_IOCTL:
+		writel(block_device.sys_buf_phys &
+			block_device.translation_mask,
+			block_device.ephipcra + A2P_ADDR_MAP_LO0);
+		break;
+	}
+	writel(0x0, block_device.ephipcra + A2P_ADDR_MAP_HI0);
+
+	switch (cmd) {
+	case GET_IOCTL:
+		return copy_to_user((void *)arg,
+				    (void *)&block_device.datlen,
+				    sizeof(long));
+	case SET_IOCTL:
+		return copy_from_user((void *)&block_device.dmaxfer_cmd,
+					  (void *)arg,
+				      sizeof(int));
+	case PCI_TX_IOCTL:
+		/* From EP On-Chip RAM to RP On-Chip RAM using SGDMA */
+		memset(block_device.rpdata, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(block_device.epdata_avoff,
+						      block_device.rpdata_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	case PCI_RX_IOCTL:
+		/* From RP On-Chip RAM to EP On-Chip RAM using SGDMA */
+		memset(block_device.epdata, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(block_device.rpdata_avoff,
+						      block_device.epdata_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	case SYS_TX_IOCTL:
+		/* From EP On-Chip RAM to RP System Memory using SGDMA */
+		memset(block_device.sys_buf, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(block_device.epdata_avoff,
+						      sys_buf_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	case SYS_RX_IOCTL:
+		/* From RP System Memory to EP On-Chip RAM using SGDMA */
+		memset(block_device.epdata, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(sys_buf_avoff,
+						      block_device.epdata_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	}
+
+	return -ENOTTY;
+}
+
+static struct request_queue *block_queue;
+static const struct block_device_operations block_fops = {
+	.owner = THIS_MODULE,
+	.ioctl = block_ioctl,
+};
+
+static void blk_request(struct request_queue *rq)
+{
+	int err;
+	struct request *req;
+	unsigned long offset, nbytes;
+
+	req = blk_fetch_request(rq);
+	while (req) {
+		if (req->cmd_type != REQ_TYPE_FS)
+			continue;
+
+		offset = blk_rq_pos(req) * SECTOR_SIZE;
+		nbytes = blk_rq_cur_sectors(req) * SECTOR_SIZE;
+
+		err = -EIO;
+		if ((offset + nbytes) <= block_device.datlen) {
+			err = 0;
+			switch (rq_data_dir(req)) {
+			case READ:
+				switch (block_device.dmaxfer_cmd) {
+				case PCI_TX_IOCTL:
+					/* Read from RP On-Chip RAM to Buffer */
+					memcpy(req->buffer,
+						block_device.rpdata + offset,
+						nbytes);
+					break;
+				case SYS_TX_IOCTL:
+					/* Read from System Buffer to Buffer */
+					memcpy(req->buffer,
+						block_device.sys_buf + offset,
+						nbytes);
+					break;
+				case PCI_RX_IOCTL:
+				case SYS_RX_IOCTL:
+					/* Read from EP On-Chip RAM to Buffer */
+					memcpy(req->buffer,
+						block_device.epdata + offset,
+						nbytes);
+					break;
+				}
+				break;
+			case WRITE:
+				switch (block_device.dmaxfer_cmd) {
+				case PCI_TX_IOCTL:
+				case SYS_TX_IOCTL:
+					/* Write from Buffer to EP OCM */
+					memcpy(block_device.epdata + offset,
+						req->buffer, nbytes);
+					break;
+				case PCI_RX_IOCTL:
+					/* Write from Buffer to RP OCM */
+					memcpy(block_device.rpdata + offset,
+						req->buffer, nbytes);
+					break;
+				case SYS_RX_IOCTL:
+					/* Write from Buffer to System Buffer */
+					memcpy(block_device.sys_buf + offset,
+						req->buffer, nbytes);
+					break;
+				}
+				break;
+			default:
+				err = -EIO;
+				pr_notice("Unknown request %u\n",
+				       rq_data_dir(req));
+				break;
+			}
+		}
+
+		if (__blk_end_request_cur(req, err) == 0)
+			req = blk_fetch_request(rq);
+	}
+}
+
+static int block_device_init(struct pci_dev *pdev)
+{
+	int err;
+
+	/* register as block device */
+	err = register_blkdev(MAJOR_NR, DEVICE_NAME);
+	if (err) {
+		dev_err(&pdev->dev, "block device fail to register\n");
+		goto err_ret;
+	}
+
+	spin_lock_init(&block_device.lock);
+
+	block_queue = blk_init_queue(blk_request, &block_device.lock);
+	if (!block_queue) {
+		dev_err(&pdev->dev, "block_device: error in blk_init_queue\n");
+		goto err_unregdev;
+	}
+
+	/* whole disk, no partition */
+	block_device.gd = alloc_disk(1);
+	if (!block_device.gd) {
+		dev_err(&pdev->dev, "block_device: error in alloc_disk\n");
+		goto err_deinit_queue;
+	}
+
+	block_device.gd->major = MAJOR_NR;
+	block_device.gd->first_minor = 0;
+	block_device.gd->fops = &block_fops;
+	block_device.gd->private_data = &block_device;
+	sprintf(block_device.gd->disk_name, "%s%d", DEVICE_NAME, 0);
+	set_capacity(block_device.gd,
+		    (block_device.datlen/SECTOR_SIZE));
+
+	block_device.gd->queue = block_queue;
+
+	add_disk(block_device.gd);
+
+	return 0;
+err_deinit_queue:
+	blk_cleanup_queue(block_queue);
+err_unregdev:
+	unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+err_ret:
+	return err;
+}
+
+static irqreturn_t epde_isr(int irq, void *arg)
+{
+	const u32 *ptr1, *ptr2;
+
+	switch (block_device.dmaxfer_cmd) {
+	case PCI_RX_IOCTL:
+	case PCI_TX_IOCTL:
+		ptr1 = (u32 *)block_device.rpdata;
+		ptr2 = (u32 *)block_device.epdata;
+		break;
+	case SYS_RX_IOCTL:
+	case SYS_TX_IOCTL:
+		ptr1 = (u32 *)block_device.sys_buf;
+		ptr2 = (u32 *)block_device.epdata;
+		break;
+	default:
+		/* Unknow command */
+		return IRQ_NONE;
+	}
+	
+	/* last 4 bytes location */
+	ptr1 += ((block_device.datlen / 4) - 1);
+	ptr2 += ((block_device.datlen / 4) - 1);
+
+	/* wait until last 4 bytes posted write complete */
+	while (*ptr1 != *ptr2);
+
+	/* Stop counter */
+	__raw_writel(PERFCTR_GBL_CLK_CTR_LO_STOP,
+		block_device.perfcounter + PERFCTR_GBL_CLK_CTR_LO);
+
+	/* Clear Interrupt and RUN bits */
+	__raw_writel((__raw_readl(block_device.dmacsr + MSGDMA_CTL)
+				  | MSGDMA_CTL_DIS_BITS)
+				  & ~MSGDMA_CTL_IE_GLOBAL,
+		     block_device.dmacsr + MSGDMA_CTL);
+
+	if (__raw_readl(block_device.dmacsr + MSGDMA_STS) & MSGDMA_STS_IRQ)
+		/* read tick */
+		block_device.perfcountertick = readl(block_device.perfcounter +
+						     PERFCTR_GBL_CLK_CTR_LO);
+
+	/* Clear any SDGMA STS */
+	__raw_writel(MSGDMA_STS_CLR_BITS, block_device.dmacsr + MSGDMA_STS);
+
+	/* Clear HIP INT status */
+	__raw_writel(EP_DMA_IRQ_A2P, block_device.ephipcra + A2P_INT_STS_REG);
+
+	/* completed */
+	complete(&block_device.dma_done);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PCI_MSI
+static int setup_msi(struct pci_dev *pdev)
+{
+	int ret;
+	ret = pci_enable_msi(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize MSI interrupt.\n");
+		return ret;
+	}
+
+	ret = request_irq(pdev->irq, epde_isr, 0, "epde-msi", &block_device);
+	if (ret) {
+		pci_disable_msi(pdev);
+		dev_err(&pdev->dev, "failed to allocate MSI interrupt\n");
+		return ret;
+	}
+
+	pci_intx(pdev, 0);
+	block_device.is_msi = true;
+	dev_info(&pdev->dev, "using MSI irq #%d\n", pdev->irq);
+	return ret;
+}
+#endif
+
+static int setup_intx(struct pci_dev *pdev)
+{
+	int ret;
+
+	ret = request_irq(pdev->irq, epde_isr, IRQF_SHARED, "epde-intx",
+		&block_device);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to allocate legacy interrupt\n");
+		return ret;
+	}
+
+	pci_msi_off(pdev);
+	pci_intx(pdev, 1);
+	block_device.is_msi = false;
+	dev_info(&pdev->dev, "using legacy irq #%d\n", pdev->irq);
+	return ret;
+}
+
+static int setup_interrupt(struct pci_dev *pdev)
+{
+#ifdef CONFIG_PCI_MSI
+	int ret;
+	ret = setup_msi(pdev);
+	if (!ret)
+		return ret;
+#endif
+	return setup_intx(pdev);
+}
+
+static int altr_epde_pci_probe(struct pci_dev *pdev,
+				    const struct pci_device_id *pci_id)
+{
+	int err = -EIO;
+	struct pci_dev *rpdev = NULL;
+
+	/* Max block length */
+	block_device.datlen = min3((unsigned long)RP_OCRAM_SIZE,
+				   (unsigned long)EP_OCRAM_SIZE,
+				   (unsigned long)SGDMA_MAX_SIZE);
+
+	/*********** Is the right RP available? ***********/
+	rpdev = pci_get_device(PCI_VENDOR_ID_RPDE, PCI_DEVICE_ID_RPDE, NULL);
+	if (!rpdev) {
+		dev_err(&pdev->dev, "has no require RP detected\n");
+		err = -ENODEV;
+		return err;
+	}
+	if ((rpdev->subsystem_vendor != PCI_SUBVEN_ID_RPDE) ||
+	    (rpdev->subsystem_device != PCI_SUBDEV_ID_RPDE)) {
+		dev_err(&pdev->dev, "has no require RP detected\n");
+		err = -ENODEV;
+		goto err_nodev;
+	}
+	/* IO Map RP On-Chip RAM region*/
+	block_device.rpdata = ioremap_nocache(RP_OCRAM_SBASE,
+					      block_device.datlen);
+	if (!block_device.rpdata) {
+		dev_err(&pdev->dev, "fail to ioremap rpdata\n");
+		goto err_nodev;
+	}
+
+	/*********** EP Setup ***********/
+	/* Enable Device */
+	err = pci_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "fail to enable pcie device\n");
+		goto err_unmaprpdata;
+	}
+	pci_set_master(pdev);
+
+	/* Request EP data resources */
+	err = pci_request_region(pdev, EP_DIR_BAR_NR, DRIVER_NAME);
+	if (err) {
+		dev_err(&pdev->dev, "fail to request DIR BAR region\n");
+		goto err_disdev;
+	}
+
+	/* Map EP On-Chip RAM */
+	block_device.epdata_avoff = EP_DMA2OCRAM_ADDR;
+	block_device.epdata = pci_iomap(pdev, EP_DIR_BAR_NR, 0);
+	if (!block_device.epdata) {
+		dev_err(&pdev->dev, "fail to mapping epdata region\n");
+		goto err_freedir;
+	}
+
+	/* Request EP CSR resources */
+	err = pci_request_region(pdev, EP_CSR_BAR_NR, DRIVER_NAME);
+	if (err) {
+		dev_err(&pdev->dev, "fail to request CSR BAR region\n");
+		goto err_unmapocr;
+	}
+
+	block_device.csrbar_base = pci_iomap(pdev, EP_CSR_BAR_NR, 0);
+	if (!block_device.csrbar_base) {
+		dev_err(&pdev->dev, "fail to mapping EP CSR region\n");
+		goto err_relcsr;
+	}
+
+	block_device.ephipcra = block_device.csrbar_base + EP_HIP_CRA_AVOFF;
+	block_device.dmacsr = block_device.csrbar_base + EP_DMA_CSR_AVOFF;
+	block_device.dmades = block_device.csrbar_base + EP_DMA_DES_AVOFF;
+	block_device.perfcounter =
+		block_device.csrbar_base + EP_PERFCOUNTER_AVOFF;
+
+	/*********** SGDMA IRQ Setup ***********/
+	/* verify if SGDMA status ok */
+	writel(~0UL, block_device.dmacsr + MSGDMA_STS);
+	if ((readl(block_device.dmacsr + MSGDMA_STS) & MSGDMA_STS_MASK) !=
+	    MSGDMA_STS_AT_RESET) {
+		dev_err(&pdev->dev, "MSGDMA fail status test\n");
+		goto err_unmapcsrbar;
+	}
+
+	err = setup_interrupt(pdev);
+	if (err)
+		goto err_unmapcsrbar;
+
+	/*********** Block Setup ***********/
+	/* Init block device API */
+	err = block_device_init(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "fail to init block device\n");
+		goto err_freeirq;
+	}
+
+	/* Allocate system memory for DMA trasfer */
+	block_device.sys_buf = pci_alloc_consistent(pdev,
+		block_device.datlen, (dma_addr_t *)&block_device.sys_buf_phys);
+	if (!block_device.sys_buf) {
+		dev_err(&pdev->dev, "Fail to allocate system memory for DMA\n");
+		goto err_unregblk;
+	}
+
+	/* get address translation table mask */
+	writel(~0UL, block_device.ephipcra + A2P_ADDR_MAP_LO0);
+	block_device.translation_mask = readl(
+		block_device.ephipcra + A2P_ADDR_MAP_LO0) & A2P_ADDR_MAP_MASK;
+
+	block_device.timediff = 0;
+	/* enable IRQ */
+	writel(EP_DMA_IRQ_A2P, block_device.ephipcra + A2P_INT_ENA_REG);
+
+	pci_dev_put(rpdev);
+	return 0;
+err_unregblk:
+	unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+err_freeirq:
+	if (block_device.is_msi)
+		pci_disable_msi(pdev);
+err_unmapcsrbar:
+	pci_iounmap(pdev, block_device.csrbar_base);
+err_relcsr:
+	pci_release_region(pdev, EP_CSR_BAR_NR);
+err_unmapocr:
+	pci_iounmap(pdev, block_device.epdata);
+err_freedir:
+	pci_release_region(pdev, EP_DIR_BAR_NR);
+err_disdev:
+	pci_disable_device(pdev);
+err_unmaprpdata:
+	iounmap(block_device.rpdata);
+err_nodev:
+	pci_dev_put(rpdev);
+	return err;
+}
+
+static void altr_epde_pci_remove(struct pci_dev *pdev)
+{
+	pci_free_consistent(pdev, block_device.datlen,
+		block_device.sys_buf, block_device.sys_buf_phys);
+	pci_iounmap(pdev, block_device.epdata);
+	pci_iounmap(pdev, block_device.csrbar_base);
+	blk_cleanup_queue(block_queue);
+	unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+	del_gendisk(block_device.gd);
+	put_disk(block_device.gd);
+	pci_release_region(pdev, EP_CSR_BAR_NR);
+	pci_release_region(pdev, EP_DIR_BAR_NR);
+	pci_disable_device(pdev);
+	iounmap(block_device.rpdata);
+	free_irq(pdev->irq, &block_device);
+	if (block_device.is_msi)
+		pci_disable_msi(pdev);
+	return;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(altr_epde_pci_tbl) = {
+	{PCI_DEVICE(PCI_VENDOR_ID_EPDE, PCI_DEVICE_ID_EPDE)},
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, altr_epde_pci_tbl);
+
+static struct pci_driver epde_driver = {
+	.name = DRIVER_NAME,
+	.probe = altr_epde_pci_probe,
+	.remove = altr_epde_pci_remove,
+	.id_table = altr_epde_pci_tbl,
+};
+
+static int __init altr_epde_pci_init(void)
+{
+	return pci_register_driver(&epde_driver);
+}
+
+static void __exit altr_epde_pci_exit(void)
+{
+	pci_unregister_driver(&epde_driver);
+}
+
+module_init(altr_epde_pci_init);
+module_exit(altr_epde_pci_exit);
+
+MODULE_DESCRIPTION("Altera EndPoint Design Example with SGDMA Block Transfer");
+MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/meta-altera/recipes-pcie/pcie-ed/files/altera_epde.h b/meta-altera/recipes-pcie/pcie-ed/files/altera_epde.h
new file mode 100644
index 0000000..23e08b5
--- /dev/null
+++ b/meta-altera/recipes-pcie/pcie-ed/files/altera_epde.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ALTERA_EPDE_H
+#define _ALTERA_EPDE_H
+
+/* From QSYS design */
+#define PCI_VENDOR_ID_EPDE      0x1172
+#define PCI_DEVICE_ID_EPDE      0xE001
+#define PCI_SUBVEN_ID_EPDE      0x1172
+#define PCI_SUBDEV_ID_EPDE      0x1484
+#define EP_DIR_BAR_NR           0
+#define  EP_OCRAM_BAR_AVOFF     0x00000000
+#define  EP_OCRAM_SIZE          0x40000
+#define EP_CSR_BAR_NR           2
+#define  EP_HIP_CRA_AVOFF       0x00000000
+#define  EP_HIP_CRA_SIZE        0x4000
+#define  EP_DMA_CSR_AVOFF       0x00004000
+#define  EP_DMA_CSR_SIZE        0x20
+#define  EP_DMA_DES_AVOFF       0x00004020
+#define  EP_DMA_DES_SIZE        0x10
+#define  EP_PERFCOUNTER_AVOFF   0x00004040
+#define  EP_PERFCOUNTER_SIZE    0x20
+#define  EP_PERFCOUNTER_MFREQ   125
+#define EP_DMA_IRQ_A2P          0x01
+#define EP_DMA2OCRAM_BASE       0x80000000
+#define EP_DMA2OCRAM_ADDR       (EP_DMA2OCRAM_BASE + EP_OCRAM_BAR_AVOFF)
+
+#endif
+
diff --git a/meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.c b/meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.c
new file mode 100644
index 0000000..1995411
--- /dev/null
+++ b/meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/module.h>
+#include <linux/ide.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+#include "altera_rpde.h"
+#include "altera_epde.h"
+
+#define MAJOR_NR                240
+#define DEVICE_NAME             "blkrpde"
+
+struct block_device_t {
+	spinlock_t     lock;
+	void __iomem   *rpdata;
+	unsigned long  rpdata_avoff;
+	unsigned long  datlen;
+	void __iomem   *dmacsr;
+	void __iomem   *dmades;
+	unsigned int   rphwirq;
+	void __iomem   *epdata;
+	unsigned long  epdata_avoff;
+	char           *sys_buf;
+	unsigned long  sys_buf_phys;
+	void __iomem   *perfcounter;
+	unsigned long  timediff;
+	unsigned long  perfcountertick;
+	unsigned int   dmaxfer_cmd;
+	struct completion dma_done;
+	struct gendisk *gd;
+};
+static struct block_device_t block_device;
+
+/* Support only one DES */
+static void init_msgdma_xfer(void __iomem *dmades, unsigned long frphy,
+			     unsigned long tophy, u32 size32)
+{
+	writel(frphy, dmades + MSGDMA_READ_ADDR);
+	writel(tophy, dmades + MSGDMA_WRITE_ADDR);
+	writel(size32, dmades + MSGDMA_DES_LEN);
+
+	/* record start time */
+	/* reset */
+	writel(PERFCTR_GBL_CLK_CTR_LO_RST,
+		block_device.perfcounter + PERFCTR_GBL_CLK_CTR_LO);
+	/* start */
+	writel(PERFCTR_GBL_CLK_CTR_HI_START,
+		block_device.perfcounter + PERFCTR_GBL_CLK_CTR_HI);
+
+	/* start transfer */
+	writel(MSGDMA_DES_CTL_TX_CH   |
+	       MSGDMA_DES_CTL_TC_IRQ  |
+	       MSGDMA_DES_CTL_GO,
+	      dmades + MSGDMA_DES_CTL);
+}
+
+/* Simple SGDMA csr with interrupt */
+static void init_msgdma_csr(void __iomem *dmacsr)
+{
+	/* Stop and clear CSR */
+	MSGDMA_STOP_CTL(dmacsr);
+
+	/* Clear any SDGMA STS */
+	MSGDMA_CLR_STS(dmacsr);
+
+	/* Enable CSR */
+	MSGDMA_ENA_CTL(dmacsr);
+}
+
+/* MSGDMA transfer function
+ *   return timediff from performance counter
+ */
+static unsigned long msgdma_transfer(unsigned long frphy, unsigned long tophy,
+				     u32 size32)
+{
+	unsigned long rt;
+
+	/* Setup performance counter and barrier */
+	block_device.perfcountertick = 0;
+	init_completion(&block_device.dma_done);
+
+	/* Setting up SGDMA CSR */
+	init_msgdma_csr(block_device.dmacsr);
+
+	/* Init SGDMA transfer */
+	init_msgdma_xfer(block_device.dmades, frphy, tophy, size32);
+
+	/* wait DMA transfer complete */
+	wait_for_completion(&block_device.dma_done);
+
+	rt = (block_device.perfcountertick / RP_PERFCOUNTER_MFREQ);  /* usec */
+
+	return rt;
+}
+
+static int block_ioctl(struct block_device *bdev, fmode_t mode,
+		       unsigned int cmd, unsigned long arg)
+{
+	int rt;
+	struct ioctl_t ioctl_data;
+	unsigned long sys_buf_avoff;
+
+	block_device.rpdata_avoff = RP_DMA2OCR_OFF;
+	sys_buf_avoff = block_device.sys_buf_phys + RP_DMA2SDRAM_OFF;
+
+	switch (cmd) {
+	case GET_IOCTL:
+		return copy_to_user((void *)arg,
+				    (void *)&block_device.datlen,
+				    sizeof(long));
+	case SET_IOCTL:
+		return copy_from_user((void *)&block_device.dmaxfer_cmd,
+					  (void __user *)arg,
+				      sizeof(int));
+	case PCI_RX_IOCTL:
+		/* From EP On-Chip RAM to RP On-Chip RAM using SGDMA */
+		memset(block_device.rpdata, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(block_device.epdata_avoff,
+						      block_device.rpdata_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	case PCI_TX_IOCTL:
+		/* From RP On-Chip RAM to EP On-Chip RAM using SGDMA */
+		memset(block_device.epdata, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(block_device.rpdata_avoff,
+						      block_device.epdata_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	case SYS_RX_IOCTL:
+		/* From EP On-Chip RAM to RP System Memory using SGDMA */
+		memset(block_device.sys_buf, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(block_device.epdata_avoff,
+						      sys_buf_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	case SYS_TX_IOCTL:
+		/* From RP System Memory to EP On-Chip RAM using SGDMA */
+		memset(block_device.epdata, 0, block_device.datlen);
+		ioctl_data.datlen = block_device.datlen;
+		ioctl_data.timediff = msgdma_transfer(sys_buf_avoff,
+						      block_device.epdata_avoff,
+						      block_device.datlen);
+		rt = copy_to_user((void *)arg, (void *)&ioctl_data,
+				  sizeof(struct ioctl_t));
+		return rt;
+	}
+
+	return -ENOTTY;
+}
+
+static struct request_queue *block_queue;
+static const struct block_device_operations block_fops = {
+	.owner = THIS_MODULE,
+	.ioctl = block_ioctl,
+};
+
+static void blk_request(struct request_queue *rq)
+{
+	int err;
+	struct request *req;
+	unsigned long offset, nbytes;
+
+	req = blk_fetch_request(rq);
+	while (req) {
+		if (req->cmd_type != REQ_TYPE_FS)
+			continue;
+
+		offset = blk_rq_pos(req) * SECTOR_SIZE;
+		nbytes = blk_rq_cur_sectors(req) * SECTOR_SIZE;
+		err = -EIO;
+		if ((offset + nbytes) <= block_device.datlen) {
+			err = 0;
+			switch (rq_data_dir(req)) {
+			case READ:
+				switch (block_device.dmaxfer_cmd) {
+				case PCI_RX_IOCTL:
+					/* Read from RP On-Chip RAM to Buffer */
+					memcpy(req->buffer,
+						block_device.rpdata + offset,
+						nbytes);
+					break;
+				case SYS_RX_IOCTL:
+					/* Read from System Buffer to Buffer */
+					memcpy(req->buffer,
+						block_device.sys_buf + offset,
+						nbytes);
+					break;
+				case PCI_TX_IOCTL:
+				case SYS_TX_IOCTL:
+					/* Read from EP On-Chip RAM to Buffer */
+					memcpy(req->buffer,
+						block_device.epdata + offset,
+						nbytes);
+					break;
+				}
+				break;
+			case WRITE:
+				switch (block_device.dmaxfer_cmd) {
+				case PCI_RX_IOCTL:
+				case SYS_RX_IOCTL:
+					/* Write from Buffer to EP OCM */
+					memcpy(block_device.epdata + offset,
+						req->buffer, nbytes);
+					break;
+				case PCI_TX_IOCTL:
+					/* Write from Buffer to RP OCM */
+					memcpy(block_device.rpdata + offset,
+						req->buffer, nbytes);
+					break;
+				case SYS_TX_IOCTL:
+					/* Write from Buffer to System Buffer */
+					memcpy(block_device.sys_buf + offset,
+						req->buffer, nbytes);
+					break;
+				}
+				break;
+			default:
+				err = -EIO;
+				pr_notice("Unknown request %u\n",
+				       rq_data_dir(req));
+				break;
+			}
+		}
+
+		if (__blk_end_request_cur(req, err) == 0)
+			req = blk_fetch_request(rq);
+	}
+}
+
+static int block_device_init(struct platform_device *pdev)
+{
+	int err;
+
+	/* register as block device */
+	err = register_blkdev(MAJOR_NR, DEVICE_NAME);
+	if (err) {
+		dev_err(&pdev->dev, "block device fail to register\n");
+		goto err_ret;
+	}
+	spin_lock_init(&block_device.lock);
+
+	block_queue = blk_init_queue(blk_request, &block_device.lock);
+	if (!block_queue) {
+		dev_err(&pdev->dev, "block_device: error in blk_init_queue\n");
+		goto err_unregdev;
+	}
+
+	/* whole disk, no partition */
+	block_device.gd = alloc_disk(1);
+	if (!block_device.gd) {
+		dev_err(&pdev->dev, "block_device: error in alloc_disk\n");
+		goto err_deinit_queue;
+	}
+
+	block_device.gd->major = MAJOR_NR;
+	block_device.gd->first_minor = 0;
+	block_device.gd->fops = &block_fops;
+	block_device.gd->private_data = &block_device;
+	sprintf(block_device.gd->disk_name, "%s%d", DEVICE_NAME, 0);
+	set_capacity(block_device.gd,
+		    (block_device.datlen/SECTOR_SIZE));
+
+	block_device.gd->queue = block_queue;
+
+	add_disk(block_device.gd);
+	return 0;
+err_deinit_queue:
+	blk_cleanup_queue(block_queue);
+err_unregdev:
+	unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+err_ret:
+	return err;
+}
+
+static irqreturn_t rpde_isr(int irq, void *arg)
+{
+	const u32 *ptr1, *ptr2;
+
+	switch (block_device.dmaxfer_cmd) {
+	case PCI_RX_IOCTL:
+	case PCI_TX_IOCTL:
+		ptr1 = (u32 *)block_device.rpdata;
+		ptr2 = (u32 *)block_device.epdata;
+		break;
+	case SYS_RX_IOCTL:
+	case SYS_TX_IOCTL:
+		ptr1 = (u32 *)block_device.sys_buf;
+		ptr2 = (u32 *)block_device.epdata;
+		break;
+	default:
+		/* Unknow command */
+		return IRQ_NONE;
+	}
+
+	/* last 4 bytes location */
+	ptr1 += ((block_device.datlen / 4) - 1);
+	ptr2 += ((block_device.datlen / 4) - 1);
+	/* wait until last 4 bytes posted write complete */
+	while (*ptr1 != *ptr2);
+
+	/* Stop counter */
+	__raw_writel(PERFCTR_GBL_CLK_CTR_LO_STOP,
+		block_device.perfcounter + PERFCTR_GBL_CLK_CTR_LO);
+
+	/* Clear Interrupt and RUN bits */
+	__raw_writel((__raw_readl(block_device.dmacsr + MSGDMA_CTL)
+				  | MSGDMA_CTL_DIS_BITS)
+				  & ~MSGDMA_CTL_IE_GLOBAL,
+		     block_device.dmacsr + MSGDMA_CTL);
+
+	if (__raw_readl(block_device.dmacsr + MSGDMA_STS) & MSGDMA_STS_IRQ)
+		/* read tick */
+		block_device.perfcountertick = readl(block_device.perfcounter +
+						     PERFCTR_GBL_CLK_CTR_LO);
+
+	/* Clear any SDGMA STS */
+	__raw_writel(MSGDMA_STS_CLR_BITS, block_device.dmacsr + MSGDMA_STS);
+
+	/* completed */
+	complete(&block_device.dma_done);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Map (physical) PCI mem into (virtual) kernel space
+ */
+static void __iomem *remap_pci_mem(ulong base, ulong size)
+{
+	ulong page_base = ((ulong) base) & PAGE_MASK;
+	ulong page_offs = ((ulong) base) - page_base;
+	void __iomem *page_remapped = ioremap_nocache(page_base,
+						      page_offs + size);
+
+	return page_remapped ? (page_remapped + page_offs) : NULL;
+}
+
+static const struct of_device_id altr_sgdma_ids[] = {
+	{ .compatible = "ALTR,msgdma-1.0", },
+	{ .compatible = "ALTR,msgdma-dispatcher-1.0", },
+	{},
+};
+
+static int altr_rpde_pci_probe(struct platform_device *pdev)
+{
+	int err = -EIO;
+	struct pci_dev *lpdev = NULL;
+	struct device_node *node = NULL;
+	struct resource res;
+	block_device.timediff = 0;
+
+	/* Max block length */
+	block_device.datlen = min3((unsigned long)RP_OCRAM_SIZE,
+				   (unsigned long)EP_OCRAM_SIZE,
+				   (unsigned long)SGDMA_MAX_SIZE);
+
+	/*********** Is the right EP available? ***********/
+	lpdev = pci_get_device(PCI_VENDOR_ID_EPDE, PCI_DEVICE_ID_EPDE, NULL);
+	if (!lpdev) {
+		dev_err(&pdev->dev, "has no require EP detected\n");
+		err = -ENODEV;
+		return err;
+	}
+	if ((lpdev->subsystem_vendor != PCI_SUBVEN_ID_EPDE) ||
+	    (lpdev->subsystem_device != PCI_SUBDEV_ID_EPDE)) {
+		dev_err(&pdev->dev, "has no require EP detected\n");
+		err = -ENODEV;
+		goto err_nodev;
+	}
+
+	/*********** RP OCRAM Setup ***********/
+	res.start = RP_OCRAM_SBASE;
+	res.end = res.start + RP_OCRAM_SIZE - 1;
+	res.flags = IORESOURCE_MEM;
+	res.name = "RP OCR";
+
+	block_device.rpdata = devm_request_and_ioremap(&pdev->dev, &res);
+	if (!block_device.rpdata) {
+		dev_err(&pdev->dev, "failed to mapping onchip memory region\n");
+		goto err_nodev;
+	}
+
+	/* Performance counter */
+	node = of_find_compatible_node(NULL, NULL, "altr,perf-counter-1.0");
+	if (of_address_to_resource(node, 0, &res)) {
+		dev_err(&pdev->dev, "Missing performance counter register\n");
+		goto err_nodev;
+	}
+	block_device.perfcounter = devm_request_and_ioremap(&pdev->dev, &res);
+	if (!block_device.perfcounter) {
+		dev_err(&pdev->dev,
+			"failed to mapping performance counter region\n");
+		goto err_nodev;
+	}
+
+	/*********** SGDMA Setup ***********/
+	node = of_find_matching_node(NULL, altr_sgdma_ids);
+	if (!node) {
+		dev_err(&pdev->dev, "No SGDMA found in Device Tree\n");
+		goto err_nodev;
+	}
+
+	/* SGDMA CSR */
+	if (of_address_to_resource(node, 0, &res)) {
+		dev_err(&pdev->dev, "Missing SGDMA CSR register\n");
+		goto err_nodev;
+	}
+	block_device.dmacsr = devm_request_and_ioremap(&pdev->dev, &res);
+	if (!block_device.dmacsr) {
+		dev_err(&pdev->dev, "fail to mapping SGDMA CSR region\n");
+		goto err_nodev;
+	}
+
+	/* SGDMA descriptor */
+	if (of_address_to_resource(node, 1, &res)) {
+		dev_err(&pdev->dev, "Missing SGDMA descriptor register\n");
+		goto err_nodev;
+	}
+	block_device.dmades = devm_request_and_ioremap(&pdev->dev, &res);
+	if (!block_device.dmades) {
+		dev_err(&pdev->dev,
+			"fail to mapping SGDMA descriptor region\n");
+		goto err_nodev;
+	}
+
+	/* verify if SGDMA status ok */
+	writel(~0UL, block_device.dmacsr + MSGDMA_STS);
+	if ((readl(block_device.dmacsr + MSGDMA_STS) & MSGDMA_STS_MASK) !=
+	    MSGDMA_STS_AT_RESET) {
+		dev_err(&pdev->dev, "SGDMA fail status test\n");
+		goto err_nodev;
+	}
+
+	/* SGDMA IRQ */
+	block_device.rphwirq = irq_of_parse_and_map(node, 0);
+	err = devm_request_irq(&pdev->dev, block_device.rphwirq, rpde_isr,
+			0, pdev->name, &block_device);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register IRQ\n");
+		goto err_nodev;
+	}
+
+	/*********** Block Setup ***********/
+	/* Init block device API */
+	err = block_device_init(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "fail to init block device\n");
+		goto err_nodev;
+	}
+
+	/* Allocate system memory for DMA trasfer */
+	block_device.sys_buf = dma_alloc_coherent(&pdev->dev,
+		block_device.datlen, (dma_addr_t *)&block_device.sys_buf_phys,
+		GFP_KERNEL | GFP_DMA);
+	if (!block_device.sys_buf) {
+		dev_err(&pdev->dev,
+			"failed to allocate system memory for DMA\n");
+		goto err_nodev;
+	}
+
+	block_device.epdata_avoff = RP_DMA2TXS_OFF + EP_OCRAM_BAR_AVOFF;
+	block_device.epdata =
+		remap_pci_mem(pci_resource_start(lpdev, EP_DIR_BAR_NR)
+			      + EP_OCRAM_BAR_AVOFF,
+			      block_device.datlen);
+	if (!block_device.epdata) {
+		dev_err(&pdev->dev, "failed to mapping epdata\n");
+		goto err_nodev;
+	}
+
+	pci_dev_put(lpdev);
+	return 0;
+err_nodev:
+	pci_dev_put(lpdev);
+	return err;
+}
+
+static int altr_rpde_pci_remove(struct platform_device *pdev)
+{
+	dma_free_coherent(&pdev->dev, block_device.datlen,
+		block_device.sys_buf, block_device.sys_buf_phys);
+	iounmap(block_device.epdata);
+	unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+	del_gendisk(block_device.gd);
+	put_disk(block_device.gd);
+	blk_cleanup_queue(block_queue);
+	return 0;
+}
+
+static const struct of_device_id rpde_of_match[] = {
+	{ .compatible = "altr,pcie-rpde", NULL },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, rpde_of_match);
+
+static struct platform_driver rpde_driver = {
+	.driver = {
+		.name = "altera-rpde",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(rpde_of_match),
+	},
+	.probe = altr_rpde_pci_probe,
+	.remove = altr_rpde_pci_remove,
+};
+
+static int __init altr_rpde_pci_init(void)
+{
+	return platform_driver_register(&rpde_driver);
+}
+
+static void __exit altr_rpde_pci_exit(void)
+{
+	platform_driver_unregister(&rpde_driver);
+}
+
+module_init(altr_rpde_pci_init);
+module_exit(altr_rpde_pci_exit);
+
+MODULE_DESCRIPTION("Altera RootPort Design Example with SGDMA Block Transfer");
+MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.h b/meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.h
new file mode 100644
index 0000000..239b04f
--- /dev/null
+++ b/meta-altera/recipes-pcie/pcie-ed/files/altera_rpde.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ALTERA_RPDE_H
+#define _ALTERA_RPDE_H
+
+/* From QSYS design */
+#define PCI_VENDOR_ID_RPDE      0x1172
+#define PCI_DEVICE_ID_RPDE      0xE000
+#define PCI_SUBVEN_ID_RPDE      0x0000  /* missing */
+#define PCI_SUBDEV_ID_RPDE      0x0000  /* missing */
+#define RP_PERFCOUNTER_MFREQ    50
+
+#define H2F_BASE                0xC0000000
+#define RP_OCRAM_AVOFF          0x20000000
+#define RP_OCRAM_SBASE          (H2F_BASE + RP_OCRAM_AVOFF)
+#define RP_OCRAM_SIZE           0x40000
+#define RP_DMA2OCR_OFF		0xE0000000
+#define RP_DMA2SDRAM_OFF	0x00000000
+#define RP_DMA2TXS_OFF		0x80000000
+
+/* HIP registers offset */
+#define A2P_INT_STS_REG         0x0040
+#define A2P_INT_ENA_REG         0x0050
+#define A2P_ADDR_MAP_LO0        0x1000
+#define A2P_ADDR_MAP_HI0        0x1004
+#define A2P_ADDR_MAP_MASK       0xFFFFFFFC
+
+/* SGDMA CSR Registers */
+#define MSGDMA_STS              0x00
+#define  MSGDMA_STS_BUSY        0x00000001
+#define  MSGDMA_STS_DES_E       0x00000002
+#define  MSGDMA_STS_DES_F       0x00000004
+#define  MSGDMA_STS_RES_E       0x00000008
+#define  MSGDMA_STS_RES_F       0x00000010
+#define  MSGDMA_STS_MASK        0x0000001F
+#define    MSGDMA_STS_AT_RESET  0x00000002
+#define  MSGDMA_STS_STOP        0x00000020
+#define  MSGDMA_STS_RESET       0x00000040
+#define  MSGDMA_STS_STOP_ERR    0x00000080
+#define  MSGDMA_STS_STOP_TER    0x00000100
+#define  MSGDMA_STS_IRQ         0x00000200
+#define MSGDMA_CTL              0x04
+#define  MSGDMA_CTL_STOP        0x00000001
+#define  MSGDMA_CTL_RESET       0x00000002
+#define  MSGDMA_CTL_STOP_ERR    0x00000004
+#define  MSGDMA_CTL_STOP_TER    0x00000008
+#define  MSGDMA_CTL_IE_GLOBAL   0x00000010
+#define  MSGDMA_CTL_STOP_DES    0x00000020
+
+/* SGDMA Descriptor */
+#define MSGDMA_READ_ADDR        0x00
+#define MSGDMA_WRITE_ADDR       0x04
+#define MSGDMA_DES_LEN          0x08
+#define MSGDMA_DES_CTL          0x0C
+#define MSGDMA_DES_CTL_TX_CH   0x00000000
+#define MSGDMA_DES_CTL_GEN_SOP 0x00000100
+#define MSGDMA_DES_CTL_GEN_EOP 0x00000200
+#define MSGDMA_DES_CTL_END_EOP 0x00001000
+#define MSGDMA_DES_CTL_END_LEN 0x00002000
+#define MSGDMA_DES_CTL_TC_IRQ  0x00004000
+#define MSGDMA_DES_CTL_ET_IRQ  0x00008000
+#define MSGDMA_DES_CTL_TE_IRQ  0x00FF0000
+#define MSGDMA_DES_CTL_GO      0x80000000
+#define SGDMA_MAX_SIZE         0xFFFFFFFF
+
+/* MSGDMA macro */
+#define MSGDMA_CTL_DIS_BITS  (MSGDMA_CTL_STOP        |  \
+			      MSGDMA_CTL_STOP_DES)
+#define MSGDMA_STOP_CTL(dmacsr)  \
+	writel((readl(dmacsr + MSGDMA_CTL)  \
+		       | MSGDMA_CTL_DIS_BITS)           \
+		       & ~MSGDMA_CTL_IE_GLOBAL,         \
+		     dmacsr + MSGDMA_CTL);
+#define MSGDMA_ENA_CTL(dmacsr)  \
+	writel((readl(dmacsr + MSGDMA_CTL)  \
+		       | MSGDMA_CTL_IE_GLOBAL)          \
+		       & ~MSGDMA_CTL_DIS_BITS,          \
+		     dmacsr + MSGDMA_CTL);
+#define MSGDMA_STS_CLR_BITS  (MSGDMA_STS_BUSY      |  \
+			      MSGDMA_STS_DES_E     |  \
+			      MSGDMA_STS_DES_F     |  \
+			      MSGDMA_STS_RES_E     |  \
+			      MSGDMA_STS_RES_F     |  \
+			      MSGDMA_STS_STOP      |  \
+			      MSGDMA_STS_RESET     |  \
+			      MSGDMA_STS_STOP_ERR  |  \
+			      MSGDMA_STS_STOP_TER  |  \
+			      MSGDMA_STS_IRQ)
+#define MSGDMA_CLR_STS(dmacsr)  \
+	writel(MSGDMA_STS_CLR_BITS,  \
+		     dmacsr + MSGDMA_STS);
+
+/* Performance Counter */
+#define PERFCTR_GBL_CLK_CTR_LO          0x00
+#define   PERFCTR_GBL_CLK_CTR_LO_STOP   0x00000000
+#define   PERFCTR_GBL_CLK_CTR_LO_RST    0x00000001
+#define PERFCTR_GBL_CLK_CTR_HI          0x04
+#define   PERFCTR_GBL_CLK_CTR_HI_START  0x00000000
+
+/* Block Device */
+struct ioctl_t {
+	unsigned long  datlen;
+	unsigned long  timediff;
+};
+
+#define IOCTL_TYPE 'Z'
+#define GET_IOCTL _IOR(IOCTL_TYPE, 1, long)
+#define SET_IOCTL _IOW(IOCTL_TYPE, 2, int)
+#define PCI_RX_IOCTL _IOR(IOCTL_TYPE, 3, struct ioctl_t)
+#define PCI_TX_IOCTL _IOR(IOCTL_TYPE, 4, struct ioctl_t)
+#define SYS_RX_IOCTL _IOR(IOCTL_TYPE, 5, struct ioctl_t)
+#define SYS_TX_IOCTL _IOR(IOCTL_TYPE, 6, struct ioctl_t)
+
+#endif
+
diff --git a/meta-altera/recipes-pcie/pcie-ed/pcie-ed-altera_1.0.bb b/meta-altera/recipes-pcie/pcie-ed/pcie-ed-altera_1.0.bb
new file mode 100644
index 0000000..ead3ed5
--- /dev/null
+++ b/meta-altera/recipes-pcie/pcie-ed/pcie-ed-altera_1.0.bb
@@ -0,0 +1,17 @@
+DESCRIPTION = "Altera PCIe RP/EP example design modules"
+LICENSE = "GPL-2.0"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0;md5=801f80980d171dd6425610833a22dbe6"
+
+inherit module
+
+PR = "r0"
+PV = "0.1"
+
+SRC_URI = "file://Makefile \
+           file://altera_rpde.c \
+           file://altera_rpde.h \
+           file://altera_epde.c \
+           file://altera_epde.h \
+          "
+
+S = "${WORKDIR}"
diff --git a/meta-altera/scripts/altera-setup-builddir b/meta-altera/scripts/altera-setup-builddir
index 4b5ec11..96b7cab 100755
--- a/meta-altera/scripts/altera-setup-builddir
+++ b/meta-altera/scripts/altera-setup-builddir
@@ -101,6 +101,6 @@ You can now run 'bitbake <target>'
 Common targets are:
 	bitbake virtual/kernel
 	bitbake virtual/bootloader
-	bitbake altera-gsrd-image
+	bitbake altera-pcie-image
 	bitbake altera-image
 EOM
-- 
1.7.1

