linux/arch/arm/plat-orion/pcie.c
<<
>>
Prefs
   1/*
   2 * arch/arm/plat-orion/pcie.c
   3 *
   4 * Marvell Orion SoC PCIe handling.
   5 *
   6 * This file is licensed under the terms of the GNU General Public
   7 * License version 2.  This program is licensed "as is" without any
   8 * warranty of any kind, whether express or implied.
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/pci.h>
  13#include <linux/mbus.h>
  14#include <asm/mach/pci.h>
  15#include <plat/pcie.h>
  16#include <linux/delay.h>
  17
  18/*
  19 * PCIe unit register offsets.
  20 */
  21#define PCIE_DEV_ID_OFF         0x0000
  22#define PCIE_CMD_OFF            0x0004
  23#define PCIE_DEV_REV_OFF        0x0008
  24#define PCIE_BAR_LO_OFF(n)      (0x0010 + ((n) << 3))
  25#define PCIE_BAR_HI_OFF(n)      (0x0014 + ((n) << 3))
  26#define PCIE_HEADER_LOG_4_OFF   0x0128
  27#define PCIE_BAR_CTRL_OFF(n)    (0x1804 + ((n - 1) * 4))
  28#define PCIE_WIN04_CTRL_OFF(n)  (0x1820 + ((n) << 4))
  29#define PCIE_WIN04_BASE_OFF(n)  (0x1824 + ((n) << 4))
  30#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4))
  31#define PCIE_WIN5_CTRL_OFF      0x1880
  32#define PCIE_WIN5_BASE_OFF      0x1884
  33#define PCIE_WIN5_REMAP_OFF     0x188c
  34#define PCIE_CONF_ADDR_OFF      0x18f8
  35#define  PCIE_CONF_ADDR_EN              0x80000000
  36#define  PCIE_CONF_REG(r)               ((((r) & 0xf00) << 16) | ((r) & 0xfc))
  37#define  PCIE_CONF_BUS(b)               (((b) & 0xff) << 16)
  38#define  PCIE_CONF_DEV(d)               (((d) & 0x1f) << 11)
  39#define  PCIE_CONF_FUNC(f)              (((f) & 0x7) << 8)
  40#define PCIE_CONF_DATA_OFF      0x18fc
  41#define PCIE_MASK_OFF           0x1910
  42#define PCIE_CTRL_OFF           0x1a00
  43#define  PCIE_CTRL_X1_MODE              0x0001
  44#define PCIE_STAT_OFF           0x1a04
  45#define  PCIE_STAT_DEV_OFFS             20
  46#define  PCIE_STAT_DEV_MASK             0x1f
  47#define  PCIE_STAT_BUS_OFFS             8
  48#define  PCIE_STAT_BUS_MASK             0xff
  49#define  PCIE_STAT_LINK_DOWN            1
  50#define PCIE_DEBUG_CTRL         0x1a60
  51#define  PCIE_DEBUG_SOFT_RESET          (1<<20)
  52
  53
  54u32 __init orion_pcie_dev_id(void __iomem *base)
  55{
  56        return readl(base + PCIE_DEV_ID_OFF) >> 16;
  57}
  58
  59u32 __init orion_pcie_rev(void __iomem *base)
  60{
  61        return readl(base + PCIE_DEV_REV_OFF) & 0xff;
  62}
  63
  64int orion_pcie_link_up(void __iomem *base)
  65{
  66        return !(readl(base + PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
  67}
  68
  69int __init orion_pcie_x4_mode(void __iomem *base)
  70{
  71        return !(readl(base + PCIE_CTRL_OFF) & PCIE_CTRL_X1_MODE);
  72}
  73
  74int orion_pcie_get_local_bus_nr(void __iomem *base)
  75{
  76        u32 stat = readl(base + PCIE_STAT_OFF);
  77
  78        return (stat >> PCIE_STAT_BUS_OFFS) & PCIE_STAT_BUS_MASK;
  79}
  80
  81void __init orion_pcie_set_local_bus_nr(void __iomem *base, int nr)
  82{
  83        u32 stat;
  84
  85        stat = readl(base + PCIE_STAT_OFF);
  86        stat &= ~(PCIE_STAT_BUS_MASK << PCIE_STAT_BUS_OFFS);
  87        stat |= nr << PCIE_STAT_BUS_OFFS;
  88        writel(stat, base + PCIE_STAT_OFF);
  89}
  90
  91void __init orion_pcie_reset(void __iomem *base)
  92{
  93        u32 reg;
  94        int i;
  95
  96        /*
  97         * MV-S104860-U0, Rev. C:
  98         * PCI Express Unit Soft Reset
  99         * When set, generates an internal reset in the PCI Express unit.
 100         * This bit should be cleared after the link is re-established.
 101         */
 102        reg = readl(base + PCIE_DEBUG_CTRL);
 103        reg |= PCIE_DEBUG_SOFT_RESET;
 104        writel(reg, base + PCIE_DEBUG_CTRL);
 105
 106        for (i = 0; i < 20; i++) {
 107                mdelay(10);
 108
 109                if (orion_pcie_link_up(base))
 110                        break;
 111        }
 112
 113        reg &= ~(PCIE_DEBUG_SOFT_RESET);
 114        writel(reg, base + PCIE_DEBUG_CTRL);
 115}
 116
 117/*
 118 * Setup PCIE BARs and Address Decode Wins:
 119 * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks
 120 * WIN[0-3] -> DRAM bank[0-3]
 121 */
 122static void __init orion_pcie_setup_wins(void __iomem *base,
 123                                         struct mbus_dram_target_info *dram)
 124{
 125        u32 size;
 126        int i;
 127
 128        /*
 129         * First, disable and clear BARs and windows.
 130         */
 131        for (i = 1; i <= 2; i++) {
 132                writel(0, base + PCIE_BAR_CTRL_OFF(i));
 133                writel(0, base + PCIE_BAR_LO_OFF(i));
 134                writel(0, base + PCIE_BAR_HI_OFF(i));
 135        }
 136
 137        for (i = 0; i < 5; i++) {
 138                writel(0, base + PCIE_WIN04_CTRL_OFF(i));
 139                writel(0, base + PCIE_WIN04_BASE_OFF(i));
 140                writel(0, base + PCIE_WIN04_REMAP_OFF(i));
 141        }
 142
 143        writel(0, base + PCIE_WIN5_CTRL_OFF);
 144        writel(0, base + PCIE_WIN5_BASE_OFF);
 145        writel(0, base + PCIE_WIN5_REMAP_OFF);
 146
 147        /*
 148         * Setup windows for DDR banks.  Count total DDR size on the fly.
 149         */
 150        size = 0;
 151        for (i = 0; i < dram->num_cs; i++) {
 152                struct mbus_dram_window *cs = dram->cs + i;
 153
 154                writel(cs->base & 0xffff0000, base + PCIE_WIN04_BASE_OFF(i));
 155                writel(0, base + PCIE_WIN04_REMAP_OFF(i));
 156                writel(((cs->size - 1) & 0xffff0000) |
 157                        (cs->mbus_attr << 8) |
 158                        (dram->mbus_dram_target_id << 4) | 1,
 159                                base + PCIE_WIN04_CTRL_OFF(i));
 160
 161                size += cs->size;
 162        }
 163
 164        /*
 165         * Round up 'size' to the nearest power of two.
 166         */
 167        if ((size & (size - 1)) != 0)
 168                size = 1 << fls(size);
 169
 170        /*
 171         * Setup BAR[1] to all DRAM banks.
 172         */
 173        writel(dram->cs[0].base, base + PCIE_BAR_LO_OFF(1));
 174        writel(0, base + PCIE_BAR_HI_OFF(1));
 175        writel(((size - 1) & 0xffff0000) | 1, base + PCIE_BAR_CTRL_OFF(1));
 176}
 177
 178void __init orion_pcie_setup(void __iomem *base,
 179                             struct mbus_dram_target_info *dram)
 180{
 181        u16 cmd;
 182        u32 mask;
 183
 184        /*
 185         * Point PCIe unit MBUS decode windows to DRAM space.
 186         */
 187        orion_pcie_setup_wins(base, dram);
 188
 189        /*
 190         * Master + slave enable.
 191         */
 192        cmd = readw(base + PCIE_CMD_OFF);
 193        cmd |= PCI_COMMAND_IO;
 194        cmd |= PCI_COMMAND_MEMORY;
 195        cmd |= PCI_COMMAND_MASTER;
 196        writew(cmd, base + PCIE_CMD_OFF);
 197
 198        /*
 199         * Enable interrupt lines A-D.
 200         */
 201        mask = readl(base + PCIE_MASK_OFF);
 202        mask |= 0x0f000000;
 203        writel(mask, base + PCIE_MASK_OFF);
 204}
 205
 206int orion_pcie_rd_conf(void __iomem *base, struct pci_bus *bus,
 207                       u32 devfn, int where, int size, u32 *val)
 208{
 209        writel(PCIE_CONF_BUS(bus->number) |
 210                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 211                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 212                PCIE_CONF_REG(where) | PCIE_CONF_ADDR_EN,
 213                        base + PCIE_CONF_ADDR_OFF);
 214
 215        *val = readl(base + PCIE_CONF_DATA_OFF);
 216
 217        if (size == 1)
 218                *val = (*val >> (8 * (where & 3))) & 0xff;
 219        else if (size == 2)
 220                *val = (*val >> (8 * (where & 3))) & 0xffff;
 221
 222        return PCIBIOS_SUCCESSFUL;
 223}
 224
 225int orion_pcie_rd_conf_tlp(void __iomem *base, struct pci_bus *bus,
 226                           u32 devfn, int where, int size, u32 *val)
 227{
 228        writel(PCIE_CONF_BUS(bus->number) |
 229                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 230                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 231                PCIE_CONF_REG(where) | PCIE_CONF_ADDR_EN,
 232                        base + PCIE_CONF_ADDR_OFF);
 233
 234        *val = readl(base + PCIE_CONF_DATA_OFF);
 235
 236        if (bus->number != orion_pcie_get_local_bus_nr(base) ||
 237            PCI_FUNC(devfn) != 0)
 238                *val = readl(base + PCIE_HEADER_LOG_4_OFF);
 239
 240        if (size == 1)
 241                *val = (*val >> (8 * (where & 3))) & 0xff;
 242        else if (size == 2)
 243                *val = (*val >> (8 * (where & 3))) & 0xffff;
 244
 245        return PCIBIOS_SUCCESSFUL;
 246}
 247
 248int orion_pcie_rd_conf_wa(void __iomem *wa_base, struct pci_bus *bus,
 249                          u32 devfn, int where, int size, u32 *val)
 250{
 251        *val = readl(wa_base + (PCIE_CONF_BUS(bus->number) |
 252                                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 253                                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 254                                PCIE_CONF_REG(where)));
 255
 256        if (size == 1)
 257                *val = (*val >> (8 * (where & 3))) & 0xff;
 258        else if (size == 2)
 259                *val = (*val >> (8 * (where & 3))) & 0xffff;
 260
 261        return PCIBIOS_SUCCESSFUL;
 262}
 263
 264int orion_pcie_wr_conf(void __iomem *base, struct pci_bus *bus,
 265                       u32 devfn, int where, int size, u32 val)
 266{
 267        int ret = PCIBIOS_SUCCESSFUL;
 268
 269        writel(PCIE_CONF_BUS(bus->number) |
 270                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 271                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 272                PCIE_CONF_REG(where) | PCIE_CONF_ADDR_EN,
 273                        base + PCIE_CONF_ADDR_OFF);
 274
 275        if (size == 4) {
 276                writel(val, base + PCIE_CONF_DATA_OFF);
 277        } else if (size == 2) {
 278                writew(val, base + PCIE_CONF_DATA_OFF + (where & 3));
 279        } else if (size == 1) {
 280                writeb(val, base + PCIE_CONF_DATA_OFF + (where & 3));
 281        } else {
 282                ret = PCIBIOS_BAD_REGISTER_NUMBER;
 283        }
 284
 285        return ret;
 286}
 287