Tizen VPCI added
authorStanislav Vorobiov <s.vorobiov@samsung.com>
Wed, 6 Jun 2012 06:56:45 +0000 (10:56 +0400)
committerEvgeny Voevodin <e.voevodin@samsung.com>
Fri, 14 Sep 2012 05:42:19 +0000 (09:42 +0400)
arch/arm/Kconfig
arch/arm/mach-exynos/Kconfig
arch/arm/mach-exynos/Makefile
arch/arm/mach-exynos/common.c
arch/arm/mach-exynos/tizen-vpci.c [new file with mode: 0644]
arch/arm/mach-exynos/tizen-vpci.h [new file with mode: 0644]

index 36586dba6fa6e0af6260dce3827cfe1495c7f592..ae53a038e39900bbed898f301c9be89ccb524dc2 100644 (file)
@@ -1467,6 +1467,13 @@ config PCI_HOST_ITE8152
 
 source "drivers/pci/Kconfig"
 
+config TIZEN_VPCI
+       bool "Tizen VPCI"
+       depends on PCI && MACH_TIZEN
+       default n
+       help
+         Support for Tizen Virtual PCI Bus.
+
 source "drivers/pcmcia/Kconfig"
 
 endmenu
index 59a67d8e4318dd0c3bc3689f82b2b9dbde652e05..28f02fbe2a4ecd4bc6a719573b852126db156c1c 100644 (file)
@@ -343,6 +343,7 @@ config MACH_TIZEN
        select S5P_SETUP_MIPIPHY
        select SAMSUNG_DEV_PWM
        select SAMSUNG_DEV_ADC
+       select MIGHT_HAVE_PCI
        help
          Machine support for Samsung Tizen Board.
 
index ed9a05c21625bae89cbed6d00fe95a641df5731b..a93eb630edd6c64970fb5e5ebbaf4b845f0c8f62 100644 (file)
@@ -75,3 +75,4 @@ obj-$(CONFIG_EXYNOS4_SETUP_KEYPAD)    += setup-keypad.o
 obj-$(CONFIG_EXYNOS4_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
 obj-$(CONFIG_EXYNOS4_SETUP_USB_PHY)    += setup-usb-phy.o
 obj-$(CONFIG_EXYNOS4_SETUP_SPI)                += setup-spi.o
+obj-$(CONFIG_TIZEN_VPCI)               += tizen-vpci.o
index 5ccd6e80a607fec8750409d6d5731dfa92f2a73e..f6bc922fed31fc07c0497452596938496f2bcd27 100644 (file)
@@ -47,6 +47,9 @@
 #include <plat/regs-serial.h>
 
 #include "common.h"
+#ifdef CONFIG_TIZEN_VPCI
+#include "tizen-vpci.h"
+#endif
 #define L2_AUX_VAL 0x7C470001
 #define L2_AUX_MASK 0xC200ffff
 
@@ -304,9 +307,23 @@ void __init exynos_init_io(struct map_desc *mach_desc, int size)
        s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids));
 }
 
+#ifdef CONFIG_TIZEN_VPCI
+static struct map_desc tizen_vpci_iodesc[] __initdata = {
+    {
+        .virtual    = (unsigned long)TIZEN_VPCI_CFG_VIRT_BASE,
+        .pfn        = __phys_to_pfn(TIZEN_VPCI_CFG_BASE),
+        .length     = TIZEN_VPCI_CFG_BASE_SIZE,
+        .type       = MT_DEVICE,
+    }
+};
+#endif
+
 static void __init exynos4_map_io(void)
 {
        iotable_init(exynos4_iodesc, ARRAY_SIZE(exynos4_iodesc));
+#ifdef CONFIG_TIZEN_VPCI
+       iotable_init(tizen_vpci_iodesc, ARRAY_SIZE(tizen_vpci_iodesc));
+#endif
 
        if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_0)
                iotable_init(exynos4_iodesc0, ARRAY_SIZE(exynos4_iodesc0));
diff --git a/arch/arm/mach-exynos/tizen-vpci.c b/arch/arm/mach-exynos/tizen-vpci.c
new file mode 100644 (file)
index 0000000..1aff971
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ *  linux/arch/arm/mach-exynos/tizen-vpci.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach/pci.h>
+
+#include "tizen-vpci.h"
+
+#define DEVICE_ID_OFFSET    0x00
+#define CSR_OFFSET          0x04
+#define CLASS_ID_OFFSET     0x08
+
+#define TIZEN_VPCI_DEVICE_ID    0x030010ee
+#define TIZEN_VPCI_CLASS_ID     0x0b400000
+
+static unsigned long pci_slot_ignore = 0;
+
+static int __init tizen_vpci_slot_ignore(char *str)
+{
+    int retval;
+    int slot;
+
+    while ((retval = get_option(&str,&slot))) {
+        if ((slot < 0) || (slot > 31)) {
+            printk("Illegal slot value: %d\n",slot);
+        } else {
+            pci_slot_ignore |= (1 << slot);
+        }
+    }
+    return 1;
+}
+
+__setup("pci_slot_ignore=", tizen_vpci_slot_ignore);
+
+static void __iomem *__pci_addr(struct pci_bus *bus,
+                unsigned int devfn, int offset)
+{
+    unsigned int busnr = bus->number;
+
+    /*
+     * Trap out illegal values
+     */
+    if (offset > 255)
+        BUG();
+    if (busnr > 255)
+        BUG();
+    if (devfn > 255)
+        BUG();
+
+    return TIZEN_VPCI_CFG_VIRT_BASE + ((busnr << 16) |
+        (PCI_SLOT(devfn) << 11) | (PCI_FUNC(devfn) << 8) | offset);
+}
+
+static int tizen_vpci_read_config(struct pci_bus *bus, unsigned int devfn, int where,
+                 int size, u32 *val)
+{
+    void __iomem *addr = __pci_addr(bus, devfn, where & ~3);
+    u32 v;
+    int slot = PCI_SLOT(devfn);
+
+    if (pci_slot_ignore & (1 << slot)) {
+        /* Ignore this slot */
+        switch (size) {
+        case 1:
+            v = 0xff;
+            break;
+        case 2:
+            v = 0xffff;
+            break;
+        default:
+            v = 0xffffffff;
+        }
+    } else {
+        switch (size) {
+        case 1:
+            v = __raw_readl(addr);
+            if (where & 2) v >>= 16;
+            if (where & 1) v >>= 8;
+            v &= 0xff;
+            break;
+
+        case 2:
+            v = __raw_readl(addr);
+            if (where & 2) v >>= 16;
+            v &= 0xffff;
+            break;
+
+        default:
+            v = __raw_readl(addr);
+            break;
+        }
+    }
+
+    *val = v;
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int tizen_vpci_write_config(struct pci_bus *bus, unsigned int devfn, int where,
+                  int size, u32 val)
+{
+    void __iomem *addr = __pci_addr(bus, devfn, where);
+    int slot = PCI_SLOT(devfn);
+
+    if (pci_slot_ignore & (1 << slot)) {
+        return PCIBIOS_SUCCESSFUL;
+    }
+
+    switch (size) {
+    case 1:
+        __raw_writeb((u8)val, addr);
+        break;
+
+    case 2:
+        __raw_writew((u16)val, addr);
+        break;
+
+    case 4:
+        __raw_writel(val, addr);
+        break;
+    }
+
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops tizen_vpci_ops = {
+    .read   = tizen_vpci_read_config,
+    .write  = tizen_vpci_write_config,
+};
+
+static struct resource io_mem = {
+    .name   = "PCI I/O space",
+    .start  = TIZEN_VPCI_MEM_BASE0,
+    .end    = TIZEN_VPCI_MEM_BASE0+TIZEN_VPCI_MEM_BASE0_SIZE-1,
+    .flags  = IORESOURCE_IO,
+};
+
+static struct resource non_mem = {
+    .name   = "PCI non-prefetchable",
+    .start  = TIZEN_VPCI_MEM_BASE1,
+    .end    = TIZEN_VPCI_MEM_BASE1+TIZEN_VPCI_MEM_BASE1_SIZE-1,
+    .flags  = IORESOURCE_MEM,
+};
+
+static struct resource pre_mem = {
+    .name   = "PCI prefetchable",
+    .start  = TIZEN_VPCI_MEM_BASE2,
+    .end    = TIZEN_VPCI_MEM_BASE2+TIZEN_VPCI_MEM_BASE2_SIZE-1,
+    .flags  = IORESOURCE_MEM | IORESOURCE_PREFETCH,
+};
+
+static int __init tizen_vpci_setup_resources(struct pci_sys_data *sys)
+{
+    int ret = 0;
+
+    ret = request_resource(&iomem_resource, &io_mem);
+    if (ret) {
+        printk(KERN_ERR "PCI: unable to allocate I/O "
+               "memory region (%d)\n", ret);
+        goto out;
+    }
+    ret = request_resource(&iomem_resource, &non_mem);
+    if (ret) {
+        printk(KERN_ERR "PCI: unable to allocate non-prefetchable "
+               "memory region (%d)\n", ret);
+        goto release_io_mem;
+    }
+    ret = request_resource(&iomem_resource, &pre_mem);
+    if (ret) {
+        printk(KERN_ERR "PCI: unable to allocate prefetchable "
+               "memory region (%d)\n", ret);
+        goto release_non_mem;
+    }
+
+    /*
+     * the IO resource for this bus
+     * the mem resource for this bus
+     * the prefetch mem resource for this bus
+     */
+    pci_add_resource_offset(&sys->resources, &io_mem, sys->io_offset);
+    pci_add_resource_offset(&sys->resources, &non_mem, sys->mem_offset);
+    pci_add_resource_offset(&sys->resources, &pre_mem, sys->mem_offset);
+
+    goto out;
+
+release_non_mem:
+    release_resource(&non_mem);
+release_io_mem:
+    release_resource(&io_mem);
+out:
+    return ret;
+}
+
+int __init tizen_vpci_setup(int nr, struct pci_sys_data *sys)
+{
+    int ret = 0;
+    int i;
+    int myslot = -1;
+    unsigned long val;
+    void __iomem *local_pci_cfg_base;
+
+    if (nr == 0) {
+        sys->mem_offset = 0;
+        ret = tizen_vpci_setup_resources(sys);
+        if (ret < 0) {
+            printk("tizen_vpci_setup: resources... oops?\n");
+            goto out;
+        }
+    } else {
+        printk("tizen_vpci_setup: resources... nr == 0??\n");
+        goto out;
+    }
+
+    /*
+     *  We need to discover the PCI core first to configure itself
+     *  before the main PCI probing is performed
+     */
+    for (i=0; i<32; i++)
+        if ((__raw_readl(TIZEN_VPCI_CFG_VIRT_BASE+(i<<11)+DEVICE_ID_OFFSET)
+                == TIZEN_VPCI_DEVICE_ID) &&
+            (__raw_readl(TIZEN_VPCI_CFG_VIRT_BASE+(i<<11)+CLASS_ID_OFFSET)
+                == TIZEN_VPCI_CLASS_ID)) {
+            myslot = i;
+            break;
+        }
+
+    if (myslot == -1) {
+        printk("Cannot find PCI core!\n");
+        ret = -EIO;
+        goto out;
+    }
+
+    printk("PCI core found (slot %d)\n",myslot);
+
+    local_pci_cfg_base = TIZEN_VPCI_CFG_VIRT_BASE + (myslot << 11);
+
+    val = __raw_readl(local_pci_cfg_base + CSR_OFFSET);
+    val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
+    __raw_writel(val, local_pci_cfg_base + CSR_OFFSET);
+
+    /*
+     * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM
+     */
+    __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0);
+    __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1);
+    __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2);
+
+    /*
+     * Do not to map PCI device into memory space
+     */
+    pci_slot_ignore |= (1 << myslot);
+    ret = 1;
+
+out:
+    return ret;
+}
+
+struct pci_bus * __init tizen_vpci_scan_bus(int nr, struct pci_sys_data *sys)
+{
+    return pci_scan_root_bus(NULL, sys->busnr, &tizen_vpci_ops, sys,
+                 &sys->resources);
+}
+
+void __init tizen_vpci_preinit(void)
+{
+    pcibios_min_io = TIZEN_VPCI_MEM_BASE0_SIZE;
+    pcibios_min_mem = TIZEN_VPCI_MEM_BASE1_SIZE;
+}
+
+static int __init tizen_vpci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+    int irq;
+    int devslot = PCI_SLOT(dev->devfn);
+
+    switch (pin) {
+    case 1:
+        irq = TIZEN_VPCI_IRQ0;
+        break;
+    case 2:
+        irq = TIZEN_VPCI_IRQ1;
+        break;
+    case 3:
+        irq = TIZEN_VPCI_IRQ2;
+        break;
+    case 4:
+        irq = TIZEN_VPCI_IRQ3;
+        break;
+    default:
+        BUG();
+    }
+
+    printk("PCI map irq: slot %d, pin %d, devslot %d, irq: %d\n", slot, pin, devslot, irq);
+
+    return irq;
+}
+
+static struct hw_pci tizen_vpci __initdata = {
+    .swizzle        = NULL,
+    .map_irq        = tizen_vpci_map_irq,
+    .nr_controllers = 1,
+    .setup          = tizen_vpci_setup,
+    .scan           = tizen_vpci_scan_bus,
+    .preinit        = tizen_vpci_preinit,
+};
+
+static int __init tizen_vpci_init(void)
+{
+    pci_common_init(&tizen_vpci);
+    return 0;
+}
+
+subsys_initcall(tizen_vpci_init);
diff --git a/arch/arm/mach-exynos/tizen-vpci.h b/arch/arm/mach-exynos/tizen-vpci.h
new file mode 100644 (file)
index 0000000..7527160
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  linux/arch/arm/mach-exynos/tizen-vpci.h
+ */
+
+#ifndef __ASM_ARCH_MACH_EXYNOS_TIZEN_VPCI_H
+#define __ASM_ARCH_MACH_EXYNOS_TIZEN_VPCI_H
+
+#include <linux/init.h>
+
+#include <mach/irqs.h>
+
+#define TIZEN_VPCI_CFG_VIRT_BASE (void __iomem *)0xf5000000ul
+
+/* PCI space */
+#define TIZEN_VPCI_CFG_BASE         0xC2000000
+#define TIZEN_VPCI_MEM_BASE0        0xC3000000
+#define TIZEN_VPCI_MEM_BASE1        0xD0000000
+#define TIZEN_VPCI_MEM_BASE2        0xE0000000
+/* Sizes of above maps */
+#define TIZEN_VPCI_CFG_BASE_SIZE    0x01000000
+#define TIZEN_VPCI_MEM_BASE0_SIZE   0x0C000000   /* 32Mb */
+#define TIZEN_VPCI_MEM_BASE1_SIZE   0x10000000   /* 256Mb */
+#define TIZEN_VPCI_MEM_BASE2_SIZE   0x10000000   /* 256Mb */
+
+/* PCI interrupts */
+#define TIZEN_VPCI_IRQ0 IRQ_EINT(8)
+#define TIZEN_VPCI_IRQ1 IRQ_EINT(9)
+#define TIZEN_VPCI_IRQ2 IRQ_EINT(10)
+#define TIZEN_VPCI_IRQ3 IRQ_EINT(11)
+
+#endif