|  | /* | 
|  | * arch/arm/mach-ixp2000/pci.c | 
|  | * | 
|  | * PCI routines for IXDP2400/IXDP2800 boards | 
|  | * | 
|  | * Original Author: Naeem Afzal <naeem.m.afzal@intel.com> | 
|  | * Maintained by: Deepak Saxena <dsaxena@plexity.net> | 
|  | * | 
|  | * Copyright 2002 Intel Corp. | 
|  | * Copyright (C) 2003-2004 MontaVista Software, Inc. | 
|  | * | 
|  | *  This program is free software; you can redistribute  it and/or modify it | 
|  | *  under  the terms of  the GNU General  Public License as published by the | 
|  | *  Free Software Foundation;  either version 2 of the  License, or (at your | 
|  | *  option) any later version. | 
|  | */ | 
|  |  | 
|  | #include <linux/sched.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/pci.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/mm.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/ioport.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/io.h> | 
|  |  | 
|  | #include <asm/irq.h> | 
|  | #include <asm/system.h> | 
|  | #include <mach/hardware.h> | 
|  |  | 
|  | #include <asm/mach/pci.h> | 
|  |  | 
|  | static volatile int pci_master_aborts = 0; | 
|  |  | 
|  | static int clear_master_aborts(void); | 
|  |  | 
|  | u32 * | 
|  | ixp2000_pci_config_addr(unsigned int bus_nr, unsigned int devfn, int where) | 
|  | { | 
|  | u32 *paddress; | 
|  |  | 
|  | if (PCI_SLOT(devfn) > 7) | 
|  | return 0; | 
|  |  | 
|  | /* Must be dword aligned */ | 
|  | where &= ~3; | 
|  |  | 
|  | /* | 
|  | * For top bus, generate type 0, else type 1 | 
|  | */ | 
|  | if (!bus_nr) { | 
|  | /* only bits[23:16] are used for IDSEL */ | 
|  | paddress = (u32 *) (IXP2000_PCI_CFG0_VIRT_BASE | 
|  | | (1 << (PCI_SLOT(devfn) + 16)) | 
|  | | (PCI_FUNC(devfn) << 8) | where); | 
|  | } else { | 
|  | paddress = (u32 *) (IXP2000_PCI_CFG1_VIRT_BASE | 
|  | | (bus_nr << 16) | 
|  | | (PCI_SLOT(devfn) << 11) | 
|  | | (PCI_FUNC(devfn) << 8) | where); | 
|  | } | 
|  |  | 
|  | return paddress; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Mask table, bits to mask for quantity of size 1, 2 or 4 bytes. | 
|  | * 0 and 3 are not valid indexes... | 
|  | */ | 
|  | static u32 bytemask[] = { | 
|  | /*0*/	0, | 
|  | /*1*/	0xff, | 
|  | /*2*/	0xffff, | 
|  | /*3*/	0, | 
|  | /*4*/	0xffffffff, | 
|  | }; | 
|  |  | 
|  |  | 
|  | int ixp2000_pci_read_config(struct pci_bus *bus, unsigned int devfn, int where, | 
|  | int size, u32 *value) | 
|  | { | 
|  | u32 n; | 
|  | u32 *addr; | 
|  |  | 
|  | n = where % 4; | 
|  |  | 
|  | addr = ixp2000_pci_config_addr(bus->number, devfn, where); | 
|  | if (!addr) | 
|  | return PCIBIOS_DEVICE_NOT_FOUND; | 
|  |  | 
|  | pci_master_aborts = 0; | 
|  | *value = (*addr >> (8*n)) & bytemask[size]; | 
|  | if (pci_master_aborts) { | 
|  | pci_master_aborts = 0; | 
|  | *value = 0xffffffff; | 
|  | return PCIBIOS_DEVICE_NOT_FOUND; | 
|  | } | 
|  |  | 
|  | return PCIBIOS_SUCCESSFUL; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * We don't do error checks by calling clear_master_aborts() b/c the | 
|  | * assumption is that the caller did a read first to make sure a device | 
|  | * exists. | 
|  | */ | 
|  | int ixp2000_pci_write_config(struct pci_bus *bus, unsigned int devfn, int where, | 
|  | int size, u32 value) | 
|  | { | 
|  | u32 mask; | 
|  | u32 *addr; | 
|  | u32 temp; | 
|  |  | 
|  | mask = ~(bytemask[size] << ((where % 0x4) * 8)); | 
|  | addr = ixp2000_pci_config_addr(bus->number, devfn, where); | 
|  | if (!addr) | 
|  | return PCIBIOS_DEVICE_NOT_FOUND; | 
|  | temp = (u32) (value) << ((where % 0x4) * 8); | 
|  | *addr = (*addr & mask) | temp; | 
|  |  | 
|  | clear_master_aborts(); | 
|  |  | 
|  | return PCIBIOS_SUCCESSFUL; | 
|  | } | 
|  |  | 
|  |  | 
|  | static struct pci_ops ixp2000_pci_ops = { | 
|  | .read	= ixp2000_pci_read_config, | 
|  | .write	= ixp2000_pci_write_config | 
|  | }; | 
|  |  | 
|  | struct pci_bus *ixp2000_pci_scan_bus(int nr, struct pci_sys_data *sysdata) | 
|  | { | 
|  | return pci_scan_root_bus(NULL, sysdata->busnr, &ixp2000_pci_ops, | 
|  | sysdata, &sysdata->resources); | 
|  | } | 
|  |  | 
|  |  | 
|  | int ixp2000_pci_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | 
|  | { | 
|  |  | 
|  | volatile u32 temp; | 
|  | unsigned long flags; | 
|  |  | 
|  | pci_master_aborts = 1; | 
|  |  | 
|  | local_irq_save(flags); | 
|  | temp = *(IXP2000_PCI_CONTROL); | 
|  | if (temp & ((1 << 8) | (1 << 5))) { | 
|  | ixp2000_reg_wrb(IXP2000_PCI_CONTROL, temp); | 
|  | } | 
|  |  | 
|  | temp = *(IXP2000_PCI_CMDSTAT); | 
|  | if (temp & (1 << 29)) { | 
|  | while (temp & (1 << 29)) { | 
|  | ixp2000_reg_write(IXP2000_PCI_CMDSTAT, temp); | 
|  | temp = *(IXP2000_PCI_CMDSTAT); | 
|  | } | 
|  | } | 
|  | local_irq_restore(flags); | 
|  |  | 
|  | /* | 
|  | * If it was an imprecise abort, then we need to correct the | 
|  | * return address to be _after_ the instruction. | 
|  | */ | 
|  | if (fsr & (1 << 10)) | 
|  | regs->ARM_pc += 4; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | clear_master_aborts(void) | 
|  | { | 
|  | volatile u32 temp; | 
|  | unsigned long flags; | 
|  |  | 
|  | local_irq_save(flags); | 
|  | temp = *(IXP2000_PCI_CONTROL); | 
|  | if (temp & ((1 << 8) | (1 << 5))) { | 
|  | ixp2000_reg_wrb(IXP2000_PCI_CONTROL, temp); | 
|  | } | 
|  |  | 
|  | temp = *(IXP2000_PCI_CMDSTAT); | 
|  | if (temp & (1 << 29)) { | 
|  | while (temp & (1 << 29)) { | 
|  | ixp2000_reg_write(IXP2000_PCI_CMDSTAT, temp); | 
|  | temp = *(IXP2000_PCI_CMDSTAT); | 
|  | } | 
|  | } | 
|  | local_irq_restore(flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void __init | 
|  | ixp2000_pci_preinit(void) | 
|  | { | 
|  | pci_set_flags(0); | 
|  |  | 
|  | pcibios_min_io = 0; | 
|  | pcibios_min_mem = 0; | 
|  |  | 
|  | #ifndef CONFIG_IXP2000_SUPPORT_BROKEN_PCI_IO | 
|  | /* | 
|  | * Configure the PCI unit to properly byteswap I/O transactions, | 
|  | * and verify that it worked. | 
|  | */ | 
|  | ixp2000_reg_write(IXP2000_PCI_CONTROL, | 
|  | (*IXP2000_PCI_CONTROL | PCI_CONTROL_IEE)); | 
|  |  | 
|  | if ((*IXP2000_PCI_CONTROL & PCI_CONTROL_IEE) == 0) | 
|  | panic("IXP2000: PCI I/O is broken on this ixp model, and " | 
|  | "the needed workaround has not been configured in"); | 
|  | #endif | 
|  |  | 
|  | hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS, 0, | 
|  | "PCI config cycle to non-existent device"); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * IXP2000 systems often have large resource requirements, so we just | 
|  | * use our own resource space. | 
|  | */ | 
|  | static struct resource ixp2000_pci_mem_space = { | 
|  | .start	= 0xe0000000, | 
|  | .end	= 0xffffffff, | 
|  | .flags	= IORESOURCE_MEM, | 
|  | .name	= "PCI Mem Space" | 
|  | }; | 
|  |  | 
|  | static struct resource ixp2000_pci_io_space = { | 
|  | .start	= 0x00010000, | 
|  | .end	= 0x0001ffff, | 
|  | .flags	= IORESOURCE_IO, | 
|  | .name	= "PCI I/O Space" | 
|  | }; | 
|  |  | 
|  | int ixp2000_pci_setup(int nr, struct pci_sys_data *sys) | 
|  | { | 
|  | if (nr >= 1) | 
|  | return 0; | 
|  |  | 
|  | pci_add_resource(&sys->resources, &ixp2000_pci_io_space); | 
|  | pci_add_resource(&sys->resources, &ixp2000_pci_mem_space); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  |