diff options
Diffstat (limited to 'arch/mips/cobalt/pci.c')
-rw-r--r-- | arch/mips/cobalt/pci.c | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/arch/mips/cobalt/pci.c b/arch/mips/cobalt/pci.c new file mode 100644 index 000000000..a325ad7b9 --- /dev/null +++ b/arch/mips/cobalt/pci.c @@ -0,0 +1,348 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Cobalt Qube specific PCI support. + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <asm/cobalt.h> +#include <asm/pci.h> +#include <asm/io.h> + +#undef PCI_DEBUG + +#ifdef CONFIG_PCI + +static void qube_expansion_slot_bist(void) +{ + unsigned char ctrl; + int timeout = 100000; + + pcibios_read_config_byte(0, (0x0a<<3), PCI_BIST, &ctrl); + if(!(ctrl & PCI_BIST_CAPABLE)) + return; + + pcibios_write_config_byte(0, (0x0a<<3), PCI_BIST, ctrl|PCI_BIST_START); + do { + pcibios_read_config_byte(0, (0x0a<<3), PCI_BIST, &ctrl); + if(!(ctrl & PCI_BIST_START)) + break; + } while(--timeout > 0); + if((timeout <= 0) || (ctrl & PCI_BIST_CODE_MASK)) + printk("PCI: Expansion slot card failed BIST with code %x\n", + (ctrl & PCI_BIST_CODE_MASK)); +} + +static void qube_expansion_slot_fixup(void) +{ + unsigned short pci_cmd; + unsigned long ioaddr_base = 0x10108000; /* It's magic, ask Doug. */ + unsigned long memaddr_base = 0x12000000; + int i; + + /* Enable bits in COMMAND so driver can talk to it. */ + pcibios_read_config_word(0, (0x0a<<3), PCI_COMMAND, &pci_cmd); + pci_cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pcibios_write_config_word(0, (0x0a<<3), PCI_COMMAND, pci_cmd); + + /* Give it a working IRQ. */ + pcibios_write_config_byte(0, (0x0a<<3), PCI_INTERRUPT_LINE, 9); + + /* Fixup base addresses, we only support I/O at the moment. */ + for(i = 0; i <= 5; i++) { + unsigned int regaddr = (PCI_BASE_ADDRESS_0 + (i * 4)); + unsigned int rval, mask, size, alignme, aspace; + unsigned long *basep = &ioaddr_base; + + /* Check type first, punt if non-IO. */ + pcibios_read_config_dword(0, (0x0a<<3), regaddr, &rval); + aspace = (rval & PCI_BASE_ADDRESS_SPACE); + if(aspace != PCI_BASE_ADDRESS_SPACE_IO) + basep = &memaddr_base; + + /* Figure out how much it wants, if anything. */ + pcibios_write_config_dword(0, (0x0a<<3), regaddr, 0xffffffff); + pcibios_read_config_dword(0, (0x0a<<3), regaddr, &rval); + + /* Unused? */ + if(rval == 0) + continue; + + rval &= PCI_BASE_ADDRESS_IO_MASK; + mask = (~rval << 1) | 0x1; + size = (mask & rval) & 0xffffffff; + alignme = size; + if(alignme < 0x400) + alignme = 0x400; + rval = ((*basep + (alignme - 1)) & ~(alignme - 1)); + *basep = (rval + size); + pcibios_write_config_dword(0, (0x0a<<3), regaddr, rval | aspace); + } + qube_expansion_slot_bist(); +} + +static void qube_raq_tulip_fixup(void) +{ + unsigned short pci_cmd; + + /* Enable the device. */ + pcibios_read_config_word(0, (0x0c<<3), PCI_COMMAND, &pci_cmd); + pci_cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MASTER); + pcibios_write_config_word(0, (0x0c<<3), PCI_COMMAND, pci_cmd); + + /* Give it it's IRQ. */ + /* NOTE: RaQ board #1 has a bunch of green wires which swapped the + * IRQ line values of Tulip 0 and Tulip 1. All other + * boards have eth0=4,eth1=13. -DaveM + */ +#ifndef RAQ_BOARD_1_WITH_HWHACKS + pcibios_write_config_byte(0, (0x0c<<3), PCI_INTERRUPT_LINE, 13); +#else + pcibios_write_config_byte(0, (0x0c<<3), PCI_INTERRUPT_LINE, 4); +#endif + + /* And finally, a usable I/O space allocation, right after what + * the first Tulip uses. + */ + pcibios_write_config_dword(0, (0x0c<<3), PCI_BASE_ADDRESS_0, 0x10101001); +} + +static void qube_raq_scsi_fixup(void) +{ + unsigned short pci_cmd; + + /* Enable the device. */ + pcibios_read_config_word(0, (0x08<<3), PCI_COMMAND, &pci_cmd); + + pci_cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY + | PCI_COMMAND_INVALIDATE); + pcibios_write_config_word(0, (0x08<<3), PCI_COMMAND, pci_cmd); + + /* Give it it's IRQ. */ + pcibios_write_config_byte(0, (0x08<<3), PCI_INTERRUPT_LINE, 4); + + /* And finally, a usable I/O space allocation, right after what + * the second Tulip uses. + */ + pcibios_write_config_dword(0, (0x08<<3), PCI_BASE_ADDRESS_0, 0x10102001); + pcibios_write_config_dword(0, (0x08<<3), PCI_BASE_ADDRESS_1, 0x00002000); + pcibios_write_config_dword(0, (0x08<<3), PCI_BASE_ADDRESS_2, 0x00100000); +} + +static unsigned long +qube_pcibios_fixup(unsigned long mem_start, unsigned long mem_end) +{ + extern int cobalt_is_raq; + int raq_p = cobalt_is_raq; + unsigned int tmp; + + /* Fixup I/O and Memory space decoding on Galileo. */ + isa_slot_offset = COBALT_LOCAL_IO_SPACE; + + /* Fix PCI latency-timer and cache-line-size values in Galileo + * host bridge. + */ + pcibios_write_config_byte(0, 0, PCI_LATENCY_TIMER, 64); + pcibios_write_config_byte(0, 0, PCI_CACHE_LINE_SIZE, 7); + + /* + * Now tell the SCSI device that we expect an interrupt at + * IRQ 7 and not the default 0. + */ + pcibios_write_config_byte(0, 0x08<<3, PCI_INTERRUPT_LINE, + COBALT_SCSI_IRQ); + + /* + * Now tell the Ethernet device that we expect an interrupt at + * IRQ 13 and not the default 189. + * + * The IRQ of the first Tulip is different on Qube and RaQ + * hardware except for the weird first RaQ bringup board, + * see above for details. -DaveM + */ + if (! raq_p) { + /* All Qube's route this the same way. */ + pcibios_write_config_byte(0, 0x07<<3, PCI_INTERRUPT_LINE, + COBALT_ETHERNET_IRQ); + } else { +#ifndef RAQ_BOARD_1_WITH_HWHACKS + pcibios_write_config_byte(0, (0x07<<3), PCI_INTERRUPT_LINE, 4); +#else + pcibios_write_config_byte(0, (0x07<<3), PCI_INTERRUPT_LINE, 13); +#endif + } + + if (! raq_p) { + /* See if there is a device in the expansion slot, if so + * fixup IRQ, fix base addresses, and enable master + + * I/O + memory accesses in config space. + */ + pcibios_read_config_dword(0, 0x0a<<3, PCI_VENDOR_ID, &tmp); + if(tmp != 0xffffffff && tmp != 0x00000000) + qube_expansion_slot_fixup(); + } else { + /* If this is a RAQ, we may need to setup the second Tulip + * and SCSI as well. Due to the different configurations + * a RaQ can have, we need to explicitly check for the + * presence of each of these (optional) devices. -DaveM + */ + pcibios_read_config_dword(0, 0x0c<<3, PCI_VENDOR_ID, &tmp); + if(tmp != 0xffffffff && tmp != 0x00000000) + qube_raq_tulip_fixup(); + + pcibios_read_config_dword(0, 0x08<<3, PCI_VENDOR_ID, &tmp); + if(tmp != 0xffffffff && tmp != 0x00000000) + qube_raq_scsi_fixup(); + + /* And if we are a 2800 we have to setup the expansion slot + * too. + */ + pcibios_read_config_dword(0, 0x0a<<3, PCI_VENDOR_ID, &tmp); + if(tmp != 0xffffffff && tmp != 0x00000000) + qube_expansion_slot_fixup(); + } + + return mem_start; +} + +static __inline__ int pci_range_ck(unsigned char bus, unsigned char dev) +{ + if ((bus == 0) && ( (dev==0) || ((dev>6) && (dev <= 12))) ) + return 0; /* OK device number */ + + return -1; /* NOT ok device number */ +} + +#define PCI_CFG_DATA ((volatile unsigned long *)0xb4000cfc) +#define PCI_CFG_CTRL ((volatile unsigned long *)0xb4000cf8) + +#define PCI_CFG_SET(dev,fun,off) \ + ((*PCI_CFG_CTRL) = (0x80000000 | ((dev)<<11) | \ + ((fun)<<8) | (off))) + +static int qube_pcibios_read_config_dword (unsigned char bus, + unsigned char dev, + unsigned char offset, + unsigned int *val) +{ + unsigned char fun = dev & 0x07; + + dev >>= 3; + if (offset & 0x3) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (pci_range_ck(bus, dev)) { + *val = 0xFFFFFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } + PCI_CFG_SET(dev, fun, offset); + *val = *PCI_CFG_DATA; + return PCIBIOS_SUCCESSFUL; +} + +static int qube_pcibios_read_config_word (unsigned char bus, + unsigned char dev, + unsigned char offset, + unsigned short *val) +{ + unsigned char fun = dev & 0x07; + + dev >>= 3; + if (offset & 0x1) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (pci_range_ck(bus, dev)) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + PCI_CFG_SET(dev, fun, (offset & ~0x3)); + *val = *PCI_CFG_DATA >> ((offset & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + +static int qube_pcibios_read_config_byte (unsigned char bus, + unsigned char dev, + unsigned char offset, + unsigned char *val) +{ + unsigned char fun = dev & 0x07; + + dev >>= 3; + if (pci_range_ck(bus, dev)) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + PCI_CFG_SET(dev, fun, (offset & ~0x3)); + *val = *PCI_CFG_DATA >> ((offset & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + +static int qube_pcibios_write_config_dword (unsigned char bus, + unsigned char dev, + unsigned char offset, + unsigned int val) +{ + unsigned char fun = dev & 0x07; + + dev >>= 3; + if(offset & 0x3) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (pci_range_ck(bus, dev)) + return PCIBIOS_DEVICE_NOT_FOUND; + PCI_CFG_SET(dev, fun, offset); + *PCI_CFG_DATA = val; + return PCIBIOS_SUCCESSFUL; +} + +static int +qube_pcibios_write_config_word (unsigned char bus, unsigned char dev, + unsigned char offset, unsigned short val) +{ + unsigned char fun = dev & 0x07; + unsigned long tmp; + + dev >>= 3; + if (offset & 0x1) + return PCIBIOS_BAD_REGISTER_NUMBER; + if (pci_range_ck(bus, dev)) + return PCIBIOS_DEVICE_NOT_FOUND; + PCI_CFG_SET(dev, fun, (offset & ~0x3)); + tmp = *PCI_CFG_DATA; + tmp &= ~(0xffff << ((offset & 0x3) * 8)); + tmp |= (val << ((offset & 0x3) * 8)); + *PCI_CFG_DATA = tmp; + return PCIBIOS_SUCCESSFUL; +} + +static int +qube_pcibios_write_config_byte (unsigned char bus, unsigned char dev, + unsigned char offset, unsigned char val) +{ + unsigned char fun = dev & 0x07; + unsigned long tmp; + + dev >>= 3; + if (pci_range_ck(bus, dev)) + return PCIBIOS_DEVICE_NOT_FOUND; + PCI_CFG_SET(dev, fun, (offset & ~0x3)); + tmp = *PCI_CFG_DATA; + tmp &= ~(0xff << ((offset & 0x3) * 8)); + tmp |= (val << ((offset & 0x3) * 8)); + *PCI_CFG_DATA = tmp; + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops qube_pci_ops = { + qube_pcibios_fixup, + qube_pcibios_read_config_byte, + qube_pcibios_read_config_word, + qube_pcibios_read_config_dword, + qube_pcibios_write_config_byte, + qube_pcibios_write_config_word, + qube_pcibios_write_config_dword +}; + +#endif /* CONFIG_PCI */ |