lkml.org 
[lkml]   [2010]   [Nov]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH] ARM: Gemini: Add support for PCI BUS
    Date
    Add support for PCI Bux on Gemini Devices SL3516
    -changed Paulius Zaleckas mail address
    -changed #include <asm/gpio.h> to #include <linux/gpio.h>

    Signed-off-by: Hans Ulli Kroll <ulli.kroll@googlemail.com>
    ---
    arch/arm/Kconfig | 1 +
    arch/arm/mach-gemini/Makefile | 2 +
    arch/arm/mach-gemini/include/mach/hardware.h | 8 +
    arch/arm/mach-gemini/include/mach/irqs.h | 7 +-
    arch/arm/mach-gemini/mm.c | 5 +
    arch/arm/mach-gemini/pci.c | 319 ++++++++++++++++++++++++++
    6 files changed, 340 insertions(+), 2 deletions(-)
    create mode 100644 arch/arm/mach-gemini/pci.c

    diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
    index a19a526..5d4b398 100644
    --- a/arch/arm/Kconfig
    +++ b/arch/arm/Kconfig
    @@ -307,6 +307,7 @@ config ARCH_GEMINI
    select CPU_FA526
    select ARCH_REQUIRE_GPIOLIB
    select ARCH_USES_GETTIMEOFFSET
    + select PCI
    help
    Support for the Cortina Systems Gemini family SoCs

    diff --git a/arch/arm/mach-gemini/Makefile b/arch/arm/mach-gemini/Makefile
    index c5b24b9..c263f48 100644
    --- a/arch/arm/mach-gemini/Makefile
    +++ b/arch/arm/mach-gemini/Makefile
    @@ -6,6 +6,8 @@

    obj-y := irq.o mm.o time.o devices.o gpio.o

    +obj-$(CONFIG_PCI) += pci.o
    +
    # Board-specific support
    obj-$(CONFIG_MACH_NAS4220B) += board-nas4220b.o
    obj-$(CONFIG_MACH_RUT100) += board-rut1xx.o
    diff --git a/arch/arm/mach-gemini/include/mach/hardware.h b/arch/arm/mach-gemini/include/mach/hardware.h
    index 213a4fc..38c530f 100644
    --- a/arch/arm/mach-gemini/include/mach/hardware.h
    +++ b/arch/arm/mach-gemini/include/mach/hardware.h
    @@ -71,4 +71,12 @@
    */
    #define IO_ADDRESS(x) ((((x) & 0xFFF00000) >> 4) | ((x) & 0x000FFFFF) | 0xF0000000)

    +/*
    + * PCI subsystem macros
    + */
    +#define PCIBIOS_MIN_IO 0x00000100
    +#define PCIBIOS_MIN_MEM 0x00000000
    +
    +#define pcibios_assign_all_busses() 1
    +
    #endif
    diff --git a/arch/arm/mach-gemini/include/mach/irqs.h b/arch/arm/mach-gemini/include/mach/irqs.h
    index 06bc47e..c737673 100644
    --- a/arch/arm/mach-gemini/include/mach/irqs.h
    +++ b/arch/arm/mach-gemini/include/mach/irqs.h
    @@ -43,11 +43,14 @@

    #define NORMAL_IRQ_NUM 32

    -#define GPIO_IRQ_BASE NORMAL_IRQ_NUM
    +#define PCI_IRQ_BASE NORMAL_IRQ_NUM
    +#define PCI_IRQ_NUM 4
    +
    +#define GPIO_IRQ_BASE (NORMAL_IRQ_NUM + PCI_IRQ_NUM)
    #define GPIO_IRQ_NUM (3 * 32)

    #define ARCH_TIMER_IRQ IRQ_TIMER2

    -#define NR_IRQS (NORMAL_IRQ_NUM + GPIO_IRQ_NUM)
    +#define NR_IRQS (NORMAL_IRQ_NUM + PCI_IRQ_NUM + GPIO_IRQ_NUM)

    #endif /* __MACH_IRQS_H__ */
    diff --git a/arch/arm/mach-gemini/mm.c b/arch/arm/mach-gemini/mm.c
    index 5194824..2bf20b2 100644
    --- a/arch/arm/mach-gemini/mm.c
    +++ b/arch/arm/mach-gemini/mm.c
    @@ -59,6 +59,11 @@ static struct map_desc gemini_io_desc[] __initdata = {
    .length = SZ_512K,
    .type = MT_DEVICE,
    }, {
    + .virtual = IO_ADDRESS(GEMINI_PCI_IO_BASE),
    + .pfn = __phys_to_pfn(GEMINI_PCI_IO_BASE),
    + .length = SZ_512K,
    + .type = MT_DEVICE,
    + }, {
    .virtual = IO_ADDRESS(GEMINI_FLASH_CTRL_BASE),
    .pfn = __phys_to_pfn(GEMINI_FLASH_CTRL_BASE),
    .length = SZ_512K,
    diff --git a/arch/arm/mach-gemini/pci.c b/arch/arm/mach-gemini/pci.c
    new file mode 100644
    index 0000000..1acdd07
    --- /dev/null
    +++ b/arch/arm/mach-gemini/pci.c
    @@ -0,0 +1,319 @@
    +/*
    + * Support for Gemini PCI Controller
    + *
    + * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
    + * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@gmail.com>
    + *
    + * based on SL2312 PCI controller code
    + * Storlink (C) 2003
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + */
    +
    +#include <linux/kernel.h>
    +#include <linux/pci.h>
    +#include <linux/irq.h>
    +
    +#include <asm/mach/pci.h>
    +#include <linux/gpio.h>
    +
    +#include <mach/irqs.h>
    +
    +#define GEMINI_PCI_IOSIZE_1M 0x0000
    +
    +#define GEMINI_PCI_PMC 0x40
    +#define GEMINI_PCI_PMCSR 0x44
    +#define GEMINI_PCI_CTRL1 0x48
    +#define GEMINI_PCI_CTRL2 0x4C
    +#define GEMINI_PCI_MEM1_BASE_SIZE 0x50
    +#define GEMINI_PCI_MEM2_BASE_SIZE 0x54
    +#define GEMINI_PCI_MEM3_BASE_SIZE 0x58
    +
    +#define PCI_CTRL2_INTSTS_OFFSET 28
    +#define PCI_CTRL2_INTMASK_OFFSET 22
    +
    +#define GEMINI_PCI_DMA_MASK 0xFFF00000
    +#define GEMINI_PCI_DMA_MEM1_BASE 0x00000000
    +#define GEMINI_PCI_DMA_MEM2_BASE 0x00000000
    +#define GEMINI_PCI_DMA_MEM3_BASE 0x00000000
    +#define GEMINI_PCI_DMA_MEM1_SIZE 7
    +#define GEMINI_PCI_DMA_MEM2_SIZE 6
    +#define GEMINI_PCI_DMA_MEM3_SIZE 6
    +
    +#define PCI_CONF_ENABLE (1 << 31)
    +#define PCI_CONF_WHERE(r) ((r) & 0xFC)
    +#define PCI_CONF_BUS(b) (((b) & 0xFF) << 16)
    +#define PCI_CONF_DEVICE(d) (((d) & 0x1F) << 11)
    +#define PCI_CONF_FUNCTION(f) (((f) & 0x07) << 8)
    +
    +#define PCI_IOSIZE_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE))
    +#define PCI_PROT_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x04)
    +#define PCI_CTRL_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x08)
    +#define PCI_SOFTRST_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x10)
    +#define PCI_CONFIG_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x28)
    +#define PCI_DATA_REG (IO_ADDRESS(GEMINI_PCI_IO_BASE) + 0x2C)
    +
    +
    +static DEFINE_SPINLOCK(gemini_pci_lock);
    +
    +static struct resource gemini_pci_resource_io = {
    + .name = "PCI I/O Space",
    + .start = IO_ADDRESS(GEMINI_PCI_IO_BASE),
    + .end = IO_ADDRESS(GEMINI_PCI_IO_BASE) + SZ_1M - 1,
    + .flags = IORESOURCE_IO,
    +};
    +
    +static struct resource gemini_pci_resource_mem = {
    + .name = "PCI Memory Space",
    + .start = GEMINI_PCI_MEM_BASE,
    + .end = GEMINI_PCI_MEM_BASE + SZ_128M - 1,
    + .flags = IORESOURCE_MEM,
    +};
    +
    +static int gemini_pci_read_config(struct pci_bus *bus, unsigned int fn,
    + int config, int size, u32 *value)
    +{
    + unsigned long irq_flags;
    +
    + spin_lock_irqsave(&gemini_pci_lock, irq_flags);
    +
    + __raw_writel(PCI_CONF_BUS(bus->number) |
    + PCI_CONF_DEVICE(PCI_SLOT(fn)) |
    + PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
    + PCI_CONF_WHERE(config) |
    + PCI_CONF_ENABLE,
    + PCI_CONFIG_REG);
    +
    + *value = __raw_readl(PCI_DATA_REG);
    +
    + if (size == 1)
    + *value = (*value >> (8 * (config & 3))) & 0xFF;
    + else if (size == 2)
    + *value = (*value >> (8 * (config & 3))) & 0xFFFF;
    +
    + spin_unlock_irqrestore(&gemini_pci_lock, irq_flags);
    +
    + dev_dbg(&bus->dev,
    + "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
    + PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value);
    +
    + return PCIBIOS_SUCCESSFUL;
    +}
    +
    +static int gemini_pci_write_config(struct pci_bus *bus, unsigned int fn,
    + int config, int size, u32 value)
    +{
    + unsigned long irq_flags = 0;
    + int ret = PCIBIOS_SUCCESSFUL;
    +
    + dev_dbg(&bus->dev,
    + "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
    + PCI_SLOT(fn), PCI_FUNC(fn), config, size, value);
    +
    + spin_lock_irqsave(&gemini_pci_lock, irq_flags);
    +
    + __raw_writel(PCI_CONF_BUS(bus->number) |
    + PCI_CONF_DEVICE(PCI_SLOT(fn)) |
    + PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
    + PCI_CONF_WHERE(config) |
    + PCI_CONF_ENABLE,
    + PCI_CONFIG_REG);
    +
    + switch (size) {
    + case 4:
    + __raw_writel(value, PCI_DATA_REG);
    + break;
    + case 2:
    + __raw_writew(value, PCI_DATA_REG + (config & 3));
    + break;
    + case 1:
    + __raw_writeb(value, PCI_DATA_REG + (config & 3));
    + break;
    + default:
    + ret = PCIBIOS_BAD_REGISTER_NUMBER;
    + }
    +
    + spin_unlock_irqrestore(&gemini_pci_lock, irq_flags);
    +
    + return ret;
    +}
    +
    +static struct pci_ops gemini_pci_ops = {
    + .read = gemini_pci_read_config,
    + .write = gemini_pci_write_config,
    +};
    +
    +static int __init gemini_pci_request_resources(struct pci_sys_data *sys)
    +{
    + if (request_resource(&ioport_resource, &gemini_pci_resource_io))
    + goto bad_resources;
    + if (request_resource(&iomem_resource, &gemini_pci_resource_mem))
    + goto bad_resources;
    +
    + sys->resource[0] = &gemini_pci_resource_io;
    + sys->resource[1] = &gemini_pci_resource_mem;
    + sys->resource[2] = 0;
    +
    + return 0;
    +
    +bad_resources:
    + pr_err("Gemini PCI: request_resource() failed. "
    + "Abort PCI bus enumeration.\n");
    + return -1;
    +}
    +
    +static int __init gemini_pci_setup(int nr, struct pci_sys_data *sys)
    +{
    + unsigned int cmd;
    +
    + if ((nr > 0) || gemini_pci_request_resources(sys))
    + return 0;
    +
    + /* setup I/O space to 1MB size */
    + __raw_writel(GEMINI_PCI_IOSIZE_1M, PCI_IOSIZE_REG);
    +
    + /* setup hostbridge */
    + cmd = __raw_readl(PCI_CTRL_REG);
    + cmd |= PCI_COMMAND_IO;
    + cmd |= PCI_COMMAND_MEMORY;
    + cmd |= PCI_COMMAND_MASTER;
    + __raw_writel(cmd, PCI_CTRL_REG);
    +
    + return 1;
    +}
    +
    +static struct pci_bus __init *gemini_pci_scan_bus(int nr,
    + struct pci_sys_data *sys)
    +{
    + unsigned int reg = 0;
    + struct pci_bus *bus = 0;
    +
    + bus = pci_scan_bus(nr, &gemini_pci_ops, sys);
    + if (bus) {
    + dev_dbg(&bus->dev, "setting up PCI DMA\n");
    + reg = (GEMINI_PCI_DMA_MEM1_BASE & GEMINI_PCI_DMA_MASK)
    + | (GEMINI_PCI_DMA_MEM1_SIZE << 16);
    + gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM1_BASE_SIZE,
    + 4, reg);
    + reg = (GEMINI_PCI_DMA_MEM2_BASE & GEMINI_PCI_DMA_MASK)
    + | (GEMINI_PCI_DMA_MEM2_SIZE << 16);
    + gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM2_BASE_SIZE,
    + 4, reg);
    + reg = (GEMINI_PCI_DMA_MEM3_BASE & GEMINI_PCI_DMA_MASK)
    + | (GEMINI_PCI_DMA_MEM3_SIZE << 16);
    + gemini_pci_write_config(bus, 0, GEMINI_PCI_MEM3_BASE_SIZE,
    + 4, reg);
    + }
    +
    + return bus;
    +}
    +
    +/* Should work with all boards based on original Storlink EVB */
    +static int __init gemini_pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
    +{
    + if (slot < 9 || slot > 12)
    + return -1;
    +
    + return PCI_IRQ_BASE + (((slot - 9) + (pin - 1)) & 0x3);
    +}
    +
    +static struct hw_pci gemini_hw_pci __initdata = {
    + .nr_controllers = 1,
    + .setup = gemini_pci_setup,
    + .scan = gemini_pci_scan_bus,
    + .swizzle = pci_std_swizzle,
    + .map_irq = gemini_pci_map_irq,
    +};
    +
    +/* we need this for muxed PCI interrupts handling */
    +static struct pci_bus bogus_pci_bus;
    +
    +static void gemini_pci_ack_irq(unsigned int irq)
    +{
    + unsigned int reg;
    +
    + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
    + reg &= ~(0xF << PCI_CTRL2_INTSTS_OFFSET);
    + reg |= 1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTSTS_OFFSET);
    + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg);
    +}
    +
    +static void gemini_pci_mask_irq(unsigned int irq)
    +{
    + unsigned int reg;
    +
    + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
    + reg &= ~((0xF << PCI_CTRL2_INTSTS_OFFSET)
    + | (1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTMASK_OFFSET)));
    + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg);
    +}
    +
    +static void gemini_pci_unmask_irq(unsigned int irq)
    +{
    + unsigned int reg;
    +
    + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
    + reg &= ~(0xF << PCI_CTRL2_INTSTS_OFFSET);
    + reg |= 1 << (irq - PCI_IRQ_BASE + PCI_CTRL2_INTMASK_OFFSET);
    + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, reg);
    +}
    +
    +static void gemini_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
    +{
    + unsigned int pci_irq_no, irq_stat, reg, i;
    +
    + gemini_pci_read_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2, 4, &reg);
    + irq_stat = reg >> PCI_CTRL2_INTSTS_OFFSET;
    +
    + for (i = 0; i < 4; i++) {
    +
    + if ((irq_stat & (1 << i)) == 0)
    + continue;
    +
    + pci_irq_no = PCI_IRQ_BASE + i;
    +
    + BUG_ON(!(irq_desc[pci_irq_no].handle_irq));
    + irq_desc[pci_irq_no].handle_irq(pci_irq_no,
    + &irq_desc[pci_irq_no]);
    + }
    +}
    +
    +static struct irq_chip gemini_pci_irq_chip = {
    + .name = "PCI",
    + .ack = gemini_pci_ack_irq,
    + .mask = gemini_pci_mask_irq,
    + .unmask = gemini_pci_unmask_irq,
    +};
    +
    +static int __init gemini_pci_init(void)
    +{
    + int i;
    +
    + for (i = 72; i <= 95; i++)
    + gpio_request(i, "PCI");
    +
    + /* initialize our bogus bus */
    + dev_set_name(&bogus_pci_bus.dev, "PCI IRQ handler");
    + bogus_pci_bus.number = 0;
    +
    + /* mask and clear all interrupts */
    + gemini_pci_write_config(&bogus_pci_bus, 0, GEMINI_PCI_CTRL2 + 2, 2,
    + 0xF000);
    +
    + for (i = PCI_IRQ_BASE; i < PCI_IRQ_BASE + 4; i++) {
    + set_irq_chip(i, &gemini_pci_irq_chip);
    + set_irq_handler(i, handle_level_irq);
    + set_irq_flags(i, IRQF_VALID);
    + }
    +
    + set_irq_chained_handler(IRQ_PCI, gemini_pci_irq_handler);
    +
    + pci_common_init(&gemini_hw_pci);
    +
    + return 0;
    +}
    +
    +subsys_initcall(gemini_pci_init);
    --
    1.7.3.2


    \
     
     \ /
      Last update: 2010-11-27 13:27    [W:4.412 / U:0.052 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site