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
  17/*
  18 * PCIe unit register offsets.
  19 */
  20#define PCIE_DEV_ID_OFF         0x0000
  21#define PCIE_CMD_OFF            0x0004
  22#define PCIE_DEV_REV_OFF        0x0008
  23#define PCIE_BAR_LO_OFF(n)      (0x0010 + ((n) << 3))
  24#define PCIE_BAR_HI_OFF(n)      (0x0014 + ((n) << 3))
  25#define PCIE_HEADER_LOG_4_OFF   0x0128
  26#define PCIE_BAR_CTRL_OFF(n)    (0x1804 + ((n - 1) * 4))
  27#define PCIE_WIN04_CTRL_OFF(n)  (0x1820 + ((n) << 4))
  28#define PCIE_WIN04_BASE_OFF(n)  (0x1824 + ((n) << 4))
  29#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4))
  30#define PCIE_WIN5_CTRL_OFF      0x1880
  31#define PCIE_WIN5_BASE_OFF      0x1884
  32#define PCIE_WIN5_REMAP_OFF     0x188c
  33#define PCIE_CONF_ADDR_OFF      0x18f8
  34#define  PCIE_CONF_ADDR_EN              0x80000000
  35#define  PCIE_CONF_REG(r)               ((((r) & 0xf00) << 16) | ((r) & 0xfc))
  36#define  PCIE_CONF_BUS(b)               (((b) & 0xff) << 16)
  37#define  PCIE_CONF_DEV(d)               (((d) & 0x1f) << 11)
  38#define  PCIE_CONF_FUNC(f)              (((f) & 0x7) << 8)
  39#define PCIE_CONF_DATA_OFF      0x18fc
  40#define PCIE_MASK_OFF           0x1910
  41#define PCIE_CTRL_OFF           0x1a00
  42#define  PCIE_CTRL_X1_MODE              0x0001
  43#define PCIE_STAT_OFF           0x1a04
  44#define  PCIE_STAT_DEV_OFFS             20
  45#define  PCIE_STAT_DEV_MASK             0x1f
  46#define  PCIE_STAT_BUS_OFFS             8
  47#define  PCIE_STAT_BUS_MASK             0xff
  48#define  PCIE_STAT_LINK_DOWN            1
  49
  50
  51u32 __init orion_pcie_dev_id(void __iomem *base)
  52{
  53        return readl(base + PCIE_DEV_ID_OFF) >> 16;
  54}
  55
  56u32 __init orion_pcie_rev(void __iomem *base)
  57{
  58        return readl(base + PCIE_DEV_REV_OFF) & 0xff;
  59}
  60
  61int orion_pcie_link_up(void __iomem *base)
  62{
  63        return !(readl(base + PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
  64}
  65
  66int __init orion_pcie_x4_mode(void __iomem *base)
  67{
  68        return !(readl(base + PCIE_CTRL_OFF) & PCIE_CTRL_X1_MODE);
  69}
  70
  71int orion_pcie_get_local_bus_nr(void __iomem *base)
  72{
  73        u32 stat = readl(base + PCIE_STAT_OFF);
  74
  75        return (stat >> PCIE_STAT_BUS_OFFS) & PCIE_STAT_BUS_MASK;
  76}
  77
  78void __init orion_pcie_set_local_bus_nr(void __iomem *base, int nr)
  79{
  80        u32 stat;
  81
  82        stat = readl(base + PCIE_STAT_OFF);
  83        stat &= ~(PCIE_STAT_BUS_MASK << PCIE_STAT_BUS_OFFS);
  84        stat |= nr << PCIE_STAT_BUS_OFFS;
  85        writel(stat, base + PCIE_STAT_OFF);
  86}
  87
  88/*
  89 * Setup PCIE BARs and Address Decode Wins:
  90 * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks
  91 * WIN[0-3] -> DRAM bank[0-3]
  92 */
  93static void __init orion_pcie_setup_wins(void __iomem *base,
  94                                         struct mbus_dram_target_info *dram)
  95{
  96        u32 size;
  97        int i;
  98
  99        /*
 100         * First, disable and clear BARs and windows.
 101         */
 102        for (i = 1; i <= 2; i++) {
 103                writel(0, base + PCIE_BAR_CTRL_OFF(i));
 104                writel(0, base + PCIE_BAR_LO_OFF(i));
 105                writel(0, base + PCIE_BAR_HI_OFF(i));
 106        }
 107
 108        for (i = 0; i < 5; i++) {
 109                writel(0, base + PCIE_WIN04_CTRL_OFF(i));
 110                writel(0, base + PCIE_WIN04_BASE_OFF(i));
 111                writel(0, base + PCIE_WIN04_REMAP_OFF(i));
 112        }
 113
 114        writel(0, base + PCIE_WIN5_CTRL_OFF);
 115        writel(0, base + PCIE_WIN5_BASE_OFF);
 116        writel(0, base + PCIE_WIN5_REMAP_OFF);
 117
 118        /*
 119         * Setup windows for DDR banks.  Count total DDR size on the fly.
 120         */
 121        size = 0;
 122        for (i = 0; i < dram->num_cs; i++) {
 123                struct mbus_dram_window *cs = dram->cs + i;
 124
 125                writel(cs->base & 0xffff0000, base + PCIE_WIN04_BASE_OFF(i));
 126                writel(0, base + PCIE_WIN04_REMAP_OFF(i));
 127                writel(((cs->size - 1) & 0xffff0000) |
 128                        (cs->mbus_attr << 8) |
 129                        (dram->mbus_dram_target_id << 4) | 1,
 130                                base + PCIE_WIN04_CTRL_OFF(i));
 131
 132                size += cs->size;
 133        }
 134
 135        /*
 136         * Setup BAR[1] to all DRAM banks.
 137         */
 138        writel(dram->cs[0].base, base + PCIE_BAR_LO_OFF(1));
 139        writel(0, base + PCIE_BAR_HI_OFF(1));
 140        writel(((size - 1) & 0xffff0000) | 1, base + PCIE_BAR_CTRL_OFF(1));
 141}
 142
 143void __init orion_pcie_setup(void __iomem *base,
 144                             struct mbus_dram_target_info *dram)
 145{
 146        u16 cmd;
 147        u32 mask;
 148
 149        /*
 150         * Point PCIe unit MBUS decode windows to DRAM space.
 151         */
 152        orion_pcie_setup_wins(base, dram);
 153
 154        /*
 155         * Master + slave enable.
 156         */
 157        cmd = readw(base + PCIE_CMD_OFF);
 158        cmd |= PCI_COMMAND_IO;
 159        cmd |= PCI_COMMAND_MEMORY;
 160        cmd |= PCI_COMMAND_MASTER;
 161        writew(cmd, base + PCIE_CMD_OFF);
 162
 163        /*
 164         * Enable interrupt lines A-D.
 165         */
 166        mask = readl(base + PCIE_MASK_OFF);
 167        mask |= 0x0f000000;
 168        writel(mask, base + PCIE_MASK_OFF);
 169}
 170
 171int orion_pcie_rd_conf(void __iomem *base, struct pci_bus *bus,
 172                       u32 devfn, int where, int size, u32 *val)
 173{
 174        writel(PCIE_CONF_BUS(bus->number) |
 175                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 176                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 177                PCIE_CONF_REG(where) | PCIE_CONF_ADDR_EN,
 178                        base + PCIE_CONF_ADDR_OFF);
 179
 180        *val = readl(base + PCIE_CONF_DATA_OFF);
 181
 182        if (size == 1)
 183                *val = (*val >> (8 * (where & 3))) & 0xff;
 184        else if (size == 2)
 185                *val = (*val >> (8 * (where & 3))) & 0xffff;
 186
 187        return PCIBIOS_SUCCESSFUL;
 188}
 189
 190int orion_pcie_rd_conf_tlp(void __iomem *base, struct pci_bus *bus,
 191                           u32 devfn, int where, int size, u32 *val)
 192{
 193        writel(PCIE_CONF_BUS(bus->number) |
 194                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 195                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 196                PCIE_CONF_REG(where) | PCIE_CONF_ADDR_EN,
 197                        base + PCIE_CONF_ADDR_OFF);
 198
 199        *val = readl(base + PCIE_CONF_DATA_OFF);
 200
 201        if (bus->number != orion_pcie_get_local_bus_nr(base) ||
 202            PCI_FUNC(devfn) != 0)
 203                *val = readl(base + PCIE_HEADER_LOG_4_OFF);
 204
 205        if (size == 1)
 206                *val = (*val >> (8 * (where & 3))) & 0xff;
 207        else if (size == 2)
 208                *val = (*val >> (8 * (where & 3))) & 0xffff;
 209
 210        return PCIBIOS_SUCCESSFUL;
 211}
 212
 213int orion_pcie_rd_conf_wa(void __iomem *wa_base, struct pci_bus *bus,
 214                          u32 devfn, int where, int size, u32 *val)
 215{
 216        *val = readl(wa_base + (PCIE_CONF_BUS(bus->number) |
 217                                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 218                                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 219                                PCIE_CONF_REG(where)));
 220
 221        if (size == 1)
 222                *val = (*val >> (8 * (where & 3))) & 0xff;
 223        else if (size == 2)
 224                *val = (*val >> (8 * (where & 3))) & 0xffff;
 225
 226        return PCIBIOS_SUCCESSFUL;
 227}
 228
 229int orion_pcie_wr_conf(void __iomem *base, struct pci_bus *bus,
 230                       u32 devfn, int where, int size, u32 val)
 231{
 232        int ret = PCIBIOS_SUCCESSFUL;
 233
 234        writel(PCIE_CONF_BUS(bus->number) |
 235                PCIE_CONF_DEV(PCI_SLOT(devfn)) |
 236                PCIE_CONF_FUNC(PCI_FUNC(devfn)) |
 237                PCIE_CONF_REG(where) | PCIE_CONF_ADDR_EN,
 238                        base + PCIE_CONF_ADDR_OFF);
 239
 240        if (size == 4) {
 241                writel(val, base + PCIE_CONF_DATA_OFF);
 242        } else if (size == 2) {
 243                writew(val, base + PCIE_CONF_DATA_OFF + (where & 3));
 244        } else if (size == 1) {
 245                writeb(val, base + PCIE_CONF_DATA_OFF + (where & 3));
 246        } else {
 247                ret = PCIBIOS_BAD_REGISTER_NUMBER;
 248        }
 249
 250        return ret;
 251}
 252
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.