/*****************************************************************************/
/*
* comemlite.c -- PCI access code for embedded CO-MEM Lite PCI controller.
*
* (C) Copyright 1999-2003, Greg Ungerer (gerg@snapgear.com).
* (C) Copyright 2000, Lineo (www.lineo.com)
*/
/*****************************************************************************/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/ptrace.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include <asm/irq.h>
#include <asm/anchor.h>
#ifdef CONFIG_eLIA
#include <asm/elia.h>
#endif
/*****************************************************************************/
/*
* Debug configuration defines. DEBUGRES sets debugging output for
* the resource allocation phase. DEBUGPCI traces on pcibios_ function
* calls, and DEBUGIO traces all accesses to devices on the PCI bus.
*/
/*#define DEBUGRES 1*/
/*#define DEBUGPCI 1*/
/*#define DEBUGIO 1*/
/*****************************************************************************/
/*
* PCI markers for bus present and active slots.
*/
int pci_bus_is_present = 0;
unsigned long pci_slotmask = 0;
/*
* We may or may not need to swap the bytes of PCI bus tranfers.
* The endianess is re-roder automatically by the CO-MEM, but it
* will get the wrong byte order for a pure data stream.
*/
#define pci_byteswap 0
/*
* Resource tracking. The CO-MEM part creates a virtual address
* space that all the PCI devices live in - it is not in any way
* directly mapped into the ColdFire address space. So we can
* really assign any resources we like to devices, as long as
* they do not clash with other PCI devices.
*/
unsigned int pci_iobase = PCIBIOS_MIN_IO; /* Arbitrary start address */
unsigned int pci_membase = PCIBIOS_MIN_MEM; /* Arbitrary start address */
#define PCI_MINIO 0x100 /* 256 byte minimum I/O */
#define PCI_MINMEM 0x00010000 /* 64k minimum chunk */
/*
* The CO-MEM's shared memory segment is visible inside the PCI
* memory address space. We need to keep track of the address that
* this is mapped at, to setup the bus masters pointers.
*/
unsigned int pci_shmemaddr;
/*****************************************************************************/
void pci_interrupt(int irq, void *id, struct pt_regs *fp);
/*****************************************************************************/
/*
* Some platforms have custom ways of reseting the PCI bus.
*/
void pci_resetbus(void)
{
#ifdef CONFIG_eLIA
int i;
#ifdef DEBUGPCI
printk(KERN_DEBUG "pci_resetbus()\n");
#endif
*((volatile unsigned short *) (MCF_MBAR+MCFSIM_PADDR)) |= eLIA_PCIRESET;
for (i = 0; (i < 1000); i++) {
*((volatile unsigned short *) (MCF_MBAR + MCFSIM_PADAT)) =
(ppdata | eLIA_PCIRESET);
}
*((volatile unsigned short *) (MCF_MBAR + MCFSIM_PADAT)) = ppdata;
#endif
}
/*****************************************************************************/
int pcibios_assign_resource_slot(int slot)
{
volatile unsigned long *rp;
volatile unsigned char *ip;
unsigned int idsel, addr, val, align, i;
int bar;
#ifdef DEBUGPCI
printk(KERN_INFO "pcibios_assign_resource_slot(slot=%x)\n", slot);
#endif
rp = (volatile unsigned long *) COMEM_BASE;
idsel = COMEM_DA_ADDR(0x1 << (slot + 16));
/* Try to assign resource to each BAR */
for (bar = 0; (bar < 6); bar++) {
addr = COMEM_PCIBUS + PCI_BASE_ADDRESS_0 + (bar * 4);
rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGRD | idsel;
val = rp[LREG(addr)];
#ifdef DEBUGRES
printk(KERN_DEBUG "-----------------------------------"
"-------------------------------------\n");
printk(KERN_DEBUG "BAR[%d]: read=%08x ", bar, val);
#endif
rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGWR | idsel;
rp[LREG(addr)] = 0xffffffff;
rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGRD | idsel;
val = rp[LREG(addr)];
#ifdef DEBUGRES
printk(KERN_DEBUG "write=%08x ", val);
#endif
if (val == 0) {
#ifdef DEBUGRES
printk(KERN_DEBUG "\n");
#endif
continue;
}
/* Determine space required by BAR */
/* FIXME: this should go backwords from 0x80000000... */
for (i = 0; (i < 32); i++) {
if ((0x1 << i) & (val & 0xfffffffc))
break;
}
#ifdef DEBUGRES
printk(KERN_DEBUG "size=%08x(%d)\n", (0x1 << i), i);
#endif
i = 0x1 << i;
/* Assign a resource */
if (val & PCI_BASE_ADDRESS_SPACE_IO) {
if (i < PCI_MINIO)
i = PCI_MINIO;
#ifdef DEBUGRES
printk(KERN_DEBUG "BAR[%d]: IO size=%08x iobase=%08x\n",
bar, i, pci_iobase);
#endif
if (i > 0xffff) {
/* Invalid size?? */
val = 0 | PCI_BASE_ADDRESS_SPACE_IO;
#ifdef DEBUGRES
printk(KERN_DEBUG "BAR[%d]: too big for IO??\n", bar);
#endif
} else {
/* Check for un-alignment */
if ((align = pci_iobase % i))
pci_iobase += (i