linux/arch/arm/mach-versatile/pci.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-versatile/pci.c
   3 *
   4 * (C) Copyright Koninklijke Philips Electronics NV 2004. All rights reserved.
   5 * You can redistribute and/or modify this software under the terms of version 2
   6 * of the GNU General Public License as published by the Free Software Foundation.
   7 * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED
   8 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   9 * General Public License for more details.
  10 * Koninklijke Philips Electronics nor its subsidiaries is obligated to provide any support for this software.
  11 *
  12 * ARM Versatile PCI driver.
  13 *
  14 * 14/04/2005 Initial version, colin.king@philips.com
  15 *
  16 */
  17#include <linux/kernel.h>
  18#include <linux/pci.h>
  19#include <linux/ioport.h>
  20#include <linux/interrupt.h>
  21#include <linux/spinlock.h>
  22#include <linux/init.h>
  23#include <linux/io.h>
  24
  25#include <mach/hardware.h>
  26#include <mach/irqs.h>
  27#include <asm/irq.h>
  28#include <asm/mach/pci.h>
  29
  30/*
  31 * these spaces are mapped using the following base registers:
  32 *
  33 * Usage Local Bus Memory         Base/Map registers used
  34 *
  35 * Mem   50000000 - 5FFFFFFF      LB_BASE0/LB_MAP0,  non prefetch
  36 * Mem   60000000 - 6FFFFFFF      LB_BASE1/LB_MAP1,  prefetch
  37 * IO    44000000 - 4FFFFFFF      LB_BASE2/LB_MAP2,  IO
  38 * Cfg   42000000 - 42FFFFFF      PCI config
  39 *
  40 */
  41#define __IO_ADDRESS(n) ((void __iomem *)(unsigned long)IO_ADDRESS(n))
  42#define SYS_PCICTL              __IO_ADDRESS(VERSATILE_SYS_PCICTL)
  43#define PCI_IMAP0               __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x0)
  44#define PCI_IMAP1               __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x4)
  45#define PCI_IMAP2               __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x8)
  46#define PCI_SMAP0               __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x14)
  47#define PCI_SMAP1               __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x18)
  48#define PCI_SMAP2               __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x1c)
  49#define PCI_SELFID              __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0xc)
  50
  51#define DEVICE_ID_OFFSET                0x00
  52#define CSR_OFFSET                      0x04
  53#define CLASS_ID_OFFSET                 0x08
  54
  55#define VP_PCI_DEVICE_ID                0x030010ee
  56#define VP_PCI_CLASS_ID                 0x0b400000
  57
  58static unsigned long pci_slot_ignore = 0;
  59
  60static int __init versatile_pci_slot_ignore(char *str)
  61{
  62        int retval;
  63        int slot;
  64
  65        while ((retval = get_option(&str,&slot))) {
  66                if ((slot < 0) || (slot > 31)) {
  67                        printk("Illegal slot value: %d\n",slot);
  68                } else {
  69                        pci_slot_ignore |= (1 << slot);
  70                }
  71        }
  72        return 1;
  73}
  74
  75__setup("pci_slot_ignore=", versatile_pci_slot_ignore);
  76
  77
  78static void __iomem *__pci_addr(struct pci_bus *bus,
  79                                unsigned int devfn, int offset)
  80{
  81        unsigned int busnr = bus->number;
  82
  83        /*
  84         * Trap out illegal values
  85         */
  86        if (offset > 255)
  87                BUG();
  88        if (busnr > 255)
  89                BUG();
  90        if (devfn > 255)
  91                BUG();
  92
  93        return VERSATILE_PCI_CFG_VIRT_BASE + ((busnr << 16) |
  94                (PCI_SLOT(devfn) << 11) | (PCI_FUNC(devfn) << 8) | offset);
  95}
  96
  97static int versatile_read_config(struct pci_bus *bus, unsigned int devfn, int where,
  98                                 int size, u32 *val)
  99{
 100        void __iomem *addr = __pci_addr(bus, devfn, where & ~3);
 101        u32 v;
 102        int slot = PCI_SLOT(devfn);
 103
 104        if (pci_slot_ignore & (1 << slot)) {
 105                /* Ignore this slot */
 106                switch (size) {
 107                case 1:
 108                        v = 0xff;
 109                        break;
 110                case 2:
 111                        v = 0xffff;
 112                        break;
 113                default:
 114                        v = 0xffffffff;
 115                }
 116        } else {
 117                switch (size) {
 118                case 1:
 119                        v = __raw_readl(addr);
 120                        if (where & 2) v >>= 16;
 121                        if (where & 1) v >>= 8;
 122                        v &= 0xff;
 123                        break;
 124
 125                case 2:
 126                        v = __raw_readl(addr);
 127                        if (where & 2) v >>= 16;
 128                        v &= 0xffff;
 129                        break;
 130
 131                default:
 132                        v = __raw_readl(addr);
 133                        break;
 134                }
 135        }
 136
 137        *val = v;
 138        return PCIBIOS_SUCCESSFUL;
 139}
 140
 141static int versatile_write_config(struct pci_bus *bus, unsigned int devfn, int where,
 142                                  int size, u32 val)
 143{
 144        void __iomem *addr = __pci_addr(bus, devfn, where);
 145        int slot = PCI_SLOT(devfn);
 146
 147        if (pci_slot_ignore & (1 << slot)) {
 148                return PCIBIOS_SUCCESSFUL;
 149        }
 150
 151        switch (size) {
 152        case 1:
 153                __raw_writeb((u8)val, addr);
 154                break;
 155
 156        case 2:
 157                __raw_writew((u16)val, addr);
 158                break;
 159
 160        case 4:
 161                __raw_writel(val, addr);
 162                break;
 163        }
 164
 165        return PCIBIOS_SUCCESSFUL;
 166}
 167
 168static struct pci_ops pci_versatile_ops = {
 169        .read   = versatile_read_config,
 170        .write  = versatile_write_config,
 171};
 172
 173static struct resource unused_mem = {
 174        .name   = "PCI unused",
 175        .start  = VERSATILE_PCI_MEM_BASE0,
 176        .end    = VERSATILE_PCI_MEM_BASE0+VERSATILE_PCI_MEM_BASE0_SIZE-1,
 177        .flags  = IORESOURCE_MEM,
 178};
 179
 180static struct resource non_mem = {
 181        .name   = "PCI non-prefetchable",
 182        .start  = VERSATILE_PCI_MEM_BASE1,
 183        .end    = VERSATILE_PCI_MEM_BASE1+VERSATILE_PCI_MEM_BASE1_SIZE-1,
 184        .flags  = IORESOURCE_MEM,
 185};
 186
 187static struct resource pre_mem = {
 188        .name   = "PCI prefetchable",
 189        .start  = VERSATILE_PCI_MEM_BASE2,
 190        .end    = VERSATILE_PCI_MEM_BASE2+VERSATILE_PCI_MEM_BASE2_SIZE-1,
 191        .flags  = IORESOURCE_MEM | IORESOURCE_PREFETCH,
 192};
 193
 194static int __init pci_versatile_setup_resources(struct pci_sys_data *sys)
 195{
 196        int ret = 0;
 197
 198        ret = request_resource(&iomem_resource, &unused_mem);
 199        if (ret) {
 200                printk(KERN_ERR "PCI: unable to allocate unused "
 201                       "memory region (%d)\n", ret);
 202                goto out;
 203        }
 204        ret = request_resource(&iomem_resource, &non_mem);
 205        if (ret) {
 206                printk(KERN_ERR "PCI: unable to allocate non-prefetchable "
 207                       "memory region (%d)\n", ret);
 208                goto release_unused_mem;
 209        }
 210        ret = request_resource(&iomem_resource, &pre_mem);
 211        if (ret) {
 212                printk(KERN_ERR "PCI: unable to allocate prefetchable "
 213                       "memory region (%d)\n", ret);
 214                goto release_non_mem;
 215        }
 216
 217        /*
 218         * the mem resource for this bus
 219         * the prefetch mem resource for this bus
 220         */
 221        pci_add_resource_offset(&sys->resources, &non_mem, sys->mem_offset);
 222        pci_add_resource_offset(&sys->resources, &pre_mem, sys->mem_offset);
 223
 224        goto out;
 225
 226 release_non_mem:
 227        release_resource(&non_mem);
 228 release_unused_mem:
 229        release_resource(&unused_mem);
 230 out:
 231        return ret;
 232}
 233
 234int __init pci_versatile_setup(int nr, struct pci_sys_data *sys)
 235{
 236        int ret = 0;
 237        int i;
 238        int myslot = -1;
 239        unsigned long val;
 240        void __iomem *local_pci_cfg_base;
 241
 242        val = __raw_readl(SYS_PCICTL);
 243        if (!(val & 1)) {
 244                printk("Not plugged into PCI backplane!\n");
 245                ret = -EIO;
 246                goto out;
 247        }
 248
 249        ret = pci_ioremap_io(0, VERSATILE_PCI_IO_BASE);
 250        if (ret)
 251                goto out;
 252
 253        if (nr == 0) {
 254                ret = pci_versatile_setup_resources(sys);
 255                if (ret < 0) {
 256                        printk("pci_versatile_setup: resources... oops?\n");
 257                        goto out;
 258                }
 259        } else {
 260                printk("pci_versatile_setup: resources... nr == 0??\n");
 261                goto out;
 262        }
 263
 264        /*
 265         *  We need to discover the PCI core first to configure itself
 266         *  before the main PCI probing is performed
 267         */
 268        for (i=0; i<32; i++)
 269                if ((__raw_readl(VERSATILE_PCI_VIRT_BASE+(i<<11)+DEVICE_ID_OFFSET) == VP_PCI_DEVICE_ID) &&
 270                    (__raw_readl(VERSATILE_PCI_VIRT_BASE+(i<<11)+CLASS_ID_OFFSET) == VP_PCI_CLASS_ID)) {
 271                        myslot = i;
 272                        break;
 273                }
 274
 275        if (myslot == -1) {
 276                printk("Cannot find PCI core!\n");
 277                ret = -EIO;
 278                goto out;
 279        }
 280
 281        printk("PCI core found (slot %d)\n",myslot);
 282
 283        __raw_writel(myslot, PCI_SELFID);
 284        local_pci_cfg_base = VERSATILE_PCI_CFG_VIRT_BASE + (myslot << 11);
 285
 286        val = __raw_readl(local_pci_cfg_base + CSR_OFFSET);
 287        val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
 288        __raw_writel(val, local_pci_cfg_base + CSR_OFFSET);
 289
 290        /*
 291         * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM
 292         */
 293        __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0);
 294        __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1);
 295        __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2);
 296
 297        /*
 298         * For many years the kernel and QEMU were symbiotically buggy
 299         * in that they both assumed the same broken IRQ mapping.
 300         * QEMU therefore attempts to auto-detect old broken kernels
 301         * so that they still work on newer QEMU as they did on old
 302         * QEMU. Since we now use the correct (ie matching-hardware)
 303         * IRQ mapping we write a definitely different value to a
 304         * PCI_INTERRUPT_LINE register to tell QEMU that we expect
 305         * real hardware behaviour and it need not be backwards
 306         * compatible for us. This write is harmless on real hardware.
 307         */
 308        __raw_writel(0, VERSATILE_PCI_VIRT_BASE+PCI_INTERRUPT_LINE);
 309
 310        /*
 311         * Do not to map Versatile FPGA PCI device into memory space
 312         */
 313        pci_slot_ignore |= (1 << myslot);
 314        ret = 1;
 315
 316 out:
 317        return ret;
 318}
 319
 320
 321void __init pci_versatile_preinit(void)
 322{
 323        pcibios_min_mem = 0x50000000;
 324
 325        __raw_writel(VERSATILE_PCI_MEM_BASE0 >> 28, PCI_IMAP0);
 326        __raw_writel(VERSATILE_PCI_MEM_BASE1 >> 28, PCI_IMAP1);
 327        __raw_writel(VERSATILE_PCI_MEM_BASE2 >> 28, PCI_IMAP2);
 328
 329        __raw_writel(PHYS_OFFSET >> 28, PCI_SMAP0);
 330        __raw_writel(PHYS_OFFSET >> 28, PCI_SMAP1);
 331        __raw_writel(PHYS_OFFSET >> 28, PCI_SMAP2);
 332
 333        __raw_writel(1, SYS_PCICTL);
 334}
 335
 336/*
 337 * map the specified device/slot/pin to an IRQ.   Different backplanes may need to modify this.
 338 */
 339static int __init versatile_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 340{
 341        int irq;
 342
 343        /*
 344         * Slot INTA    INTB    INTC    INTD
 345         * 31   PCI1    PCI2    PCI3    PCI0
 346         * 30   PCI0    PCI1    PCI2    PCI3
 347         * 29   PCI3    PCI0    PCI1    PCI2
 348         */
 349        irq = IRQ_SIC_PCI0 + ((slot + 2 + pin - 1) & 3);
 350
 351        return irq;
 352}
 353
 354static struct hw_pci versatile_pci __initdata = {
 355        .map_irq                = versatile_map_irq,
 356        .nr_controllers         = 1,
 357        .ops                    = &pci_versatile_ops,
 358        .setup                  = pci_versatile_setup,
 359        .preinit                = pci_versatile_preinit,
 360};
 361
 362static int __init versatile_pci_init(void)
 363{
 364        pci_common_init(&versatile_pci);
 365        return 0;
 366}
 367
 368subsys_initcall(versatile_pci_init);
 369