1/* 2 * arch/arm/plat-iop/pci.c 3 * 4 * PCI support for the Intel IOP32X and IOP33X processors 5 * 6 * Author: Rory Bolt <rorybolt@pacbell.net> 7 * Copyright (C) 2002 Rory Bolt 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14#include <linux/kernel.h> 15#include <linux/pci.h> 16#include <linux/slab.h> 17#include <linux/mm.h> 18#include <linux/init.h> 19#include <linux/ioport.h> 20#include <linux/io.h> 21#include <asm/irq.h> 22#include <asm/signal.h> 23#include <mach/hardware.h> 24#include <asm/mach/pci.h> 25#include <asm/hardware/iop3xx.h> 26 27// #define DEBUG 28 29#ifdef DEBUG 30#define DBG(x...) printk(x) 31#else 32#define DBG(x...) do { } while (0) 33#endif 34 35/* 36 * This routine builds either a type0 or type1 configuration command. If the 37 * bus is on the 803xx then a type0 made, else a type1 is created. 38 */ 39static u32 iop3xx_cfg_address(struct pci_bus *bus, int devfn, int where) 40{ 41 struct pci_sys_data *sys = bus->sysdata; 42 u32 addr; 43 44 if (sys->busnr == bus->number) 45 addr = 1 << (PCI_SLOT(devfn) + 16) | (PCI_SLOT(devfn) << 11); 46 else 47 addr = bus->number << 16 | PCI_SLOT(devfn) << 11 | 1; 48 49 addr |= PCI_FUNC(devfn) << 8 | (where & ~3); 50 51 return addr; 52} 53 54/* 55 * This routine checks the status of the last configuration cycle. If an error 56 * was detected it returns a 1, else it returns a 0. The errors being checked 57 * are parity, master abort, target abort (master and target). These types of 58 * errors occur during a config cycle where there is no device, like during 59 * the discovery stage. 60 */ 61static int iop3xx_pci_status(void) 62{ 63 unsigned int status; 64 int ret = 0; 65 66 /* 67 * Check the status registers. 68 */ 69 status = *IOP3XX_ATUSR; 70 if (status & 0xf900) { 71 DBG("\t\t\tPCI: P0 - status = 0x%08x\n", status); 72 *IOP3XX_ATUSR = status & 0xf900; 73 ret = 1; 74 } 75 76 status = *IOP3XX_ATUISR; 77 if (status & 0x679f) { 78 DBG("\t\t\tPCI: P1 - status = 0x%08x\n", status); 79 *IOP3XX_ATUISR = status & 0x679f; 80 ret = 1; 81 } 82 83 return ret; 84} 85 86/* 87 * Simply write the address register and read the configuration 88 * data. Note that the 4 nops ensure that we are able to handle 89 * a delayed abort (in theory.) 90 */ 91static u32 iop3xx_read(unsigned long addr) 92{ 93 u32 val; 94 95 __asm__ __volatile__( 96 "str %1, [%2]\n\t" 97 "ldr %0, [%3]\n\t" 98 "nop\n\t" 99 "nop\n\t" 100 "nop\n\t" 101 "nop\n\t" 102 : "=r" (val) 103 : "r" (addr), "r" (IOP3XX_OCCAR), "r" (IOP3XX_OCCDR)); 104 105 return val; 106} 107 108/* 109 * The read routines must check the error status of the last configuration 110 * cycle. If there was an error, the routine returns all hex f's. 111 */ 112static int 113iop3xx_read_config(struct pci_bus *bus, unsigned int devfn, int where, 114 int size, u32 *value) 115{ 116 unsigned long addr = iop3xx_cfg_address(bus, devfn, where); 117 u32 val = iop3xx_read(addr) >> ((where & 3) * 8); 118 119 if (iop3xx_pci_status()) 120 val = 0xffffffff; 121 122 *value = val; 123 124 return PCIBIOS_SUCCESSFUL; 125} 126 127static int 128iop3xx_write_config(struct pci_bus *bus, unsigned int devfn, int where, 129 int size, u32 value) 130{ 131 unsigned long addr = iop3xx_cfg_address(bus, devfn, where); 132 u32 val; 133 134 if (size != 4) { 135 val = iop3xx_read(addr); 136 if (iop3xx_pci_status()) 137 return PCIBIOS_SUCCESSFUL; 138 139 where = (where & 3) * 8; 140 141 if (size == 1) 142 val &= ~(0xff << where); 143 else 144 val &= ~(0xffff << where); 145 146 *IOP3XX_OCCDR = val | value << where; 147 } else { 148 asm volatile( 149 "str %1, [%2]\n\t" 150 "str %0, [%3]\n\t" 151 "nop\n\t" 152 "nop\n\t" 153 "nop\n\t" 154 "nop\n\t" 155 : 156 : "r" (value), "r" (addr), 157 "r" (IOP3XX_OCCAR), "r" (IOP3XX_OCCDR)); 158 } 159 160 return PCIBIOS_SUCCESSFUL; 161} 162 163struct pci_ops iop3xx_ops = { 164 .read = iop3xx_read_config, 165 .write = iop3xx_write_config, 166}; 167 168/* 169 * When a PCI device does not exist during config cycles, the 80200 gets a 170 * bus error instead of returning 0xffffffff. This handler simply returns. 171 */ 172static int 173iop3xx_pci_abort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) 174{ 175 DBG("PCI abort: address = 0x%08lx fsr = 0x%03x PC = 0x%08lx LR = 0x%08lx\n", 176 addr, fsr, regs->ARM_pc, regs->ARM_lr); 177 178 /* 179 * If it was an imprecise abort, then we need to correct the 180 * return address to be _after_ the instruction. 181 */ 182 if (fsr & (1 << 10)) 183 regs->ARM_pc += 4; 184 185 return 0; 186} 187 188int iop3xx_pci_setup(int nr, struct pci_sys_data *sys) 189{ 190 struct resource *res; 191 192 if (nr != 0) 193 return 0; 194 195 res = kzalloc(sizeof(struct resource), GFP_KERNEL); 196 if (!res) 197 panic("PCI: unable to alloc resources"); 198 199 res->start = IOP3XX_PCI_LOWER_MEM_PA; 200 res->end = IOP3XX_PCI_LOWER_MEM_PA + IOP3XX_PCI_MEM_WINDOW_SIZE - 1; 201 res->name = "IOP3XX PCI Memory Space"; 202 res->flags = IORESOURCE_MEM; 203 request_resource(&iomem_resource, res); 204 205 /* 206 * Use whatever translation is already setup. 207 */ 208 sys->mem_offset = IOP3XX_PCI_LOWER_MEM_PA - *IOP3XX_OMWTVR0; 209 210 pci_add_resource_offset(&sys->resources, res, sys->mem_offset); 211 212 pci_ioremap_io(0, IOP3XX_PCI_LOWER_IO_PA); 213 214 return 1; 215} 216 217void __init iop3xx_atu_setup(void) 218{ 219 /* BAR 0 ( Disabled ) */ 220 *IOP3XX_IAUBAR0 = 0x0; 221 *IOP3XX_IABAR0 = 0x0; 222 *IOP3XX_IATVR0 = 0x0; 223 *IOP3XX_IALR0 = 0x0; 224 225 /* BAR 1 ( Disabled ) */ 226 *IOP3XX_IAUBAR1 = 0x0; 227 *IOP3XX_IABAR1 = 0x0; 228 *IOP3XX_IALR1 = 0x0; 229 230 /* BAR 2 (1:1 mapping with Physical RAM) */ 231 /* Set limit and enable */ 232 *IOP3XX_IALR2 = ~((u32)IOP3XX_MAX_RAM_SIZE - 1) & ~0x1; 233 *IOP3XX_IAUBAR2 = 0x0; 234 235 /* Align the inbound bar with the base of memory */ 236 *IOP3XX_IABAR2 = PHYS_OFFSET | 237 PCI_BASE_ADDRESS_MEM_TYPE_64 | 238 PCI_BASE_ADDRESS_MEM_PREFETCH; 239 240 *IOP3XX_IATVR2 = PHYS_OFFSET; 241 242 /* Outbound window 0 */ 243 *IOP3XX_OMWTVR0 = IOP3XX_PCI_LOWER_MEM_BA; 244 *IOP3XX_OUMWTVR0 = 0; 245 246 /* Outbound window 1 */ 247 *IOP3XX_OMWTVR1 = IOP3XX_PCI_LOWER_MEM_BA + 248 IOP3XX_PCI_MEM_WINDOW_SIZE / 2; 249 *IOP3XX_OUMWTVR1 = 0; 250 251 /* BAR 3 ( Disabled ) */ 252 *IOP3XX_IAUBAR3 = 0x0; 253 *IOP3XX_IABAR3 = 0x0; 254 *IOP3XX_IATVR3 = 0x0; 255 *IOP3XX_IALR3 = 0x0; 256 257 /* Setup the I/O Bar 258 */ 259 *IOP3XX_OIOWTVR = IOP3XX_PCI_LOWER_IO_BA; 260 261 /* Enable inbound and outbound cycles 262 */ 263 *IOP3XX_ATUCMD |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | 264 PCI_COMMAND_PARITY | PCI_COMMAND_SERR; 265 *IOP3XX_ATUCR |= IOP3XX_ATUCR_OUT_EN; 266} 267 268void __init iop3xx_atu_disable(void) 269{ 270 *IOP3XX_ATUCMD = 0; 271 *IOP3XX_ATUCR = 0; 272 273 /* wait for cycles to quiesce */ 274 while (*IOP3XX_PCSR & (IOP3XX_PCSR_OUT_Q_BUSY | 275 IOP3XX_PCSR_IN_Q_BUSY)) 276 cpu_relax(); 277 278 /* BAR 0 ( Disabled ) */ 279 *IOP3XX_IAUBAR0 = 0x0; 280 *IOP3XX_IABAR0 = 0x0; 281 *IOP3XX_IATVR0 = 0x0; 282 *IOP3XX_IALR0 = 0x0; 283 284 /* BAR 1 ( Disabled ) */ 285 *IOP3XX_IAUBAR1 = 0x0; 286 *IOP3XX_IABAR1 = 0x0; 287 *IOP3XX_IALR1 = 0x0; 288 289 /* BAR 2 ( Disabled ) */ 290 *IOP3XX_IAUBAR2 = 0x0; 291 *IOP3XX_IABAR2 = 0x0; 292 *IOP3XX_IATVR2 = 0x0; 293 *IOP3XX_IALR2 = 0x0; 294 295 /* BAR 3 ( Disabled ) */ 296 *IOP3XX_IAUBAR3 = 0x0; 297 *IOP3XX_IABAR3 = 0x0; 298 *IOP3XX_IATVR3 = 0x0; 299 *IOP3XX_IALR3 = 0x0; 300 301 /* Clear the outbound windows */ 302 *IOP3XX_OIOWTVR = 0; 303 304 /* Outbound window 0 */ 305 *IOP3XX_OMWTVR0 = 0; 306 *IOP3XX_OUMWTVR0 = 0; 307 308 /* Outbound window 1 */ 309 *IOP3XX_OMWTVR1 = 0; 310 *IOP3XX_OUMWTVR1 = 0; 311} 312 313/* Flag to determine whether the ATU is initialized and the PCI bus scanned */ 314int init_atu; 315 316int iop3xx_get_init_atu(void) { 317 /* check if default has been overridden */ 318 if (init_atu != IOP3XX_INIT_ATU_DEFAULT) 319 return init_atu; 320 else 321 return IOP3XX_INIT_ATU_DISABLE; 322} 323 324static void __init iop3xx_atu_debug(void) 325{ 326 DBG("PCI: Intel IOP3xx PCI init.\n"); 327 DBG("PCI: Outbound memory window 0: PCI 0x%08x%08x\n", 328 *IOP3XX_OUMWTVR0, *IOP3XX_OMWTVR0); 329 DBG("PCI: Outbound memory window 1: PCI 0x%08x%08x\n", 330 *IOP3XX_OUMWTVR1, *IOP3XX_OMWTVR1); 331 DBG("PCI: Outbound IO window: PCI 0x%08x\n", 332 *IOP3XX_OIOWTVR); 333 334 DBG("PCI: Inbound memory window 0: PCI 0x%08x%08x 0x%08x -> 0x%08x\n", 335 *IOP3XX_IAUBAR0, *IOP3XX_IABAR0, *IOP3XX_IALR0, *IOP3XX_IATVR0); 336 DBG("PCI: Inbound memory window 1: PCI 0x%08x%08x 0x%08x\n", 337 *IOP3XX_IAUBAR1, *IOP3XX_IABAR1, *IOP3XX_IALR1); 338 DBG("PCI: Inbound memory window 2: PCI 0x%08x%08x 0x%08x -> 0x%08x\n", 339 *IOP3XX_IAUBAR2, *IOP3XX_IABAR2, *IOP3XX_IALR2, *IOP3XX_IATVR2); 340 DBG("PCI: Inbound memory window 3: PCI 0x%08x%08x 0x%08x -> 0x%08x\n", 341 *IOP3XX_IAUBAR3, *IOP3XX_IABAR3, *IOP3XX_IALR3, *IOP3XX_IATVR3); 342 343 DBG("PCI: Expansion ROM window: PCI 0x%08x%08x 0x%08x -> 0x%08x\n", 344 0, *IOP3XX_ERBAR, *IOP3XX_ERLR, *IOP3XX_ERTVR); 345 346 DBG("ATU: IOP3XX_ATUCMD=0x%04x\n", *IOP3XX_ATUCMD); 347 DBG("ATU: IOP3XX_ATUCR=0x%08x\n", *IOP3XX_ATUCR); 348 349 hook_fault_code(16+6, iop3xx_pci_abort, SIGBUS, 0, "imprecise external abort"); 350} 351 352/* for platforms that might be host-bus-adapters */ 353void __init iop3xx_pci_preinit_cond(void) 354{ 355 if (iop3xx_get_init_atu() == IOP3XX_INIT_ATU_ENABLE) { 356 iop3xx_atu_disable(); 357 iop3xx_atu_setup(); 358 iop3xx_atu_debug(); 359 } 360} 361 362void __init iop3xx_pci_preinit(void) 363{ 364 pcibios_min_mem = 0; 365 366 iop3xx_atu_disable(); 367 iop3xx_atu_setup(); 368 iop3xx_atu_debug(); 369} 370 371/* allow init_atu to be user overridden */ 372static int __init iop3xx_init_atu_setup(char *str) 373{ 374 init_atu = IOP3XX_INIT_ATU_DEFAULT; 375 if (str) { 376 while (*str != '\0') { 377 switch (*str) { 378 case 'y': 379 case 'Y': 380 init_atu = IOP3XX_INIT_ATU_ENABLE; 381 break; 382 case 'n': 383 case 'N': 384 init_atu = IOP3XX_INIT_ATU_DISABLE; 385 break; 386 case ',': 387 case '=': 388 break; 389 default: 390 printk(KERN_DEBUG "\"%s\" malformed at " 391 "character: \'%c\'", 392 __func__, 393 *str); 394 *(str + 1) = '\0'; 395 } 396 str++; 397 } 398 } 399 400 return 1; 401} 402 403__setup("iop3xx_init_atu", iop3xx_init_atu_setup); 404 405