/* * arch/arm/mach-iop310/iop310-pci.c * * PCI support for the Intel IOP310 chipset * * Matt Porter <mporter@mvista.com> * * Copyright (C) 2001 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 version 2 as * published by the Free Software Foundation. */ #include <linux/sched.h> #include <linux/kernel.h> #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/ioport.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/hardware.h> #include <asm/mach/pci.h> #include <asm/arch/iop310.h> /* * *** Special note - why the IOP310 should NOT be used *** * * The PCI ATU is a brain dead implementation, only allowing 32-bit * accesses to PCI configuration space. This is especially brain * dead for writes to this space. A simple for-instance: * * You want to modify the command register *without* corrupting the * status register. * * To perform this, you need to read *32* bits of data from offset 4, * mask off the low 16, replace them with the new data, and write *32* * bits back. * * Writing the status register at offset 6 with status bits set *clears* * the status. * * Hello? Could we have a *SANE* implementation of a PCI ATU some day * *PLEASE*? */ #undef DEBUG #ifdef DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) do { } while (0) #endif extern int (*external_fault)(unsigned long, struct pt_regs *); static u32 iop310_cfg_address(struct pci_dev *dev, int where) { struct pci_sys_data *sys = dev->sysdata; u32 addr; where &= ~3; if (sys->busnr == dev->bus->number) addr = 1 << (PCI_SLOT(dev->devfn) + 16); else addr = dev->bus->number << 16 | PCI_SLOT(dev->devfn) << 11 | 1; addr |= PCI_FUNC(dev->devfn) << 8 | where; return addr; } /* * Primary PCI interface support. */ static int iop310_pri_pci_status(void) { unsigned int status; int ret = 0; status = *IOP310_PATUSR; if (status & 0xf900) { *IOP310_PATUSR = status & 0xf900; ret = 1; } status = *IOP310_PATUISR; if (status & 0x0000018f) { *IOP310_PATUISR = status & 0x0000018f; ret = 1; } status = *IOP310_PSR; if (status & 0xf900) { *IOP310_PSR = status & 0xf900; ret = 1; } status = *IOP310_PBISR; if (status & 0x003f) { *IOP310_PBISR = status & 0x003f; ret = 1; } return ret; } static int iop310_pri_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p) { int ret; u8 val; *IOP310_POCCAR = iop310_cfg_address(dev, where); val = (*IOP310_POCCDR) >> ((where & 3) * 8); __asm__ __volatile__("nop; nop; nop; nop"); ret = iop310_pri_pci_status(); if (ret) val = 0xff; *p = val; return PCIBIOS_SUCCESSFUL; } static int iop310_pri_rd_cfg_word(struct pci_dev *dev, int where, u16 *p) { int ret; u16 val; *IOP310_POCCAR = iop310_cfg_address(dev, where); val = (*IOP310_POCCDR) >> ((where & 2) * 8); __asm__ __volatile__("nop; nop; nop; nop"); ret = iop310_pri_pci_status(); if (ret) val = 0xffff; *p = val; return PCIBIOS_SUCCESSFUL; } static int iop310_pri_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p) { int ret; u32 val; *IOP310_POCCAR = iop310_cfg_address(dev, where); val = *IOP310_POCCDR; __asm__ __volatile__("nop; nop; nop; nop"); ret = iop310_pri_pci_status(); if (ret) val = 0xffffffff; *p = val; return PCIBIOS_SUCCESSFUL; } static int iop310_pri_wr_cfg_byte(struct pci_dev *dev, int where, u8 v) { int ret; u32 val; *IOP310_POCCAR = iop310_cfg_address(dev, where); val = *IOP310_POCCDR; __asm__ __volatile__("nop; nop; nop; nop"); ret = iop310_pri_pci_status(); if (ret == 0) { where = (where & 3) * 8; val &= ~(0xff << where); val |= v << where; *IOP310_POCCDR = val; } return PCIBIOS_SUCCESSFUL; } static int iop310_pri_wr_cfg_word(struct pci_dev *dev, int where, u16 v) { int ret; u32 val; *IOP310_POCCAR = iop310_cfg_address(dev, where); val = *IOP310_POCCDR; __asm__ __volatile__("nop; nop; nop; nop"); ret = iop310_pri_pci_status(); if (ret == 0) { where = (where & 2) * 8; val &= ~(0xffff << where); val |= v << where; *IOP310_POCCDR = val; } return PCIBIOS_SUCCESSFUL; } static int iop310_pri_wr_cfg_dword(struct pci_dev *dev, int where, u32 v) { *IOP310_POCCAR = iop310_cfg_address(dev, where); *IOP310_POCCDR = v; __asm__ __volatile__("nop; nop; nop; nop"); return PCIBIOS_SUCCESSFUL; } static struct pci_ops iop310_primary_ops = { iop310_pri_rd_cfg_byte, iop310_pri_rd_cfg_word, iop310_pri_rd_cfg_dword, iop310_pri_wr_cfg_byte, iop310_pri_wr_cfg_word, iop310_pri_wr_cfg_dword, }; /* * Secondary PCI interface support. */ static int iop310_sec_pci_status(void) { unsigned int usr, uisr; int ret = 0; usr = *IOP310_SATUSR; uisr = *IOP310_SATUISR; if (usr & 0xf900) { *IOP310_SATUSR = usr & 0xf900; ret = 1; } if (uisr & 0x0000069f) { *IOP310_SATUISR = uisr & 0x0000069f; ret = 1; } if (ret) DBG("ERROR (%08lx %08lx)", usr, uisr); return ret; } static int iop310_sec_rd_cfg_byte(struct pci_dev *dev, int where, u8 *p) { int ret; u8 val; DBG("rdb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = (*IOP310_SOCCDR) >> ((where & 3) * 8); __asm__ __volatile__("nop; nop; nop; nop"); DBG(">= %08lx ", val); ret = iop310_sec_pci_status(); if (ret) val = 0xff; DBG("\n"); *p = val; return PCIBIOS_SUCCESSFUL; } static int iop310_sec_rd_cfg_word(struct pci_dev *dev, int where, u16 *p) { int ret; u16 val; DBG("rdw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = (*IOP310_SOCCDR) >> ((where & 3) * 8); __asm__ __volatile__("nop; nop; nop; nop"); DBG(">= %08lx ", val); ret = iop310_sec_pci_status(); if (ret) val = 0xffff; DBG("\n"); *p = val; return PCIBIOS_SUCCESSFUL; } static int iop310_sec_rd_cfg_dword(struct pci_dev *dev, int where, u32 *p) { int ret; u32 val; DBG("rdl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = *IOP310_SOCCDR; __asm__ __volatile__("nop; nop; nop; nop"); DBG(">= %08lx ", val); ret = iop310_sec_pci_status(); if (ret) val = 0xffffffff; DBG("\n"); *p = val; return PCIBIOS_SUCCESSFUL; } static int iop310_sec_wr_cfg_byte(struct pci_dev *dev, int where, u8 v) { int ret; u32 val; DBG("wrb: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = *IOP310_SOCCDR; __asm__ __volatile__("nop; nop; nop; nop"); DBG("<= %08lx", v); ret = iop310_sec_pci_status(); if (ret == 0) { where = (where & 3) * 8; val &= ~(0xff << where); val |= v << where; *IOP310_SOCCDR = val; } DBG("\n"); return PCIBIOS_SUCCESSFUL; } static int iop310_sec_wr_cfg_word(struct pci_dev *dev, int where, u16 v) { int ret; u32 val; DBG("wrw: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); val = *IOP310_SOCCDR; __asm__ __volatile__("nop; nop; nop; nop"); DBG("<= %08lx", v); ret = iop310_sec_pci_status(); if (ret == 0) { where = (where & 2) * 8; val &= ~(0xffff << where); val |= v << where; *IOP310_SOCCDR = val; } DBG("\n"); return PCIBIOS_SUCCESSFUL; } static int iop310_sec_wr_cfg_dword(struct pci_dev *dev, int where, u32 v) { DBG("wrl: %d:%02x.%x %02x ", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), where); *IOP310_SOCCAR = iop310_cfg_address(dev, where); *IOP310_SOCCDR = v; __asm__ __volatile__("nop; nop; nop; nop"); DBG("<= %08lx\n", v); return PCIBIOS_SUCCESSFUL; } static struct pci_ops iop310_secondary_ops = { iop310_sec_rd_cfg_byte, iop310_sec_rd_cfg_word, iop310_sec_rd_cfg_dword, iop310_sec_wr_cfg_byte, iop310_sec_wr_cfg_word, iop310_sec_wr_cfg_dword, }; /* * When a PCI device does not exist during config cycles, the 80200 gets a * bus error instead of returning 0xffffffff. This handler simply returns. */ int iop310_pci_abort_handler(unsigned long addr, struct pt_regs *regs) { DBG("PCI abort: address = %08x PC = %08x LR = %08lx\n", addr, regs->ARM_pc, regs->ARM_lr); return 0; } /* * Scan an IOP310 PCI bus. sys->bus defines which bus we scan. */ struct pci_bus *iop310_scan_bus(int nr, struct pci_sys_data *sys) { struct pci_ops *ops; if (nr) ops = &iop310_secondary_ops; else ops = &iop310_primary_ops; return pci_scan_bus(sys->busnr, ops, sys); } /* * Setup the system data for controller 'nr'. Return 0 if none found, * 1 if found, or negative error. * * We can alter: * io_offset - offset between IO resources and PCI bus BARs * mem_offset - offset between mem resources and PCI bus BARs * resource[0] - parent IO resource * resource[1] - parent non-prefetchable memory resource * resource[2] - parent prefetchable memory resource * swizzle - bridge swizzling function * map_irq - irq mapping function * * Note that 'io_offset' and 'mem_offset' are left as zero since * the IOP310 doesn't attempt to perform any address translation * on accesses from the host to the bus. */ int iop310_setup(int nr, struct pci_sys_data *sys) { struct resource *res; if (nr >= 2) return 0; res = kmalloc(sizeof(struct resource) * 2, GFP_KERNEL); if (!res) panic("PCI: unable to alloc resources"); switch (nr) { case 0: res[0].start = IOP310_PCIPRI_LOWER_IO + 0x6e000000; res[0].end = IOP310_PCIPRI_LOWER_IO + 0x6e00ffff; res[0].name = "PCI IO Primary"; res[1].start = IOP310_PCIPRI_LOWER_MEM; res[1].end = IOP310_PCIPRI_LOWER_MEM + IOP310_PCI_WINDOW_SIZE; res[1].name = "PCI Memory Primary"; break; case 1: res[0].start = IOP310_PCISEC_LOWER_IO + 0x6e000000; res[0].end = IOP310_PCISEC_LOWER_IO + 0x6e00ffff; res[0].name = "PCI IO Secondary"; res[1].start = IOP310_PCISEC_LOWER_MEM; res[1].end = IOP310_PCISEC_LOWER_MEM + IOP310_PCI_WINDOW_SIZE; res[1].name = "PCI Memory Secondary"; break; } request_resource(&ioport_resource, &res[0]); request_resource(&iomem_resource, &res[1]); sys->resource[0] = &res[0]; sys->resource[1] = &res[1]; sys->resource[2] = NULL; sys->io_offset = 0x6e000000; return 1; } void iop310_init(void) { DBG("PCI: Intel 80312 PCI-to-PCI init code.\n"); DBG(" ATU secondary: ATUCR =0x%08x\n", *IOP310_ATUCR); DBG(" ATU secondary: SOMWVR=0x%08x SOIOWVR=0x%08x\n", *IOP310_SOMWVR, *IOP310_SOIOWVR); DBG(" ATU secondary: SIABAR=0x%08x SIALR =0x%08x SIATVR=%08x\n", *IOP310_SIABAR, *IOP310_SIALR, *IOP310_SIATVR); DBG(" ATU primary: POMWVR=0x%08x POIOWVR=0x%08x\n", *IOP310_POMWVR, *IOP310_POIOWVR); DBG(" ATU primary: PIABAR=0x%08x PIALR =0x%08x PIATVR=%08x\n", *IOP310_PIABAR, *IOP310_PIALR, *IOP310_PIATVR); DBG(" P2P: PCR=0x%04x BCR=0x%04x EBCR=0x%04x\n", *IOP310_PCR, *IOP310_BCR, *IOP310_EBCR); /* * Windows have to be carefully opened via a nice set of calls * here or just some direct register fiddling in the board * specific init when we want transactions to occur between the * two PCI hoses. * * To do this, we will have manage RETRY assertion between the * firmware and the kernel. This will ensure that the host * system's enumeration code is held off until we have tweaked * the interrupt routing and public/private IDSELs. * * For now we will simply default to disabling the integrated type * 81 P2P bridge. */ *IOP310_PCR &= 0xfff8; external_fault = iop310_pci_abort_handler; }