From c2e4da7d28d72b03af98a51cf44ab9e0cf51b174 Mon Sep 17 00:00:00 2001 From: Stanislav Vorobiov Date: Wed, 6 Jun 2012 10:56:45 +0400 Subject: [PATCH] Tizen VPCI added --- arch/arm/Kconfig | 7 + arch/arm/mach-exynos/Kconfig | 1 + arch/arm/mach-exynos/Makefile | 1 + arch/arm/mach-exynos/common.c | 17 ++ arch/arm/mach-exynos/tizen-vpci.c | 321 ++++++++++++++++++++++++++++++ arch/arm/mach-exynos/tizen-vpci.h | 31 +++ 6 files changed, 378 insertions(+) create mode 100644 arch/arm/mach-exynos/tizen-vpci.c create mode 100644 arch/arm/mach-exynos/tizen-vpci.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 36586dba6fa6..ae53a038e399 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -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 diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 59a67d8e4318..28f02fbe2a4e 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -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. diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index ed9a05c21625..a93eb630edd6 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -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 diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index 5ccd6e80a607..f6bc922fed31 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -47,6 +47,9 @@ #include #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 index 000000000000..1aff9717f900 --- /dev/null +++ b/arch/arm/mach-exynos/tizen-vpci.c @@ -0,0 +1,321 @@ +/* + * linux/arch/arm/mach-exynos/tizen-vpci.c + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 index 000000000000..752716007325 --- /dev/null +++ b/arch/arm/mach-exynos/tizen-vpci.h @@ -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 + +#include + +#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 -- 2.34.1