417 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			417 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright 2003 PMC-Sierra
 | 
						|
 * Author: Manish Lachwani (lachwani@pmc-sierra.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/types.h>
 | 
						|
#include <linux/pci.h>
 | 
						|
#include <linux/kernel.h>
 | 
						|
#include <linux/slab.h>
 | 
						|
#include <asm/pci.h>
 | 
						|
#include <asm/io.h>
 | 
						|
 | 
						|
#include <linux/init.h>
 | 
						|
#include <asm/titan_dep.h>
 | 
						|
 | 
						|
#ifdef CONFIG_HYPERTRANSPORT
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * This function check if the Hypertransport Link Initialization completed. If
 | 
						|
 * it did, then proceed further with scanning bus #2
 | 
						|
 */
 | 
						|
static __inline__ int check_titan_htlink(void)
 | 
						|
{
 | 
						|
        u32 val;
 | 
						|
 | 
						|
        val = *(volatile uint32_t *)(RM9000x2_HTLINK_REG);
 | 
						|
        if (val & 0x00000020)
 | 
						|
                /* HT Link Initialization completed */
 | 
						|
                return 1;
 | 
						|
        else
 | 
						|
                return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int titan_ht_config_read_dword(struct pci_dev *device,
 | 
						|
                                             int offset, u32* val)
 | 
						|
{
 | 
						|
        int dev, bus, func;
 | 
						|
        uint32_t address_reg, data_reg;
 | 
						|
        uint32_t address;
 | 
						|
 | 
						|
        bus = device->bus->number;
 | 
						|
        dev = PCI_SLOT(device->devfn);
 | 
						|
        func = PCI_FUNC(device->devfn);
 | 
						|
 | 
						|
	/* XXX Need to change the Bus # */
 | 
						|
        if (bus > 2)
 | 
						|
                address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) |
 | 
						|
                                                        0x80000000 | 0x1;
 | 
						|
        else
 | 
						|
                address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000;
 | 
						|
 | 
						|
        address_reg = RM9000x2_OCD_HTCFGA;
 | 
						|
        data_reg =  RM9000x2_OCD_HTCFGD;
 | 
						|
 | 
						|
        RM9K_WRITE(address_reg, address);
 | 
						|
        RM9K_READ(data_reg, val);
 | 
						|
 | 
						|
        return PCIBIOS_SUCCESSFUL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int titan_ht_config_read_word(struct pci_dev *device,
 | 
						|
                                             int offset, u16* val)
 | 
						|
{
 | 
						|
        int dev, bus, func;
 | 
						|
        uint32_t address_reg, data_reg;
 | 
						|
        uint32_t address;
 | 
						|
 | 
						|
        bus = device->bus->number;
 | 
						|
        dev = PCI_SLOT(device->devfn);
 | 
						|
        func = PCI_FUNC(device->devfn);
 | 
						|
 | 
						|
	/* XXX Need to change the Bus # */
 | 
						|
        if (bus > 2)
 | 
						|
                address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) |
 | 
						|
                                                0x80000000 | 0x1;
 | 
						|
        else
 | 
						|
                address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000;
 | 
						|
 | 
						|
        address_reg = RM9000x2_OCD_HTCFGA;
 | 
						|
        data_reg =  RM9000x2_OCD_HTCFGD;
 | 
						|
 | 
						|
        if ((offset & 0x3) == 0)
 | 
						|
                offset = 0x2;
 | 
						|
        else
 | 
						|
                offset = 0x0;
 | 
						|
 | 
						|
        RM9K_WRITE(address_reg, address);
 | 
						|
        RM9K_READ_16(data_reg + offset, val);
 | 
						|
 | 
						|
        return PCIBIOS_SUCCESSFUL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
u32 longswap(unsigned long l)
 | 
						|
{
 | 
						|
        unsigned char b1, b2, b3, b4;
 | 
						|
 | 
						|
        b1 = l&255;
 | 
						|
        b2 = (l>>8)&255;
 | 
						|
        b3 = (l>>16)&255;
 | 
						|
        b4 = (l>>24)&255;
 | 
						|
 | 
						|
        return ((b1<<24) + (b2<<16) + (b3<<8) + b4);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int titan_ht_config_read_byte(struct pci_dev *device,
 | 
						|
                                             int offset, u8* val)
 | 
						|
{
 | 
						|
        int dev, bus, func;
 | 
						|
        uint32_t address_reg, data_reg;
 | 
						|
        uint32_t address;
 | 
						|
        int offset1;
 | 
						|
 | 
						|
        bus = device->bus->number;
 | 
						|
        dev = PCI_SLOT(device->devfn);
 | 
						|
        func = PCI_FUNC(device->devfn);
 | 
						|
 | 
						|
	/* XXX Need to change the Bus # */
 | 
						|
        if (bus > 2)
 | 
						|
                address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) |
 | 
						|
                                                        0x80000000 | 0x1;
 | 
						|
        else
 | 
						|
                address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000;
 | 
						|
 | 
						|
        address_reg = RM9000x2_OCD_HTCFGA;
 | 
						|
        data_reg =  RM9000x2_OCD_HTCFGD;
 | 
						|
 | 
						|
        RM9K_WRITE(address_reg, address);
 | 
						|
 | 
						|
        if ((offset & 0x3) == 0) {
 | 
						|
                offset1 = 0x3;
 | 
						|
        }
 | 
						|
        if ((offset & 0x3) == 1) {
 | 
						|
                offset1 = 0x2;
 | 
						|
        }
 | 
						|
        if ((offset & 0x3) == 2) {
 | 
						|
                offset1 = 0x1;
 | 
						|
        }
 | 
						|
        if ((offset & 0x3) == 3) {
 | 
						|
                offset1 = 0x0;
 | 
						|
        }
 | 
						|
        RM9K_READ_8(data_reg + offset1, val);
 | 
						|
 | 
						|
        return PCIBIOS_SUCCESSFUL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int titan_ht_config_write_dword(struct pci_dev *device,
 | 
						|
                                             int offset, u8 val)
 | 
						|
{
 | 
						|
        int dev, bus, func;
 | 
						|
        uint32_t address_reg, data_reg;
 | 
						|
        uint32_t address;
 | 
						|
 | 
						|
        bus = device->bus->number;
 | 
						|
        dev = PCI_SLOT(device->devfn);
 | 
						|
        func = PCI_FUNC(device->devfn);
 | 
						|
 | 
						|
	/* XXX Need to change the Bus # */
 | 
						|
        if (bus > 2)
 | 
						|
                address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) |
 | 
						|
                                                        0x80000000 | 0x1;
 | 
						|
        else
 | 
						|
              address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000;
 | 
						|
 | 
						|
        address_reg = RM9000x2_OCD_HTCFGA;
 | 
						|
        data_reg =  RM9000x2_OCD_HTCFGD;
 | 
						|
 | 
						|
        RM9K_WRITE(address_reg, address);
 | 
						|
        RM9K_WRITE(data_reg, val);
 | 
						|
 | 
						|
        return PCIBIOS_SUCCESSFUL;
 | 
						|
}
 | 
						|
 | 
						|
static int titan_ht_config_write_word(struct pci_dev *device,
 | 
						|
                                             int offset, u8 val)
 | 
						|
{
 | 
						|
        int dev, bus, func;
 | 
						|
        uint32_t address_reg, data_reg;
 | 
						|
        uint32_t address;
 | 
						|
 | 
						|
        bus = device->bus->number;
 | 
						|
        dev = PCI_SLOT(device->devfn);
 | 
						|
        func = PCI_FUNC(device->devfn);
 | 
						|
 | 
						|
	/* XXX Need to change the Bus # */
 | 
						|
        if (bus > 2)
 | 
						|
                address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) |
 | 
						|
                                0x80000000 | 0x1;
 | 
						|
        else
 | 
						|
                address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000;
 | 
						|
 | 
						|
        address_reg = RM9000x2_OCD_HTCFGA;
 | 
						|
        data_reg =  RM9000x2_OCD_HTCFGD;
 | 
						|
 | 
						|
        if ((offset & 0x3) == 0)
 | 
						|
                offset = 0x2;
 | 
						|
        else
 | 
						|
                offset = 0x0;
 | 
						|
 | 
						|
        RM9K_WRITE(address_reg, address);
 | 
						|
        RM9K_WRITE_16(data_reg + offset, val);
 | 
						|
 | 
						|
        return PCIBIOS_SUCCESSFUL;
 | 
						|
}
 | 
						|
 | 
						|
static int titan_ht_config_write_byte(struct pci_dev *device,
 | 
						|
                                             int offset, u8 val)
 | 
						|
{
 | 
						|
        int dev, bus, func;
 | 
						|
        uint32_t address_reg, data_reg;
 | 
						|
        uint32_t address;
 | 
						|
        int offset1;
 | 
						|
 | 
						|
        bus = device->bus->number;
 | 
						|
        dev = PCI_SLOT(device->devfn);
 | 
						|
        func = PCI_FUNC(device->devfn);
 | 
						|
 | 
						|
	/* XXX Need to change the Bus # */
 | 
						|
        if (bus > 2)
 | 
						|
                address = (bus << 16) | (dev << 11) | (func << 8) | (offset & 0xfc) |
 | 
						|
                                0x80000000 | 0x1;
 | 
						|
        else
 | 
						|
                address = (dev << 11) | (func << 8) | (offset & 0xfc) | 0x80000000;
 | 
						|
 | 
						|
        address_reg = RM9000x2_OCD_HTCFGA;
 | 
						|
        data_reg =  RM9000x2_OCD_HTCFGD;
 | 
						|
 | 
						|
        RM9K_WRITE(address_reg, address);
 | 
						|
 | 
						|
        if ((offset & 0x3) == 0) {
 | 
						|
             offset1 = 0x3;
 | 
						|
        }
 | 
						|
        if ((offset & 0x3) == 1) {
 | 
						|
             offset1 = 0x2;
 | 
						|
        }
 | 
						|
        if ((offset & 0x3) == 2) {
 | 
						|
             offset1 = 0x1;
 | 
						|
        }
 | 
						|
        if ((offset & 0x3) == 3) {
 | 
						|
            offset1 = 0x0;
 | 
						|
        }
 | 
						|
 | 
						|
        RM9K_WRITE_8(data_reg + offset1, val);
 | 
						|
        return PCIBIOS_SUCCESSFUL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void titan_pcibios_set_master(struct pci_dev *dev)
 | 
						|
{
 | 
						|
        u16 cmd;
 | 
						|
        int bus = dev->bus->number;
 | 
						|
 | 
						|
	if (check_titan_htlink())
 | 
						|
            titan_ht_config_read_word(dev, PCI_COMMAND, &cmd);
 | 
						|
 | 
						|
	cmd |= PCI_COMMAND_MASTER;
 | 
						|
 | 
						|
	if (check_titan_htlink())
 | 
						|
            titan_ht_config_write_word(dev, PCI_COMMAND, cmd);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int pcibios_enable_resources(struct pci_dev *dev)
 | 
						|
{
 | 
						|
        u16 cmd, old_cmd;
 | 
						|
        u8 tmp1;
 | 
						|
        int idx;
 | 
						|
        struct resource *r;
 | 
						|
        int bus = dev->bus->number;
 | 
						|
 | 
						|
	if (check_titan_htlink())
 | 
						|
            titan_ht_config_read_word(dev, PCI_COMMAND, &cmd);
 | 
						|
 | 
						|
	old_cmd = cmd;
 | 
						|
        for (idx = 0; idx < 6; idx++) {
 | 
						|
                r = &dev->resource[idx];
 | 
						|
                if (!r->start && r->end) {
 | 
						|
                        printk(KERN_ERR
 | 
						|
                               "PCI: Device %s not available because of "
 | 
						|
                               "resource collisions\n", pci_name(dev));
 | 
						|
                        return -EINVAL;
 | 
						|
                }
 | 
						|
                if (r->flags & IORESOURCE_IO)
 | 
						|
                        cmd |= PCI_COMMAND_IO;
 | 
						|
                if (r->flags & IORESOURCE_MEM)
 | 
						|
                        cmd |= PCI_COMMAND_MEMORY;
 | 
						|
        }
 | 
						|
        if (cmd != old_cmd) {
 | 
						|
		if (check_titan_htlink())
 | 
						|
                   titan_ht_config_write_word(dev, PCI_COMMAND, cmd);
 | 
						|
	}
 | 
						|
 | 
						|
	if (check_titan_htlink())
 | 
						|
		titan_ht_config_read_byte(dev, PCI_CACHE_LINE_SIZE, &tmp1);
 | 
						|
 | 
						|
	if (tmp1 != 8) {
 | 
						|
                printk(KERN_WARNING "PCI setting cache line size to 8 from "
 | 
						|
                       "%d\n", tmp1);
 | 
						|
	}
 | 
						|
 | 
						|
	if (check_titan_htlink())
 | 
						|
		titan_ht_config_write_byte(dev, PCI_CACHE_LINE_SIZE, 8);
 | 
						|
 | 
						|
	if (check_titan_htlink())
 | 
						|
		titan_ht_config_read_byte(dev, PCI_LATENCY_TIMER, &tmp1);
 | 
						|
 | 
						|
	if (tmp1 < 32 || tmp1 == 0xff) {
 | 
						|
                printk(KERN_WARNING "PCI setting latency timer to 32 from %d\n",
 | 
						|
                       tmp1);
 | 
						|
	}
 | 
						|
 | 
						|
	if (check_titan_htlink())
 | 
						|
		titan_ht_config_write_byte(dev, PCI_LATENCY_TIMER, 32);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int pcibios_enable_device(struct pci_dev *dev, int mask)
 | 
						|
{
 | 
						|
        return pcibios_enable_resources(dev);
 | 
						|
}
 | 
						|
 | 
						|
void pcibios_align_resource(void *data, struct resource *res,
 | 
						|
                            resource_size_t size, resource_size_t align)
 | 
						|
{
 | 
						|
        struct pci_dev *dev = data;
 | 
						|
 | 
						|
        if (res->flags & IORESOURCE_IO) {
 | 
						|
                resource_size_t 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) {
 | 
						|
                        printk(KERN_ERR "PCI: I/O Region %s/%d too large"
 | 
						|
                               " (%ld bytes)\n", pci_name(dev),
 | 
						|
                                dev->resource - res, size);
 | 
						|
                }
 | 
						|
 | 
						|
                start = (start + 1024 - 1) & ~(1024 - 1);
 | 
						|
                res->start = start;
 | 
						|
        }
 | 
						|
}
 | 
						|
 | 
						|
struct pci_ops titan_pci_ops = {
 | 
						|
        titan_ht_config_read_byte,
 | 
						|
        titan_ht_config_read_word,
 | 
						|
        titan_ht_config_read_dword,
 | 
						|
        titan_ht_config_write_byte,
 | 
						|
        titan_ht_config_write_word,
 | 
						|
        titan_ht_config_write_dword
 | 
						|
};
 | 
						|
 | 
						|
void __init pcibios_fixup_bus(struct pci_bus *c)
 | 
						|
{
 | 
						|
        titan_ht_pcibios_fixup_bus(c);
 | 
						|
}
 | 
						|
 | 
						|
void __init pcibios_init(void)
 | 
						|
{
 | 
						|
 | 
						|
        /* Reset PCI I/O and PCI MEM values */
 | 
						|
	/* XXX Need to add the proper values here */
 | 
						|
        ioport_resource.start = 0xe0000000;
 | 
						|
        ioport_resource.end   = 0xe0000000 + 0x20000000 - 1;
 | 
						|
        iomem_resource.start  = 0xc0000000;
 | 
						|
        iomem_resource.end    = 0xc0000000 + 0x20000000 - 1;
 | 
						|
 | 
						|
	/* XXX Need to add bus values */
 | 
						|
        pci_scan_bus(2, &titan_pci_ops, NULL);
 | 
						|
        pci_scan_bus(3, &titan_pci_ops, NULL);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * for parsing "pci=" kernel boot arguments.
 | 
						|
 */
 | 
						|
char *pcibios_setup(char *str)
 | 
						|
{
 | 
						|
        printk(KERN_INFO "rr: pcibios_setup\n");
 | 
						|
        /* Nothing to do for now.  */
 | 
						|
 | 
						|
        return str;
 | 
						|
}
 | 
						|
 | 
						|
unsigned __init int pcibios_assign_all_busses(void)
 | 
						|
{
 | 
						|
        /* We want to use the PCI bus detection done by PMON */
 | 
						|
        return 0;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* CONFIG_HYPERTRANSPORT */
 |