/*
 * BRIEF MODULE DESCRIPTION
 * Galileo Evaluation Boards PCI support.
 *
 * The general-purpose functions to read/write and configure the GT64120A's
 * PCI registers (function names start with pci0 or pci1) are either direct
 * copies of functions written by Galileo Technology, or are modifications
 * of their functions to work with Linux 2.4 vs Linux 2.2.  These functions
 * are Copyright - Galileo Technology.
 *
 * Other functions are derived from other MIPS PCI implementations, or were
 * written by RidgeRun, Inc,  Copyright (C) 2000 RidgeRun, Inc.
 *   glonnon@ridgerun.com, skranz@ridgerun.com, stevej@ridgerun.com
 *
 *  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/config.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <asm/pci.h>
#include <asm/io.h>
#include <asm/galileo-boards/ev64120.h>
#include <asm/gt64120.h>

#include <linux/init.h>

#undef PCI_DEBUG

#ifdef PCI_DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif

#ifdef CONFIG_PCI

#define SELF 0

/*
 * These functions and structures provide the BIOS scan and mapping of the PCI
 * devices.
 */

#define MAX_PCI_DEVS 10

struct pci_device {
	u32 slot;
	u32 BARtype[6];
	u32 BARsize[6];
};

static void __init scan_and_initialize_pci(void);
static u32 __init scan_pci_bus(struct pci_device *pci_devices);
static void __init allocate_pci_space(struct pci_device *pci_devices);

static void __init galileo_pcibios_fixup_bus(struct pci_bus *bus);

/*
 * The functions that actually read and write to the controller.
 * Copied from or modified from Galileo Technology code.
 */
static unsigned int pci0ReadConfigReg(int offset, struct pci_dev *device);
static void pci0WriteConfigReg(unsigned int offset,
			       struct pci_dev *device, unsigned int data);
static unsigned int pci1ReadConfigReg(int offset, struct pci_dev *device);
static void pci1WriteConfigReg(unsigned int offset,
			       struct pci_dev *device, unsigned int data);

static void pci0MapIOspace(unsigned int pci0IoBase,
			   unsigned int pci0IoLength);
static void pci1MapIOspace(unsigned int pci1IoBase,
			   unsigned int pci1IoLength);
static void pci0MapMemory0space(unsigned int pci0Mem0Base,
				unsigned int pci0Mem0Length);
static void pci1MapMemory0space(unsigned int pci1Mem0Base,
				unsigned int pci1Mem0Length);
static void pci0MapMemory1space(unsigned int pci0Mem1Base,
				unsigned int pci0Mem1Length);
static void pci1MapMemory1space(unsigned int pci1Mem1Base,
				unsigned int pci1Mem1Length);
static unsigned int pci0GetIOspaceBase(void);
static unsigned int pci0GetIOspaceSize(void);
static unsigned int pci0GetMemory0Base(void);
static unsigned int pci0GetMemory0Size(void);
static unsigned int pci0GetMemory1Base(void);
static unsigned int pci0GetMemory1Size(void);
static unsigned int pci1GetIOspaceBase(void);
static unsigned int pci1GetIOspaceSize(void);
static unsigned int pci1GetMemory0Base(void);
static unsigned int pci1GetMemory0Size(void);
static unsigned int pci1GetMemory1Base(void);
static unsigned int pci1GetMemory1Size(void);


/*  Functions to implement "pci ops"  */
static int galileo_pcibios_read_config_word(struct pci_dev *dev,
					    int offset, u16 * val);
static int galileo_pcibios_read_config_byte(struct pci_dev *dev,
					    int offset, u8 * val);
static int galileo_pcibios_read_config_dword(struct pci_dev *dev,
					     int offset, u32 * val);
static int galileo_pcibios_write_config_byte(struct pci_dev *dev,
					     int offset, u8 val);
static int galileo_pcibios_write_config_word(struct pci_dev *dev,
					     int offset, u16 val);
static int galileo_pcibios_write_config_dword(struct pci_dev *dev,
					      int offset, u32 val);
static void galileo_pcibios_set_master(struct pci_dev *dev);

/*
 * General-purpose PCI functions.
 */

/*
 * pci0MapIOspace - Maps PCI0 IO space for the master.
 * Inputs: base and length of pci0Io
 */
static void pci0MapIOspace(unsigned int pci0IoBase, unsigned int pci0IoLength)
{
	unsigned int pci0IoTop =
	    (unsigned int) (pci0IoBase + pci0IoLength);

	if (pci0IoLength == 0)
		pci0IoTop++;

	pci0IoBase = (unsigned int) (pci0IoBase >> 21);
	pci0IoTop = (unsigned int) (((pci0IoTop - 1) & 0x0fffffff) >> 21);
	GT_WRITE(GT_PCI0IOLD_OFS, pci0IoBase);
	GT_WRITE(GT_PCI0IOHD_OFS, pci0IoTop);
}

/*
 * pci1MapIOspace - Maps PCI1 IO space for the master.
 * Inputs: base and length of pci1Io
 */

static void pci1MapIOspace(unsigned int pci1IoBase,
			   unsigned int pci1IoLength)
{
	unsigned int pci1IoTop =
	    (unsigned int) (pci1IoBase + pci1IoLength);

	if (pci1IoLength == 0)
		pci1IoTop++;

	pci1IoBase = (unsigned int) (pci1IoBase >> 21);
	pci1IoTop = (unsigned int) (((pci1IoTop - 1) & 0x0fffffff) >> 21);
	GT_WRITE(GT_PCI1IOLD_OFS, pci1IoBase);
	GT_WRITE(GT_PCI1IOHD_OFS, pci1IoTop);
}

/*
 * pci0MapMemory0space - Maps PCI0 memory0 space for the master.
 * Inputs: base and length of pci0Mem0
 */

static void pci0MapMemory0space(unsigned int pci0Mem0Base,
				unsigned int pci0Mem0Length)
{
	unsigned int pci0Mem0Top = pci0Mem0Base + pci0Mem0Length;

	if (pci0Mem0Length == 0)
		pci0Mem0Top++;

	pci0Mem0Base = pci0Mem0Base >> 21;
	pci0Mem0Top = ((pci0Mem0Top - 1) & 0x0fffffff) >> 21;
	GT_WRITE(GT_PCI0M0LD_OFS, pci0Mem0Base);
	GT_WRITE(GT_PCI0M0HD_OFS, pci0Mem0Top);
}

/*
 * pci1MapMemory0space - Maps PCI1 memory0 space for the master.
 * Inputs: base and length of pci1Mem0
 */

static void pci1MapMemory0space(unsigned int pci1Mem0Base,
				unsigned int pci1Mem0Length)
{
	unsigned int pci1Mem0Top = pci1Mem0Base + pci1Mem0Length;

	if (pci1Mem0Length == 0)
		pci1Mem0Top++;

	pci1Mem0Base = pci1Mem0Base >> 21;
	pci1Mem0Top = ((pci1Mem0Top - 1) & 0x0fffffff) >> 21;
	GT_WRITE(GT_PCI1M0LD_OFS, pci1Mem0Base);
	GT_WRITE(GT_PCI1M0HD_OFS, pci1Mem0Top);
}

/*
 * pci0MapMemory1space - Maps PCI0 memory1 space for the master.
 * Inputs: base and length of pci0Mem1
 */

static void pci0MapMemory1space(unsigned int pci0Mem1Base,
				unsigned int pci0Mem1Length)
{
	unsigned int pci0Mem1Top = pci0Mem1Base + pci0Mem1Length;

	if (pci0Mem1Length == 0)
		pci0Mem1Top++;

	pci0Mem1Base = pci0Mem1Base >> 21;
	pci0Mem1Top = ((pci0Mem1Top - 1) & 0x0fffffff) >> 21;
	GT_WRITE(GT_PCI0M1LD_OFS, pci0Mem1Base);
	GT_WRITE(GT_PCI0M1HD_OFS, pci0Mem1Top);

}

/*
 * pci1MapMemory1space - Maps PCI1 memory1 space for the master.
 * Inputs: base and length of pci1Mem1
 */

static void pci1MapMemory1space(unsigned int pci1Mem1Base,
				unsigned int pci1Mem1Length)
{
	unsigned int pci1Mem1Top = pci1Mem1Base + pci1Mem1Length;

	if (pci1Mem1Length == 0)
		pci1Mem1Top++;

	pci1Mem1Base = pci1Mem1Base >> 21;
	pci1Mem1Top = ((pci1Mem1Top - 1) & 0x0fffffff) >> 21;
	GT_WRITE(GT_PCI1M1LD_OFS, pci1Mem1Base);
	GT_WRITE(GT_PCI1M1HD_OFS, pci1Mem1Top);
}

/*
 * pci0GetIOspaceBase - Return PCI0 IO Base Address.
 * Inputs: N/A
 * Returns: PCI0 IO Base Address.
 */

static unsigned int pci0GetIOspaceBase(void)
{
	unsigned int base;
	GT_READ(GT_PCI0IOLD_OFS, &base);
	base = base << 21;
	return base;
}

/*
 * pci0GetIOspaceSize - Return PCI0 IO Bar Size.
 * Inputs: N/A
 * Returns: PCI0 IO Bar Size.
 */
static unsigned int pci0GetIOspaceSize(void)
{
	unsigned int top, base, size;
	GT_READ(GT_PCI0IOLD_OFS, &base);
	base = base << 21;
	GT_READ(GT_PCI0IOHD_OFS, &top);
	top = (top << 21);
	size = ((top - base) & 0xfffffff);
	size = size | 0x1fffff;
	return (size + 1);
}

/*
 * pci0GetMemory0Base - Return PCI0 Memory 0 Base Address.
 * Inputs: N/A
 * Returns: PCI0 Memory 0 Base Address.
 */
static unsigned int pci0GetMemory0Base(void)
{
	unsigned int base;
	GT_READ(GT_PCI0M0LD_OFS, &base);
	base = base << 21;
	return base;
}

/*
 * pci0GetMemory0Size - Return PCI0 Memory 0 Bar Size.
 * Inputs: N/A
 * Returns: PCI0 Memory 0 Bar Size.
 */
static unsigned int pci0GetMemory0Size(void)
{
	unsigned int top, base, size;
	GT_READ(GT_PCI0M0LD_OFS, &base);
	base = base << 21;
	GT_READ(GT_PCI0M0HD_OFS, &top);
	top = (top << 21);
	size = ((top - base) & 0xfffffff);
	size = size | 0x1fffff;
	return (size + 1);
}

/*
 * pci0GetMemory1Base - Return PCI0 Memory 1 Base Address.
 * Inputs: N/A
 * Returns: PCI0 Memory 1 Base Address.
 */
static unsigned int pci0GetMemory1Base(void)
{
	unsigned int base;
	GT_READ(GT_PCI0M1LD_OFS, &base);
	base = base << 21;
	return base;
}

/*
 * pci0GetMemory1Size - Return PCI0 Memory 1 Bar Size.
 * Inputs: N/A
 * Returns: PCI0 Memory 1 Bar Size.
 */

static unsigned int pci0GetMemory1Size(void)
{
	unsigned int top, base, size;
	GT_READ(GT_PCI0M1LD_OFS, &base);
	base = base << 21;
	GT_READ(GT_PCI0M1HD_OFS, &top);
	top = (top << 21);
	size = ((top - base) & 0xfffffff);
	size = size | 0x1fffff;
	return (size + 1);
}

/*
 * pci1GetIOspaceBase - Return PCI1 IO Base Address.
 * Inputs: N/A
 * Returns: PCI1 IO Base Address.
 */

static unsigned int pci1GetIOspaceBase(void)
{
	unsigned int base;
	GT_READ(GT_PCI1IOLD_OFS, &base);
	base = base << 21;
	return base;
}

/*
 * pci1GetIOspaceSize - Return PCI1 IO Bar Size.
 * Inputs: N/A
 * Returns: PCI1 IO Bar Size.
 */

static unsigned int pci1GetIOspaceSize(void)
{
	unsigned int top, base, size;
	GT_READ(GT_PCI1IOLD_OFS, &base);
	base = base << 21;
	GT_READ(GT_PCI1IOHD_OFS, &top);
	top = (top << 21);
	size = ((top - base) & 0xfffffff);
	size = size | 0x1fffff;
	return (size + 1);
}

/*
 * pci1GetMemory0Base - Return PCI1 Memory 0 Base Address.
 * Inputs: N/A
 * Returns: PCI1 Memory 0 Base Address.
 */

static unsigned int pci1GetMemory0Base(void)
{
	unsigned int base;
	GT_READ(GT_PCI1M0LD_OFS, &base);
	base = base << 21;
	return base;
}

/*
 * pci1GetMemory0Size - Return PCI1 Memory 0 Bar Size.
 * Inputs: N/A
 * Returns: PCI1 Memory 0 Bar Size.
 */

static unsigned int pci1GetMemory0Size(void)
{
	unsigned int top, base, size;
	GT_READ(GT_PCI1M1LD_OFS, &base);
	base = base << 21;
	GT_READ(GT_PCI1M1HD_OFS, &top);
	top = (top << 21);
	size = ((top - base) & 0xfffffff);
	size = size | 0x1fffff;
	return (size + 1);
}

/*
 * pci1GetMemory1Base - Return PCI1 Memory 1 Base Address.
 * Inputs: N/A
 * Returns: PCI1 Memory 1 Base Address.
 */

static unsigned int pci1GetMemory1Base(void)
{
	unsigned int base;
	GT_READ(GT_PCI1M1LD_OFS, &base);
	base = base << 21;
	return base;
}

/*
 * pci1GetMemory1Size - Return PCI1 Memory 1 Bar Size.
 * Inputs: N/A
 * Returns: PCI1 Memory 1 Bar Size.
 */

static unsigned int pci1GetMemory1Size(void)
{
	unsigned int top, base, size;
	GT_READ(GT_PCI1M1LD_OFS, &base);
	base = base << 21;
	GT_READ(GT_PCI1M1HD_OFS, &top);
	top = (top << 21);
	size = ((top - base) & 0xfffffff);
	size = size | 0x1fffff;
	return (size + 1);
}



/*
 * pci_range_ck -
 *
 * Check if the pci device that are trying to access does really exists
 * on the evaluation board.
 *
 * Inputs :
 * bus - bus number (0 for PCI 0 ; 1 for PCI 1)
 * dev - number of device on the specific pci bus
 *
 * Outpus :
 * 0 - if OK , 1 - if failure
 */
static __inline__ int pci_range_ck(unsigned char bus, unsigned char dev)
{
	//DBG(KERN_INFO "p_r_c %d %d\n",bus,dev);
	if (((bus == 0) || (bus == 1)) && (dev >= 6) && (dev <= 8))
		return 0;	// Bus/Device Number OK
	return -1;		// Bus/Device Number not OK
}

/*
 * pciXReadConfigReg  - Read from a PCI configuration register
 *                    - Make sure the GT is configured as a master before
 *                      reading from another device on the PCI.
 *                   - The function takes care of Big/Little endian conversion.
 * INPUTS:   regOffset: The register offset as it apears in the GT spec (or PCI
 *                        spec)
 *           pciDevNum: The device number needs to be addressed.
 * RETURNS: data , if the data == 0xffffffff check the master abort bit in the
 *                 cause register to make sure the data is valid
 *
 *  Configuration Address 0xCF8:
 *
 *       31 30    24 23  16 15  11 10     8 7      2  0     <=bit Number
 *  |congif|Reserved|  Bus |Device|Function|Register|00|
 *  |Enable|        |Number|Number| Number | Number |  |    <=field Name
 *
 */
static unsigned int pci0ReadConfigReg(int offset, struct pci_dev *device)
{
	unsigned int DataForRegCf8;
	unsigned int data;

	DataForRegCf8 = ((PCI_SLOT(device->devfn) << 11) |
			 (PCI_FUNC(device->devfn) << 8) |
			 (offset & ~0x3)) | 0x80000000;
	GT_WRITE(GT_PCI0_CFGADDR_OFS, DataForRegCf8);

	/*  The casual observer might wonder why the READ is duplicated here,
	   rather than immediately following the WRITE, and just have the
	   swap in the "if".  That's because there is a latency problem
	   with trying to read immediately after setting up the address
	   register.  The "if" check gives enough time for the address
	   to stabilize, so the READ can work.
	 */
	if (PCI_SLOT(device->devfn) == SELF) {	/* This board */
		GT_READ(GT_PCI0_CFGDATA_OFS, &data);
		return data;
	} else {	/* The PCI is working in LE Mode so swap the Data. */
		GT_READ(GT_PCI0_CFGDATA_OFS, &data);
		return cpu_to_le32(data);
	}
}

static unsigned int pci1ReadConfigReg(int offset, struct pci_dev *device)
{
	unsigned int DataForRegCf8;
	unsigned int data;

	DataForRegCf8 = ((PCI_SLOT(device->devfn) << 11) |
			 (PCI_FUNC(device->devfn) << 8) |
			 (offset & ~0x3)) | 0x80000000;
	/*  The casual observer might wonder why the READ is duplicated here,
	   rather than immediately following the WRITE, and just have the
	   swap in the "if".  That's because there is a latency problem
	   with trying to read immediately after setting up the address
	   register.  The "if" check gives enough time for the address
	   to stabilize, so the READ can work.
	 */
	if (PCI_SLOT(device->devfn) == SELF) {	/* This board */
		/* when configurating our own PCI 1 L-unit the access is through
		   the PCI 0 interface with reg number = reg number + 0x80 */
		DataForRegCf8 |= 0x80;
		GT_WRITE(GT_PCI0_CFGADDR_OFS, DataForRegCf8);
	} else {		/* The PCI is working in LE Mode so swap the Data. */
		GT_WRITE(GT_PCI1_CFGADDR_OFS, DataForRegCf8);
	}
	if (PCI_SLOT(device->devfn) == SELF) {	/* This board */
		GT_READ(GT_PCI0_CFGDATA_OFS, &data);
		return data;
	} else {
		GT_READ(GT_PCI1_CFGDATA_OFS, &data);
		return cpu_to_le32(data);
	}
}



/*
 * pciXWriteConfigReg - Write to a PCI configuration register
 *                    - Make sure the GT is configured as a master before
 *                      writingto another device on the PCI.
 *                    - The function takes care of Big/Little endian conversion.
 * Inputs:   unsigned int regOffset: The register offset as it apears in the
 *           GT spec
 *                   (or any other PCI device spec)
 *           pciDevNum: The device number needs to be addressed.
 *
 *  Configuration Address 0xCF8:
 *
 *       31 30    24 23  16 15  11 10     8 7      2  0     <=bit Number
 *  |congif|Reserved|  Bus |Device|Function|Register|00|
 *  |Enable|        |Number|Number| Number | Number |  |    <=field Name
 *
 */
static void pci0WriteConfigReg(unsigned int offset,
			       struct pci_dev *device, unsigned int data)
{
	unsigned int DataForRegCf8;

	DataForRegCf8 = ((PCI_SLOT(device->devfn) << 11) |
			 (PCI_FUNC(device->devfn) << 8) |
			 (offset & ~0x3)) | 0x80000000;
	GT_WRITE(GT_PCI0_CFGADDR_OFS, DataForRegCf8);
	if (PCI_SLOT(device->devfn) == SELF) {	/* This board */
		GT_WRITE(GT_PCI0_CFGDATA_OFS, data);
	} else {		/* configuration Transaction over the pci. */
		/* The PCI is working in LE Mode so swap the Data. */
		GT_WRITE(GT_PCI0_CFGDATA_OFS, le32_to_cpu(data));
	}
}

static void pci1WriteConfigReg(unsigned int offset,
			       struct pci_dev *device, unsigned int data)
{
	unsigned int DataForRegCf8;

	DataForRegCf8 = ((PCI_SLOT(device->devfn) << 11) |
			 (PCI_FUNC(device->devfn) << 8) |
			 (offset & ~0x3)) | 0x80000000;
	/*  There is a latency problem
	   with trying to read immediately after setting up the address
	   register.  The "if" check gives enough time for the address
	   to stabilize, so the WRITE can work.
	 */
	if (PCI_SLOT(device->devfn) == SELF) {	/* This board */
		/* when configurating our own PCI 1 L-unit the access is through
		   the PCI 0 interface with reg number = reg number + 0x80 */
		DataForRegCf8 |= 0x80;
		GT_WRITE(GT_PCI0_CFGADDR_OFS, DataForRegCf8);
	} else {		/* configuration Transaction over the pci. */
		/* The PCI is working in LE Mode so swap the Data. */
		GT_WRITE(GT_PCI1_CFGADDR_OFS, DataForRegCf8);
	}
	if (PCI_SLOT(device->devfn) == SELF) {	/* This board */
		GT_WRITE(GT_PCI0_CFGDATA_OFS, data);
	} else {		/* configuration Transaction over the pci. */
		GT_WRITE(GT_PCI1_CFGADDR_OFS, le32_to_cpu(data));
	}
}


/*
 * galileo_pcibios_(read/write)_config_(dword/word/byte) -
 *
 * reads/write a dword/word/byte register from the configuration space
 * of a device.
 *
 * Inputs :
 * bus - bus number
 * dev - device number
 * offset - register offset in the configuration space
 * val - value to be written / read
 *
 * Outputs :
 * PCIBIOS_SUCCESSFUL when operation was succesfull
 * PCIBIOS_DEVICE_NOT_FOUND when the bus or dev is errorneous
 * PCIBIOS_BAD_REGISTER_NUMBER when accessing non aligned
 */

static int galileo_pcibios_read_config_dword(struct pci_dev *device,
					     int offset, u32 * val)
{
	int dev, bus;
	//DBG(KERN_INFO "rcd entry \n",offset,val);
	bus = device->bus->number;
	dev = PCI_SLOT(device->devfn);

	if (pci_range_ck(bus, dev)) {
		*val = 0xffffffff;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}
	if (offset & 0x3)
		return PCIBIOS_BAD_REGISTER_NUMBER;
	if (bus == 0)
		*val = pci0ReadConfigReg(offset, device);
//  if (bus == 1) *val = pci1ReadConfigReg (offset,device);
	DBG(KERN_INFO "rr: rcd dev %d offset %x %x\n", dev, offset, *val);

	/*
	 * This is so that the upper PCI layer will get the correct return
	 * value if we're not attached to anything.
	 */
	if ((offset == 0) && (*val == 0xffffffff)) {
		return PCIBIOS_DEVICE_NOT_FOUND;
	}

	return PCIBIOS_SUCCESSFUL;
}

static int galileo_pcibios_read_config_word(struct pci_dev *device,
					    int offset, u16 * val)
{
	int dev, bus;

	bus = device->bus->number;
	dev = PCI_SLOT(device->devfn);

	if (pci_range_ck(bus, dev)) {
		*val = 0xffff;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}
	if (offset & 0x1)
		return PCIBIOS_BAD_REGISTER_NUMBER;

	if (bus == 0)
		*val = (unsigned short) (pci0ReadConfigReg(offset, device) >>
				         ((offset & ~0x3) * 8));
//  if (bus == 1) *val = (unsigned short) (pci1ReadConfigReg(offset,device) >> ((offset & ~0x3) * 8));

	DBG(KERN_INFO "rr: rcw dev %d offset %x %x\n", dev, offset, *val);

	return PCIBIOS_SUCCESSFUL;
}

static int galileo_pcibios_read_config_byte(struct pci_dev *device,
					    int offset, u8 * val)
{
	int dev, bus;

	bus = device->bus->number;
	dev = PCI_SLOT(device->devfn);

	if (pci_range_ck(bus, dev)) {
		*val = 0xff;
		return PCIBIOS_DEVICE_NOT_FOUND;
	}

	if (bus == 0)
		*val =
		    (unsigned char) (pci0ReadConfigReg(offset, device) >>
				     ((offset & ~0x3) * 8));
//  if (bus == 1) *val = (unsigned char) (pci1ReadConfigReg(offset,device) >> ((offset & ~0x3) * 8));

	DBG(KERN_INFO "rr: rcb dev %d offset %x %x\n", dev, offset, *val);

	/*  This is so that the upper PCI layer will get the correct return value if
	   we're not attached to anything.  */
	if ((offset == 0xe) && (*val == 0xff)) {
		u32 MasterAbort;
		GT_READ(GT_INTRCAUSE_OFS, &MasterAbort);
		if (MasterAbort & 0x40000) {
			DBG(KERN_INFO "PCI Master Abort, ICR %x\n",
			    MasterAbort);
			GT_WRITE(GT_INTRCAUSE_OFS,
				 (MasterAbort & 0xfffbffff));
			return PCIBIOS_DEVICE_NOT_FOUND;
		}
	}

	return PCIBIOS_SUCCESSFUL;
}

static int galileo_pcibios_write_config_dword(struct pci_dev *device,
					      int offset, u32 val)
{
	int dev, bus;

	bus = device->bus->number;
	dev = PCI_SLOT(device->devfn);

	if (pci_range_ck(bus, dev))
		return PCIBIOS_DEVICE_NOT_FOUND;
	if (offset & 0x3)
		return PCIBIOS_BAD_REGISTER_NUMBER;
	if (bus == 0)
		pci0WriteConfigReg(offset, device, val);
//  if (bus == 1) pci1WriteConfigReg (offset,device,val);

	DBG(KERN_INFO "rr: wcd dev %d, offset %x, val %x\n", dev, offset,
	    val);
	return PCIBIOS_SUCCESSFUL;
}


static int galileo_pcibios_write_config_word(struct pci_dev *device,
					     int offset, u16 val)
{
	int dev, bus;
	unsigned long tmp;

	bus = device->bus->number;
	dev = PCI_SLOT(device->devfn);

	if (pci_range_ck(bus, dev))
		return PCIBIOS_DEVICE_NOT_FOUND;
	if (offset & 0x1)
		return PCIBIOS_BAD_REGISTER_NUMBER;
	if (bus == 0)
		tmp = pci0ReadConfigReg(offset, device);
//  if (bus == 1) tmp = pci1ReadConfigReg (offset,device);

	if ((offset % 4) == 0)
		tmp = (tmp & 0xffff0000) | (val & 0xffff);
	if ((offset % 4) == 2)
		tmp = (tmp & 0x0000ffff) | ((val & 0xffff) << 16);

	if (bus == 0)
		pci0WriteConfigReg(offset, device, tmp);
//  if (bus == 1) pci1WriteConfigReg (offset,device,tmp);
	DBG(KERN_INFO "rr: wcw dev %d, offset %x, val %x\n", dev, offset,
	    val);
	return PCIBIOS_SUCCESSFUL;
}

static int galileo_pcibios_write_config_byte(struct pci_dev *device,
					     int offset, u8 val)
{
	int dev, bus;
	unsigned long tmp;

	bus = device->bus->number;
	dev = PCI_SLOT(device->devfn);

	if (pci_range_ck(bus, dev))
		return PCIBIOS_DEVICE_NOT_FOUND;
	if (bus == 0)
		tmp = pci0ReadConfigReg(offset, device);
//  if (bus == 1) tmp = pci1ReadConfigReg (offset,device);

	if ((offset % 4) == 0)
		tmp = (tmp & 0xffffff00) | (val & 0xff);
	if ((offset % 4) == 1)
		tmp = (tmp & 0xffff00ff) | ((val & 0xff) << 8);
	if ((offset % 4) == 2)
		tmp = (tmp & 0xff00ffff) | ((val & 0xff) << 16);
	if ((offset % 4) == 3)
		tmp = (tmp & 0x00ffffff) | ((val & 0xff) << 24);

	if (bus == 0)
		pci0WriteConfigReg(offset, device, tmp);
//  if (bus == 1) pci1WriteConfigReg (offset,device,tmp);
	DBG(KERN_INFO "rr: wcb dev %d, offset %x, val %x\n", dev, offset,
	    val);

	return PCIBIOS_SUCCESSFUL;
}

static void galileo_pcibios_set_master(struct pci_dev *dev)
{
	u16 cmd;

	DBG(KERN_INFO "rr: galileo_pcibios_set_master\n");

	galileo_pcibios_read_config_word(dev, PCI_COMMAND, &cmd);
	cmd |= PCI_COMMAND_MASTER;
	galileo_pcibios_write_config_word(dev, PCI_COMMAND, cmd);
	DBG("PCI: Enabling device %s (%04x)\n", dev->slot_name, cmd);
}

/*  Externally-expected functions.  Do not change function names  */

int pcibios_enable_resources(struct pci_dev *dev)
{
	u16 cmd, old_cmd;
	u16 tmp;
	u8 tmp1;
	int idx;
	struct resource *r;

	DBG(KERN_INFO "rr: pcibios_enable_resources\n");

	galileo_pcibios_read_config_word(dev, PCI_COMMAND, &cmd);
	old_cmd = cmd;
	for (idx = 0; idx < 6; idx++) {
		r = &dev->resource[idx];
		DBG(KERN_INFO
		    "rr: BAR %d, start %lx, end %lx, flags %lx\n", idx,
		    r->start, r->end, r->flags);
		if (!r->start && r->end) {
			printk(KERN_ERR
			       "PCI: Device %s not available because of resource collisions\n",
			       dev->slot_name);
			return -EINVAL;
		}
		if (r->flags & IORESOURCE_IO)
			cmd |= PCI_COMMAND_IO;
		if (r->flags & IORESOURCE_MEM)
			cmd |= PCI_COMMAND_MEMORY;
	}
	if (cmd != old_cmd) {
		DBG(KERN_INFO "PCI: Enabling device %s (%04x -> %04x)\n",
		    dev->slot_name, old_cmd, cmd);
		galileo_pcibios_write_config_word(dev, PCI_COMMAND, cmd);
	}

	/*
	   Let's fix up the latency timer and cache line size here.  Cache line size =
	   32 bytes / sizeof dword (4) = 8.
	   Latency timer must be > 8.  32 is random but appears to work.
	 */
	galileo_pcibios_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &tmp1);
	if (tmp1 != 8) {
		DBG(KERN_INFO
		    "rr: PCI setting cache line size to 8 from %d\n",
		    tmp1);
		galileo_pcibios_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
						  8);
	}
	galileo_pcibios_read_config_byte(dev, PCI_LATENCY_TIMER, &tmp1);
	if (tmp1 < 32) {
		DBG(KERN_INFO
		    "rr: PCI setting latency timer to 32 from %d\n", tmp1);
		galileo_pcibios_write_config_byte(dev, PCI_LATENCY_TIMER,
						  32);
	}

	return 0;
}

int pcibios_enable_device(struct pci_dev *dev)
{
	DBG(KERN_INFO "rr: pcibios_enable_device\n");
	return pcibios_enable_resources(dev);
}

void pcibios_update_resource(struct pci_dev *dev, struct resource *root,
			     struct resource *res, int resource)
{
	u32 new, check;
	int reg;

	DBG(KERN_INFO "rr: pcibios_update_resource\n");
	return;

	new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
	if (resource < 6) {
		reg = PCI_BASE_ADDRESS_0 + 4 * resource;
	} else if (resource == PCI_ROM_RESOURCE) {
		res->flags |= PCI_ROM_ADDRESS_ENABLE;
		reg = dev->rom_base_reg;
	} else {
		/* Somebody might have asked allocation of a non-standard resource */
		return;
	}

	pci_write_config_dword(dev, reg, new);
	pci_read_config_dword(dev, reg, &check);
	if ((new ^ check) &
	    ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK :
	     PCI_BASE_ADDRESS_MEM_MASK)) {
		DBG(KERN_ERR "PCI: Error while updating region "
		    "%s/%d (%08x != %08x)\n", dev->slot_name, resource,
		    new, check);
	}
}

void pcibios_align_resource(void *data, struct resource *res,
			    unsigned long size, unsigned long align)
{
	struct pci_dev *dev = data;

	DBG(KERN_INFO "pcibios_align_resource\n");

	if (res->flags & IORESOURCE_IO) {
		unsigned long start = res->start;

		/* We need to avoid collisions with `mirrored' VGA ports
		   and other strange ISA hardware, so we always want the
		   addresses kilobyte aligned.  */
		if (size > 0x100) {
			DBG(KERN_ERR "PCI: I/O Region %s/%d too large"
			    " (%ld bytes)\n", dev->slot_name,
			    dev->resource - res, size);
		}

		start = (start + 1024 - 1) & ~(1024 - 1);
		res->start = start;
	}
}

/*
 * structure galileo_pci_ops
 *
 * This structure holds the pointers for the PCI configuration space
 * access, and the fixup for the interrupts.
 * This structure is registered to the operating system in boot time
 */
struct pci_ops galileo_pci_ops = {
	galileo_pcibios_read_config_byte,
	galileo_pcibios_read_config_word,
	galileo_pcibios_read_config_dword,
	galileo_pcibios_write_config_byte,
	galileo_pcibios_write_config_word,
	galileo_pcibios_write_config_dword
};

/*
 * galileo_pcibios_fixup_bus -
 *
 * After detecting all agents over the PCI , this function is called
 * in order to give an interrupt number for each PCI device starting
 * from IRQ 20. It does also enables master for each device.
 *
 * Inputs :
 * mem_start , mem_end are not relevant in MIPS architecture.
 *
 * Outpus :
 * return always mem_start
 */
static void __init galileo_pcibios_fixup_bus(struct pci_bus *bus)
{
	unsigned int Current_IRQ = 20;
	struct pci_bus *current_bus = bus;
	struct pci_dev *devices;
	struct list_head *devices_link;

	list_for_each(devices_link, &(current_bus->devices)) {
		devices = pci_dev_b(devices_link);
		if (devices != NULL) {
			devices->irq = Current_IRQ++;

			/* Assign an interrupt number for the device */
			galileo_pcibios_write_config_byte(devices,
							  PCI_INTERRUPT_LINE,
							  Current_IRQ);
			galileo_pcibios_set_master(devices);

		}
	}

}

struct pci_fixup pcibios_fixups[] = {
//    { PCI_FIXUP_HEADER, 0x4620, 0x11ab, galileo_pcibios_fixup },
	{0}
};

void __init pcibios_fixup_bus(struct pci_bus *c)
{
	DBG(KERN_INFO "rr: pcibios_fixup_bus\n");
	galileo_pcibios_fixup_bus(c);
}

/*
 * This code was derived from Galileo Technology's example
 * and significantly reworked.
 *
 * This is very simple.  It does not scan multiple function devices.  It does
 * not scan behind bridges.  Those would be simple to implement, but we don't
 * currently need this.
 */
static void __init scan_and_initialize_pci(void)
{
	struct pci_device pci_devices[MAX_PCI_DEVS];

	if (scan_pci_bus(pci_devices)) {
		allocate_pci_space(pci_devices);
	}
}

/*
 * This is your basic PCI scan.  It goes through each slot and checks to
 * see if there's something that responds.  If so, then get the size and
 * type of each of the responding BARs.  Save them for later.
 */

static u32 __init scan_pci_bus(struct pci_device *pci_devices)
{
	u32 arrayCounter = 0;
	u32 memType;
	u32 memSize;
	u32 pci_slot, bar;
	u32 id;
	u32 c18RegValue;
	struct pci_dev device;

	DBG(KERN_INFO "rr: scan_pci_bus\n");

	/*
	   According to PCI REV 2.1 MAX agents on the bus are 21.
	   We don't bother scanning ourselves (slot 0).
	 */
	for (pci_slot = 1; pci_slot < 22; pci_slot++) {

		device.devfn = PCI_DEVFN(pci_slot, 0);
		id = pci0ReadConfigReg(PCI_VENDOR_ID, &device);

		/*  Check for a PCI Master Abort (nothing responds in the slot) */
		GT_READ(GT_INTRCAUSE_OFS, &c18RegValue);
		/* Clearing bit 18 of in the Cause Register 0xc18 by writting 0. */
		GT_WRITE(GT_INTRCAUSE_OFS, (c18RegValue & 0xfffbffff));
		if ((id != 0xffffffff) && !(c18RegValue & 0x40000)) {
			DBG(KERN_INFO "rr: found device %x, slot %d\n", id,
			    pci_slot);
			pci_devices[arrayCounter].slot = pci_slot;
			for (bar = 0; bar < 6; bar++) {
				memType =
				    pci0ReadConfigReg(PCI_BASE_ADDRESS_0 +
						      (bar * 4), &device);
				pci_devices[arrayCounter].BARtype[bar] =
				    memType & 1;
				pci0WriteConfigReg(PCI_BASE_ADDRESS_0 +
						   (bar * 4), &device,
						   0xffffffff);
				memSize =
				    pci0ReadConfigReg(PCI_BASE_ADDRESS_0 +
						      (bar * 4), &device);
				if (memType & 1) {	/*  IO space  */
					pci_devices[arrayCounter].
					    BARsize[bar] =
					    ~(memSize & 0xfffffffc) + 1;
				} else {	/*  memory space */
					pci_devices[arrayCounter].
					    BARsize[bar] =
					    ~(memSize & 0xfffffff0) + 1;
				}
				DBG(KERN_INFO
				    "rr: BAR %d, type %d, size %x\n", bar,
				    (memType & 1),
				    pci_devices[arrayCounter].
				    BARsize[bar]);
			}	/*  BAR counter  */

			arrayCounter++;
		}
		/*  found a device  */
	}			/*  slot counter  */

	DBG(KERN_INFO "rr: found %d devices\n", arrayCounter);
	if (arrayCounter < MAX_PCI_DEVS) {
		pci_devices[arrayCounter].slot = -1;
	}
	return (arrayCounter);
}

#define ALIGN(val,align)        (((val) + ((align) - 1)) & ~((align) - 1))
#define MAX(val1, val2) ((val1) > (val2) ? (val1) : (val2))

/*
 * This function goes through the list of devices and allocates the BARs in
 * either IO or MEM space.  It does it in order of size, which will limit the
 * amount of fragmentation we have in the IO and MEM spaces.
 */

static void __init allocate_pci_space(struct pci_device *pci_devices)
{
	u32 count, maxcount, bar;
	u32 maxSize, maxDevice, maxBAR;
	u32 alignto;
	u32 base;
	u32 pci0_mem_base = pci0GetMemory0Base();
	u32 pci0_io_base = pci0GetIOspaceBase();
	struct pci_dev device;

	DBG(KERN_INFO "rr: allocate_pci_space\n");

	DBG(KERN_INFO "pci0_io_base %x\n", pci0_io_base);
	DBG(KERN_INFO "pci0_mem_base %x\n", pci0_mem_base);

	/*  How many PCI devices do we have?  */
	maxcount = MAX_PCI_DEVS;
	for (count = 0; count < MAX_PCI_DEVS; count++) {
		if (pci_devices[count].slot == -1) {
			maxcount = count;
			break;
		}
	}

//    DBG(KERN_INFO "Found %d devices\n", maxcount);

	do {
		/*  Find the largest size BAR we need to allocate  */
		maxSize = 0;
		for (count = 0; count < maxcount; count++) {
			for (bar = 0; bar < 6; bar++) {
				if (pci_devices[count].BARsize[bar] >
				    maxSize) {
					maxSize =
					    pci_devices[count].
					    BARsize[bar];
					maxDevice = count;
					maxBAR = bar;
				}
			}
		}

		/*
		   We've found the largest BAR.  Allocate it into IO or
		   mem space.  We don't idiot check the bases to make
		   sure they haven't overflowed the current size for that aperture.

		   Don't bother to enable the device's IO or MEM space here.  That will
		   be done in pci_enable_resources if the device is activated by a driver.
		 */
		if (maxSize) {
			device.devfn =
			    PCI_DEVFN(pci_devices[maxDevice].slot, 0);
			if (pci_devices[maxDevice].BARtype[maxBAR] == 1) {
				alignto = MAX(0x1000, maxSize);
				base = ALIGN(pci0_io_base, alignto);
				pci0WriteConfigReg(PCI_BASE_ADDRESS_0 +
						   (maxBAR * 4), &device,
						   base | 0x1);
				pci0_io_base = base + alignto;
				DBG(KERN_INFO
				    "Device %d BAR %d address %x\n",
				    pci_devices[maxDevice].slot, maxBAR,
				    base);
				DBG(KERN_INFO "New IO base %x\n",
				    pci0_io_base);
			} else {
				alignto = MAX(0x1000, maxSize);
				base = ALIGN(pci0_mem_base, alignto);
				pci0WriteConfigReg(PCI_BASE_ADDRESS_0 +
						   (maxBAR * 4), &device,
						   base);
				pci0_mem_base = base + alignto;
				DBG(KERN_INFO
				    "Device %d BAR %d address %x\n",
				    pci_devices[maxDevice].slot, maxBAR,
				    base);
				DBG(KERN_INFO "New mem base %x\n",
				    pci0_mem_base);
			}
			/*
			   This entry is finished.  Remove it from the list we'll scan.
			 */
			pci_devices[maxDevice].BARsize[maxBAR] = 0;
		}
	} while (maxSize);
}

unsigned __init int pcibios_assign_all_busses(void)
{
	return 1;
}

void __init pcibios_init(void)
{

	u32 tmp;
	struct pci_dev controller;

	controller.devfn = SELF;

	DBG(KERN_INFO "rr: pcibios_init\n");
	GT_READ(GT_PCI0_CMD_OFS, &tmp);
	DBG(KERN_INFO "rr: PCI0 command - %x\n", tmp);
	GT_READ(GT_PCI0_BARE_OFS, &tmp);
	DBG(KERN_INFO "rr: BAR0 - %x\n", tmp);

	/*
	 * You have to enable bus mastering to configure any other
	 * card on the bus.
	 */
	tmp = pci0ReadConfigReg(PCI_COMMAND, &controller);
	DBG(KERN_INFO "rr: command/status - %x\n", tmp);
	tmp |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
	DBG(KERN_INFO "rr: new command/status - %x\n", tmp);
	pci0WriteConfigReg(PCI_COMMAND, &controller, tmp);

	/*  This scans the PCI bus and sets up initial values.  */
	scan_and_initialize_pci();

	/*
	 * Reset PCI I/O and PCI MEM values to ones supported by EVM.
	 */
	ioport_resource.start = 0x10000000;
	ioport_resource.end = 0x11ffffff;	/*  32 MB */
	iomem_resource.start = 0x12000000;
	iomem_resource.end = 0x13ffffff;	/* 32 MB */

	pci_scan_bus(0, &galileo_pci_ops, NULL);

}

char *pcibios_setup(char *str)
{
	printk(KERN_INFO "rr: pcibios_setup\n");
	/* Nothing to do for now.  */

	return str;
}

#endif /* CONFIG_PCI */
