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