diff options
Diffstat (limited to 'kernel/arch/mips/pci/ops-tx3927.c')
-rw-r--r-- | kernel/arch/mips/pci/ops-tx3927.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/kernel/arch/mips/pci/ops-tx3927.c b/kernel/arch/mips/pci/ops-tx3927.c new file mode 100644 index 000000000..d35dc9c9a --- /dev/null +++ b/kernel/arch/mips/pci/ops-tx3927.c @@ -0,0 +1,231 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ahennessy@mvista.com + * + * Copyright (C) 2000-2001 Toshiba Corporation + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * Based on arch/mips/ddb5xxx/ddb5477/pci_ops.c + * + * Define the pci_ops for TX3927. + * + * Much of the code is derived from the original DDB5074 port by + * Geert Uytterhoeven <geert@linux-m68k.org> + * + * 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. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +#include <asm/addrspace.h> +#include <asm/txx9irq.h> +#include <asm/txx9/pci.h> +#include <asm/txx9/tx3927.h> + +static int mkaddr(struct pci_bus *bus, unsigned char devfn, unsigned char where) +{ + if (bus->parent == NULL && + devfn >= PCI_DEVFN(TX3927_PCIC_MAX_DEVNU, 0)) + return -1; + tx3927_pcicptr->ica = + ((bus->number & 0xff) << 0x10) | + ((devfn & 0xff) << 0x08) | + (where & 0xfc) | (bus->parent ? 1 : 0); + + /* clear M_ABORT and Disable M_ABORT Int. */ + tx3927_pcicptr->pcistat |= PCI_STATUS_REC_MASTER_ABORT; + tx3927_pcicptr->pcistatim &= ~PCI_STATUS_REC_MASTER_ABORT; + return 0; +} + +static inline int check_abort(void) +{ + if (tx3927_pcicptr->pcistat & PCI_STATUS_REC_MASTER_ABORT) { + tx3927_pcicptr->pcistat |= PCI_STATUS_REC_MASTER_ABORT; + tx3927_pcicptr->pcistatim |= PCI_STATUS_REC_MASTER_ABORT; + /* flush write buffer */ + iob(); + return PCIBIOS_DEVICE_NOT_FOUND; + } + return PCIBIOS_SUCCESSFUL; +} + +static int tx3927_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + if (mkaddr(bus, devfn, where)) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + switch (size) { + case 1: + *val = *(volatile u8 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 3)); + break; + + case 2: + *val = le16_to_cpu(*(volatile u16 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 3))); + break; + + case 4: + *val = le32_to_cpu(tx3927_pcicptr->icd); + break; + } + + return check_abort(); +} + +static int tx3927_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + if (mkaddr(bus, devfn, where)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *(volatile u8 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 3)) = val; + break; + + case 2: + *(volatile u16 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 2)) = + cpu_to_le16(val); + break; + + case 4: + tx3927_pcicptr->icd = cpu_to_le32(val); + } + + return check_abort(); +} + +static struct pci_ops tx3927_pci_ops = { + .read = tx3927_pci_read_config, + .write = tx3927_pci_write_config, +}; + +void __init tx3927_pcic_setup(struct pci_controller *channel, + unsigned long sdram_size, int extarb) +{ + unsigned long flags; + unsigned long io_base = + channel->io_resource->start + mips_io_port_base - IO_BASE; + unsigned long io_size = + channel->io_resource->end - channel->io_resource->start; + unsigned long io_pciaddr = + channel->io_resource->start - channel->io_offset; + unsigned long mem_base = + channel->mem_resource->start; + unsigned long mem_size = + channel->mem_resource->end - channel->mem_resource->start; + unsigned long mem_pciaddr = + channel->mem_resource->start - channel->mem_offset; + + printk(KERN_INFO "TX3927 PCIC -- DID:%04x VID:%04x RID:%02x Arbiter:%s", + tx3927_pcicptr->did, tx3927_pcicptr->vid, + tx3927_pcicptr->rid, + extarb ? "External" : "Internal"); + channel->pci_ops = &tx3927_pci_ops; + + local_irq_save(flags); + /* Disable External PCI Config. Access */ + tx3927_pcicptr->lbc = TX3927_PCIC_LBC_EPCAD; +#ifdef __BIG_ENDIAN + tx3927_pcicptr->lbc |= TX3927_PCIC_LBC_IBSE | + TX3927_PCIC_LBC_TIBSE | + TX3927_PCIC_LBC_TMFBSE | TX3927_PCIC_LBC_MSDSE; +#endif + /* LB->PCI mappings */ + tx3927_pcicptr->iomas = ~(io_size - 1); + tx3927_pcicptr->ilbioma = io_base; + tx3927_pcicptr->ipbioma = io_pciaddr; + tx3927_pcicptr->mmas = ~(mem_size - 1); + tx3927_pcicptr->ilbmma = mem_base; + tx3927_pcicptr->ipbmma = mem_pciaddr; + /* PCI->LB mappings */ + tx3927_pcicptr->iobas = 0xffffffff; + tx3927_pcicptr->ioba = 0; + tx3927_pcicptr->tlbioma = 0; + tx3927_pcicptr->mbas = ~(sdram_size - 1); + tx3927_pcicptr->mba = 0; + tx3927_pcicptr->tlbmma = 0; + /* Enable Direct mapping Address Space Decoder */ + tx3927_pcicptr->lbc |= TX3927_PCIC_LBC_ILMDE | TX3927_PCIC_LBC_ILIDE; + + /* Clear All Local Bus Status */ + tx3927_pcicptr->lbstat = TX3927_PCIC_LBIM_ALL; + /* Enable All Local Bus Interrupts */ + tx3927_pcicptr->lbim = TX3927_PCIC_LBIM_ALL; + /* Clear All PCI Status Error */ + tx3927_pcicptr->pcistat = TX3927_PCIC_PCISTATIM_ALL; + /* Enable All PCI Status Error Interrupts */ + tx3927_pcicptr->pcistatim = TX3927_PCIC_PCISTATIM_ALL; + + /* PCIC Int => IRC IRQ10 */ + tx3927_pcicptr->il = TX3927_IR_PCI; + /* Target Control (per errata) */ + tx3927_pcicptr->tc = TX3927_PCIC_TC_OF8E | TX3927_PCIC_TC_IF8E; + + /* Enable Bus Arbiter */ + if (!extarb) + tx3927_pcicptr->pbapmc = TX3927_PCIC_PBAPMC_PBAEN; + + tx3927_pcicptr->pcicmd = PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY | + PCI_COMMAND_IO | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + local_irq_restore(flags); +} + +static irqreturn_t tx3927_pcierr_interrupt(int irq, void *dev_id) +{ + struct pt_regs *regs = get_irq_regs(); + + if (txx9_pci_err_action != TXX9_PCI_ERR_IGNORE) { + printk(KERN_WARNING "PCI error interrupt at 0x%08lx.\n", + regs->cp0_epc); + printk(KERN_WARNING "pcistat:%02x, lbstat:%04lx\n", + tx3927_pcicptr->pcistat, tx3927_pcicptr->lbstat); + } + if (txx9_pci_err_action != TXX9_PCI_ERR_PANIC) { + /* clear all pci errors */ + tx3927_pcicptr->pcistat |= TX3927_PCIC_PCISTATIM_ALL; + tx3927_pcicptr->istat = TX3927_PCIC_IIM_ALL; + tx3927_pcicptr->tstat = TX3927_PCIC_TIM_ALL; + tx3927_pcicptr->lbstat = TX3927_PCIC_LBIM_ALL; + return IRQ_HANDLED; + } + console_verbose(); + panic("PCI error."); +} + +void __init tx3927_setup_pcierr_irq(void) +{ + if (request_irq(TXX9_IRQ_BASE + TX3927_IR_PCI, + tx3927_pcierr_interrupt, + 0, "PCI error", + (void *)TX3927_PCIC_REG)) + printk(KERN_WARNING "Failed to request irq for PCIERR\n"); +} |