diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/pci |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/mips/pci')
62 files changed, 8147 insertions, 0 deletions
diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile new file mode 100644 index 00000000000..c53e4cb359b --- /dev/null +++ b/arch/mips/pci/Makefile @@ -0,0 +1,54 @@ +# +# Makefile for the PCI specific kernel interface routines under Linux. +# + +obj-y += pci.o + +# +# PCI bus host bridge specific code +# +obj-$(CONFIG_ITE_BOARD_GEN) += ops-it8172.o +obj-$(CONFIG_MIPS_BONITO64) += ops-bonito64.o +obj-$(CONFIG_MIPS_GT64111) += ops-gt64111.o +obj-$(CONFIG_MIPS_GT64120) += ops-gt64120.o +obj-$(CONFIG_MIPS_GT96100) += ops-gt96100.o +obj-$(CONFIG_PCI_MARVELL) += ops-marvell.o +obj-$(CONFIG_MIPS_MSC) += ops-msc.o +obj-$(CONFIG_MIPS_NILE4) += ops-nile4.o +obj-$(CONFIG_MIPS_TX3927) += ops-jmr3927.o +obj-$(CONFIG_PCI_VR41XX) += ops-vr41xx.o pci-vr41xx.o +obj-$(CONFIG_NEC_CMBVR4133) += fixup-vr4133.o + +# +# These are still pretty much in the old state, watch, go blind. +# +obj-$(CONFIG_DDB5074) += fixup-ddb5074.o pci-ddb5074.o ops-ddb5074.o +obj-$(CONFIG_DDB5476) += ops-ddb5476.o pci-ddb5476.o +obj-$(CONFIG_DDB5477) += fixup-ddb5477.o pci-ddb5477.o ops-ddb5477.o +obj-$(CONFIG_LASAT) += pci-lasat.o +obj-$(CONFIG_MIPS_ATLAS) += fixup-atlas.o +obj-$(CONFIG_MIPS_COBALT) += fixup-cobalt.o +obj-$(CONFIG_MIPS_EV96100) += fixup-ev64120.o +obj-$(CONFIG_MIPS_EV96100) += fixup-ev96100.o pci-ev96100.o +obj-$(CONFIG_MIPS_ITE8172) += fixup-ite8172g.o +obj-$(CONFIG_MIPS_IVR) += fixup-ivr.o +obj-$(CONFIG_SOC_AU1500) += fixup-au1000.o ops-au1000.o +obj-$(CONFIG_SOC_AU1550) += fixup-au1000.o ops-au1000.o +obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o +obj-$(CONFIG_MOMENCO_JAGUAR_ATX)+= fixup-jaguar.o +obj-$(CONFIG_MOMENCO_OCELOT) += fixup-ocelot.o pci-ocelot.o +obj-$(CONFIG_MOMENCO_OCELOT_3) += fixup-ocelot3.o +obj-$(CONFIG_MOMENCO_OCELOT_C) += fixup-ocelot-c.o pci-ocelot-c.o +obj-$(CONFIG_MOMENCO_OCELOT_G) += fixup-ocelot-g.o pci-ocelot-g.o +obj-$(CONFIG_PMC_YOSEMITE) += fixup-yosemite.o ops-titan.o ops-titan-ht.o \ + pci-yosemite.o +obj-$(CONFIG_SGI_IP27) += pci-ip27.o +obj-$(CONFIG_SGI_IP32) += fixup-ip32.o ops-mace.o pci-ip32.o +obj-$(CONFIG_SIBYTE_SB1250) += fixup-sb1250.o pci-sb1250.o +obj-$(CONFIG_SNI_RM200_PCI) += fixup-sni.o ops-sni.o +obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o +obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o +obj-$(CONFIG_TOSHIBA_JMR3927) += fixup-jmr3927.o pci-jmr3927.o +obj-$(CONFIG_TOSHIBA_RBTX4927) += fixup-rbtx4927.o ops-tx4927.o +obj-$(CONFIG_VICTOR_MPC30X) += fixup-mpc30x.o +obj-$(CONFIG_ZAO_CAPCELLA) += fixup-capcella.o diff --git a/arch/mips/pci/fixup-atlas.c b/arch/mips/pci/fixup-atlas.c new file mode 100644 index 00000000000..2406835833d --- /dev/null +++ b/arch/mips/pci/fixup-atlas.c @@ -0,0 +1,69 @@ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/mips-boards/atlasint.h> + +#define INTD ATLASINT_INTD +#define INTC ATLASINT_INTC +#define INTB ATLASINT_INTB +#define INTA ATLASINT_INTA +#define SCSI ATLASINT_SCSI +#define ETH ATLASINT_ETH + +static char irq_tab[][5] __initdata = { + /* INTA INTB INTC INTD */ + {0, 0, 0, 0, 0 }, /* 0: Unused */ + {0, 0, 0, 0, 0 }, /* 1: Unused */ + {0, 0, 0, 0, 0 }, /* 2: Unused */ + {0, 0, 0, 0, 0 }, /* 3: Unused */ + {0, 0, 0, 0, 0 }, /* 4: Unused */ + {0, 0, 0, 0, 0 }, /* 5: Unused */ + {0, 0, 0, 0, 0 }, /* 6: Unused */ + {0, 0, 0, 0, 0 }, /* 7: Unused */ + {0, 0, 0, 0, 0 }, /* 8: Unused */ + {0, 0, 0, 0, 0 }, /* 9: Unused */ + {0, 0, 0, 0, 0 }, /* 10: Unused */ + {0, 0, 0, 0, 0 }, /* 11: Unused */ + {0, 0, 0, 0, 0 }, /* 12: Unused */ + {0, 0, 0, 0, 0 }, /* 13: Unused */ + {0, 0, 0, 0, 0 }, /* 14: Unused */ + {0, 0, 0, 0, 0 }, /* 15: Unused */ + {0, SCSI, 0, 0, 0 }, /* 16: SYM53C810A SCSI */ + {0, 0, 0, 0, 0 }, /* 17: Core */ + {0, INTA, INTB, INTC, INTD }, /* 18: PCI Slot 1 */ + {0, ETH, 0, 0, 0 }, /* 19: SAA9730 Ethernet */ + {0, 0, 0, 0, 0 }, /* 20: PCI Slot 3 */ + {0, 0, 0, 0, 0 } /* 21: PCI Slot 4 */ +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_tab[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +#ifdef CONFIG_KGDB +/* + * The PCI scan may have moved the saa9730 I/O address, so reread + * the address here. + * This does mean that it's not possible to debug the PCI bus configuration + * code, but it is better than nothing... + */ + +static void atlas_saa9730_base_fixup (struct pci_dev *pdev) +{ + extern void *saa9730_base; + if (pdev->bus == 0 && PCI_SLOT(pdev->devfn) == 19) + (void) pci_read_config_dword (pdev, 0x14, (u32 *)&saa9730_base); + printk ("saa9730_base = %x\n", saa9730_base); +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA9730, + atlas_saa9730_base_fixup); + +#endif diff --git a/arch/mips/pci/fixup-au1000.c b/arch/mips/pci/fixup-au1000.c new file mode 100644 index 00000000000..39fe2b16fce --- /dev/null +++ b/arch/mips/pci/fixup-au1000.c @@ -0,0 +1,123 @@ +/* + * BRIEF MODULE DESCRIPTION + * Board specific pci fixups. + * + * Copyright 2001-2003 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.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/init.h> + +#include <asm/mach-au1x00/au1000.h> + +/* + * Shortcut + */ +#ifdef CONFIG_SOC_AU1500 +#define INTA AU1000_PCI_INTA +#define INTB AU1000_PCI_INTB +#define INTC AU1000_PCI_INTC +#define INTD AU1000_PCI_INTD +#endif + +#ifdef CONFIG_SOC_AU1550 +#define INTA AU1550_PCI_INTA +#define INTB AU1550_PCI_INTB +#define INTC AU1550_PCI_INTC +#define INTD AU1550_PCI_INTD +#endif + +#define INTX 0xFF /* not valid */ + +#ifdef CONFIG_MIPS_DB1500 +static char irq_tab_alchemy[][5] __initdata = { + [12] = { -1, INTA, INTX, INTX, INTX}, /* IDSEL 12 - HPT371 */ + [13] = { -1, INTA, INTB, INTC, INTD}, /* IDSEL 13 - PCI slot */ +}; +#endif + +#ifdef CONFIG_MIPS_BOSPORUS +static char irq_tab_alchemy[][5] __initdata = { + [11] = { -1, INTA, INTB, INTX, INTX}, /* IDSEL 11 - miniPCI */ + [12] = { -1, INTA, INTX, INTX, INTX}, /* IDSEL 12 - SN1741 */ + [13] = { -1, INTA, INTB, INTC, INTD}, /* IDSEL 13 - PCI slot */ +}; +#endif + +#ifdef CONFIG_MIPS_MIRAGE +static char irq_tab_alchemy[][5] __initdata = { + [11] = { -1, INTD, INTX, INTX, INTX}, /* IDSEL 11 - SMI VGX */ + [12] = { -1, INTX, INTX, INTC, INTX}, /* IDSEL 12 - PNX1300 */ + [13] = { -1, INTA, INTB, INTX, INTX}, /* IDSEL 13 - miniPCI */ +}; +#endif + +#ifdef CONFIG_MIPS_DB1550 +static char irq_tab_alchemy[][5] __initdata = { + [11] = { -1, INTC, INTX, INTX, INTX}, /* IDSEL 11 - on-board HPT371 */ + [12] = { -1, INTB, INTC, INTD, INTA}, /* IDSEL 12 - PCI slot 2 (left) */ + [13] = { -1, INTA, INTB, INTC, INTD}, /* IDSEL 13 - PCI slot 1 (right) */ +}; +#endif + +#ifdef CONFIG_MIPS_PB1500 +static char irq_tab_alchemy[][5] __initdata = { + [12] = { -1, INTA, INTX, INTX, INTX}, /* IDSEL 12 - HPT370 */ + [13] = { -1, INTA, INTB, INTC, INTD}, /* IDSEL 13 - PCI slot */ +}; +#endif + +#ifdef CONFIG_MIPS_PB1550 +static char irq_tab_alchemy[][5] __initdata = { + [12] = { -1, INTB, INTC, INTD, INTA}, /* IDSEL 12 - PCI slot 2 (left) */ + [13] = { -1, INTA, INTB, INTC, INTD}, /* IDSEL 13 - PCI slot 1 (right) */ +}; +#endif + +#ifdef CONFIG_MIPS_MTX1 +static char irq_tab_alchemy[][5] __initdata = { + [0] = { -1, INTA, INTB, INTX, INTX}, /* IDSEL 00 - AdapterA-Slot0 (top) */ + [1] = { -1, INTB, INTA, INTX, INTX}, /* IDSEL 01 - AdapterA-Slot1 (bottom) */ + [2] = { -1, INTC, INTD, INTX, INTX}, /* IDSEL 02 - AdapterB-Slot0 (top) */ + [3] = { -1, INTD, INTC, INTX, INTX}, /* IDSEL 03 - AdapterB-Slot1 (bottom) */ + [4] = { -1, INTA, INTB, INTX, INTX}, /* IDSEL 04 - AdapterC-Slot0 (top) */ + [5] = { -1, INTB, INTA, INTX, INTX}, /* IDSEL 05 - AdapterC-Slot1 (bottom) */ + [6] = { -1, INTC, INTD, INTX, INTX}, /* IDSEL 06 - AdapterD-Slot0 (top) */ + [7] = { -1, INTD, INTC, INTX, INTX}, /* IDSEL 07 - AdapterD-Slot1 (bottom) */ +}; +#endif + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_tab_alchemy[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-capcella.c b/arch/mips/pci/fixup-capcella.c new file mode 100644 index 00000000000..f2fc82c1c7c --- /dev/null +++ b/arch/mips/pci/fixup-capcella.c @@ -0,0 +1,50 @@ +/* + * fixup-cappcela.c, The ZAO Networks Capcella specific PCI fixups. + * + * Copyright (C) 2002,2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/vr41xx/capcella.h> + +/* + * Shortcuts + */ +#define INT1 RTL8139_1_IRQ +#define INT2 RTL8139_2_IRQ +#define INTA PC104PLUS_INTA_IRQ +#define INTB PC104PLUS_INTB_IRQ +#define INTC PC104PLUS_INTC_IRQ +#define INTD PC104PLUS_INTD_IRQ + +static char irq_tab_capcella[][5] __initdata = { + [11] = { -1, INT1, INT1, INT1, INT1 }, + [12] = { -1, INT2, INT2, INT2, INT2 }, + [14] = { -1, INTA, INTB, INTC, INTD } +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_tab_capcella[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-cobalt.c b/arch/mips/pci/fixup-cobalt.c new file mode 100644 index 00000000000..57e1ca2116b --- /dev/null +++ b/arch/mips/pci/fixup-cobalt.c @@ -0,0 +1,112 @@ +/* + * Cobalt Qube/Raq PCI support + * + * 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. + * + * Copyright (C) 1995, 1996, 1997, 2002, 2003 by Ralf Baechle + * Copyright (C) 2001, 2002, 2003 by Liam Davies (ldavies@agile.tv) + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/pci.h> +#include <asm/io.h> +#include <asm/gt64120.h> + +#include <asm/cobalt/cobalt.h> + +extern int cobalt_board_id; + +static void qube_raq_via_bmIDE_fixup(struct pci_dev *dev) +{ + unsigned short cfgword; + unsigned char lt; + + /* Enable Bus Mastering and fast back to back. */ + pci_read_config_word(dev, PCI_COMMAND, &cfgword); + cfgword |= (PCI_COMMAND_FAST_BACK | PCI_COMMAND_MASTER); + pci_write_config_word(dev, PCI_COMMAND, cfgword); + + /* Enable both ide interfaces. ROM only enables primary one. */ + pci_write_config_byte(dev, 0x40, 0xb); + + /* Set latency timer to reasonable value. */ + pci_read_config_byte(dev, PCI_LATENCY_TIMER, <); + if (lt < 64) + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 7); +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1, + qube_raq_via_bmIDE_fixup); + +static void qube_raq_galileo_fixup(struct pci_dev *dev) +{ + unsigned short galileo_id; + + /* Fix PCI latency-timer and cache-line-size values in Galileo + * host bridge. + */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 7); + + /* + * On all machines prior to Q2, we had the STOP line disconnected + * from Galileo to VIA on PCI. The new Galileo does not function + * correctly unless we have it connected. + * + * Therefore we must set the disconnect/retry cycle values to + * something sensible when using the new Galileo. + */ + pci_read_config_word(dev, PCI_REVISION_ID, &galileo_id); + galileo_id &= 0xff; /* mask off class info */ + if (galileo_id >= 0x10) { + /* New Galileo, assumes PCI stop line to VIA is connected. */ + GALILEO_OUTL(0x4020, GT_PCI0_TOR_OFS); + } else if (galileo_id == 0x1 || galileo_id == 0x2) { + signed int timeo; + /* XXX WE MUST DO THIS ELSE GALILEO LOCKS UP! -DaveM */ + timeo = GALILEO_INL(GT_PCI0_TOR_OFS); + /* Old Galileo, assumes PCI STOP line to VIA is disconnected. */ + GALILEO_OUTL(0xffff, GT_PCI0_TOR_OFS); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_GALILEO, PCI_ANY_ID, + qube_raq_galileo_fixup); + +static char irq_tab_cobalt[] __initdata = { + [COBALT_PCICONF_CPU] = 0, + [COBALT_PCICONF_ETH0] = COBALT_ETH0_IRQ, + [COBALT_PCICONF_RAQSCSI] = COBALT_SCSI_IRQ, + [COBALT_PCICONF_VIA] = 0, + [COBALT_PCICONF_PCISLOT] = COBALT_QUBE_SLOT_IRQ, + [COBALT_PCICONF_ETH1] = COBALT_ETH1_IRQ +}; + +static char irq_tab_raq2[] __initdata = { + [COBALT_PCICONF_CPU] = 0, + [COBALT_PCICONF_ETH0] = COBALT_ETH0_IRQ, + [COBALT_PCICONF_RAQSCSI] = COBALT_RAQ_SCSI_IRQ, + [COBALT_PCICONF_VIA] = 0, + [COBALT_PCICONF_PCISLOT] = COBALT_QUBE_SLOT_IRQ, + [COBALT_PCICONF_ETH1] = COBALT_ETH1_IRQ +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (cobalt_board_id == COBALT_BRD_ID_RAQ2) + return irq_tab_raq2[slot]; + + return irq_tab_cobalt[slot]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-ddb5074.c b/arch/mips/pci/fixup-ddb5074.c new file mode 100644 index 00000000000..b345e528a53 --- /dev/null +++ b/arch/mips/pci/fixup-ddb5074.c @@ -0,0 +1,21 @@ +/* + * It's nice to have the LEDs on the GPIO pins available for debugging + */ +static void ddb5074_fixup(struct pci_dev *dev) +{ + extern struct pci_dev *pci_pmu; + u8 t8; + + pci_pmu = dev; /* for LEDs D2 and D3 */ + /* Program the lines for LEDs D2 and D3 to output */ + pci_read_config_byte(dev, 0x7d, &t8); + t8 |= 0xc0; + pci_write_config_byte(dev, 0x7d, t8); + /* Turn LEDs D2 and D3 off */ + pci_read_config_byte(dev, 0x7e, &t8); + t8 |= 0xc0; + pci_write_config_byte(dev, 0x7e, t8); +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, + ddb5074_fixup); diff --git a/arch/mips/pci/fixup-ddb5477.c b/arch/mips/pci/fixup-ddb5477.c new file mode 100644 index 00000000000..6abdc88bab1 --- /dev/null +++ b/arch/mips/pci/fixup-ddb5477.c @@ -0,0 +1,78 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Board specific pci fixups. + * + * Copyright 2001, 2002, 2003 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 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/init.h> + +static void ddb5477_fixup(struct pci_dev *dev) +{ + u8 old; + + printk(KERN_NOTICE "Enabling ALI M1533/35 PS2 keyboard/mouse.\n"); + pci_read_config_byte(dev, 0x41, &old); + pci_write_config_byte(dev, 0x41, old | 0xd0); +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, + ddb5477_fixup); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1535, + ddb5477_fixup); + +/* + * Fixup baseboard AMD chip so that tx does not underflow. + * bcr_18 |= 0x0800 + * This sets NOUFLO bit which makes tx not start until whole pkt + * is fetched to the chip. + */ +#define PCNET32_WIO_RDP 0x10 +#define PCNET32_WIO_RAP 0x12 +#define PCNET32_WIO_RESET 0x14 +#define PCNET32_WIO_BDP 0x16 + +static void ddb5477_amd_lance_fixup(struct pci_dev *dev) +{ + unsigned long ioaddr; + u16 temp; + + ioaddr = pci_resource_start(dev, 0); + + inw(ioaddr + PCNET32_WIO_RESET); /* reset chip */ + + /* bcr_18 |= 0x0800 */ + outw(18, ioaddr + PCNET32_WIO_RAP); + temp = inw(ioaddr + PCNET32_WIO_BDP); + temp |= 0x0800; + outw(18, ioaddr + PCNET32_WIO_RAP); + outw(temp, ioaddr + PCNET32_WIO_BDP); +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, + ddb5477_amd_lance_fixup); diff --git a/arch/mips/pci/fixup-ev64120.c b/arch/mips/pci/fixup-ev64120.c new file mode 100644 index 00000000000..8dbb90d63f0 --- /dev/null +++ b/arch/mips/pci/fixup-ev64120.c @@ -0,0 +1,34 @@ +#include <linux/pci.h> +#include <linux/init.h> + +int pci_range_ck(unsigned char bus, unsigned char dev) +{ + if (((bus == 0) || (bus == 1)) && (dev >= 6) && (dev <= 8)) + return 0; + + return -1; +} + +/* + * 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. + */ +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ + unsigned int irq = 20; + struct pci_bus *current_bus = bus; + struct pci_dev *dev; + struct list_head *devices_link; + + list_for_each(devices_link, &(current_bus->devices)) { + dev = pci_dev_b(devices_link); + if (dev != NULL) { + dev->irq = irq++; + + /* Assign an interrupt number for the device */ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); + pcibios_set_master(dev); + } + } +} diff --git a/arch/mips/pci/fixup-ev96100.c b/arch/mips/pci/fixup-ev96100.c new file mode 100644 index 00000000000..e2bc977b6d5 --- /dev/null +++ b/arch/mips/pci/fixup-ev96100.c @@ -0,0 +1,48 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * EV96100 Board specific pci fixups. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.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/init.h> +#include <linux/types.h> +#include <linux/pci.h> + +static char irq_tab_ev96100[][5] __initdata = { + [8] = { 0, 5, 5, 5, 5 }, + [9] = { 0, 2, 2, 2, 2 } +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_tab_ev96100[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-ip32.c b/arch/mips/pci/fixup-ip32.c new file mode 100644 index 00000000000..3e66b0aa63c --- /dev/null +++ b/arch/mips/pci/fixup-ip32.c @@ -0,0 +1,51 @@ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <asm/ip32/ip32_ints.h> +/* + * O2 has up to 5 PCI devices connected into the MACE bridge. The device + * map looks like this: + * + * 0 aic7xxx 0 + * 1 aic7xxx 1 + * 2 expansion slot + * 3 N/C + * 4 N/C + */ + +#define SCSI0 MACEPCI_SCSI0_IRQ +#define SCSI1 MACEPCI_SCSI1_IRQ +#define INTA0 MACEPCI_SLOT0_IRQ +#define INTA1 MACEPCI_SLOT1_IRQ +#define INTA2 MACEPCI_SLOT2_IRQ +#define INTB MACEPCI_SHARED0_IRQ +#define INTC MACEPCI_SHARED1_IRQ +#define INTD MACEPCI_SHARED2_IRQ +static char irq_tab_mace[][5] __initdata = { + /* Dummy INT#A INT#B INT#C INT#D */ + {0, 0, 0, 0, 0}, /* This is placeholder row - never used */ + {0, SCSI0, SCSI0, SCSI0, SCSI0}, + {0, SCSI1, SCSI1, SCSI1, SCSI1}, + {0, INTA0, INTB, INTC, INTD}, + {0, INTA1, INTC, INTD, INTB}, + {0, INTA2, INTD, INTB, INTC}, +}; + + +/* + * Given a PCI slot number (a la PCI_SLOT(...)) and the interrupt pin of + * the device (1-4 => A-D), tell what irq to use. Note that we don't + * in theory have slots 4 and 5, and we never normally use the shared + * irqs. I suppose a device without a pin A will thank us for doing it + * right if there exists such a broken piece of crap. + */ +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_tab_mace[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-ite8172g.c b/arch/mips/pci/fixup-ite8172g.c new file mode 100644 index 00000000000..2290ea4228d --- /dev/null +++ b/arch/mips/pci/fixup-ite8172g.c @@ -0,0 +1,80 @@ +/* + * BRIEF MODULE DESCRIPTION + * Board specific pci fixups. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.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/init.h> + +#include <asm/it8172/it8172.h> +#include <asm/it8172/it8172_pci.h> +#include <asm/it8172/it8172_int.h> + +/* + * Shortcuts + */ +#define INTA IT8172_PCI_INTA_IRQ +#define INTB IT8172_PCI_INTB_IRQ +#define INTC IT8172_PCI_INTC_IRQ +#define INTD IT8172_PCI_INTD_IRQ + +static const int internal_func_irqs[7] __initdata = { + IT8172_AC97_IRQ, + IT8172_DMA_IRQ, + IT8172_CDMA_IRQ, + IT8172_USB_IRQ, + IT8172_BRIDGE_MASTER_IRQ, + IT8172_IDE_IRQ, + IT8172_MC68K_IRQ +}; + +static char irq_tab_ite8172g[][5] __initdata = { + [0x10] = { 0, INTA, INTB, INTC, INTD }, + [0x11] = { 0, INTA, INTB, INTC, INTD }, + [0x12] = { 0, INTB, INTC, INTD, INTA }, + [0x13] = { 0, INTC, INTD, INTA, INTB }, + [0x14] = { 0, INTD, INTA, INTB, INTC }, +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + /* + * Internal device 1 is actually 7 different internal devices on the + * IT8172G (a multifunction device). + */ + if (slot == 1) + return internal_func_irqs[PCI_FUNC(dev->devfn)]; + + return irq_tab_ite8172g[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-ivr.c b/arch/mips/pci/fixup-ivr.c new file mode 100644 index 00000000000..0c7c16464c1 --- /dev/null +++ b/arch/mips/pci/fixup-ivr.c @@ -0,0 +1,75 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Globespan IVR board-specific pci fixups. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.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/init.h> + +#include <asm/it8172/it8172.h> +#include <asm/it8172/it8172_pci.h> +#include <asm/it8172/it8172_int.h> + +/* + * Shortcuts + */ +#define INTA IT8172_PCI_INTA_IRQ +#define INTB IT8172_PCI_INTB_IRQ +#define INTC IT8172_PCI_INTC_IRQ +#define INTD IT8172_PCI_INTD_IRQ + +static const int internal_func_irqs[7] __initdata = { + IT8172_AC97_IRQ, + IT8172_DMA_IRQ, + IT8172_CDMA_IRQ, + IT8172_USB_IRQ, + IT8172_BRIDGE_MASTER_IRQ, + IT8172_IDE_IRQ, + IT8172_MC68K_IRQ +}; + +static char irq_tab_ivr[][5] __initdata = { + [0x11] = { INTC, INTC, INTD, INTA, INTB }, /* Realtek RTL-8139 */ + [0x12] = { INTB, INTB, INTB, INTC, INTC }, /* IVR slot */ + [0x13] = { INTA, INTA, INTB, INTC, INTD } /* Expansion slot */ +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (slot == 1) + return internal_func_irqs[PCI_FUNC(dev->devfn)]; + + return irq_tab_ivr[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-jaguar.c b/arch/mips/pci/fixup-jaguar.c new file mode 100644 index 00000000000..6c5e1d47179 --- /dev/null +++ b/arch/mips/pci/fixup-jaguar.c @@ -0,0 +1,43 @@ +/* + * 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. + * + * Marvell MV64340 interrupt fixup code. + * + * Marvell wants an NDA for their docs so this was written without + * documentation. You've been warned. + * + * Copyright (C) 2004 Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/mipsregs.h> + +/* + * WARNING: Example of how _NOT_ to do it. + */ +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int bus = dev->bus->number; + + if (bus == 0 && slot == 1) + return 3; /* PCI-X A */ + if (bus == 0 && slot == 2) + return 4; /* PCI-X B */ + if (bus == 1 && slot == 1) + return 5; /* PCI A */ + if (bus == 1 && slot == 2) + return 6; /* PCI B */ + +return 0; + panic("Whooops in pcibios_map_irq"); +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-jmr3927.c b/arch/mips/pci/fixup-jmr3927.c new file mode 100644 index 00000000000..f8696081c5b --- /dev/null +++ b/arch/mips/pci/fixup-jmr3927.c @@ -0,0 +1,105 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Board specific pci fixups. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.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/init.h> + +#include <asm/jmr3927/jmr3927.h> + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + unsigned char irq = pin; + + /* IRQ rotation (PICMG) */ + irq--; /* 0-3 */ + if (dev->bus->parent == NULL && + slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(23)) { + /* PCI CardSlot (IDSEL=A23, DevNu=12) */ + /* PCIA => PCIC (IDSEL=A23) */ + /* NOTE: JMR3927 JP1 must be set to OPEN */ + irq = (irq + 2) % 4; + } else if (dev->bus->parent == NULL && + slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(22)) { + /* PCI CardSlot (IDSEL=A22, DevNu=11) */ + /* PCIA => PCIA (IDSEL=A22) */ + /* NOTE: JMR3927 JP1 must be set to OPEN */ + irq = (irq + 0) % 4; + } else { + /* PCI Backplane */ + irq = (irq + 3 + slot) % 4; + } + irq++; /* 1-4 */ + + switch (irq) { + case 1: + irq = JMR3927_IRQ_IOC_PCIA; + break; + case 2: + // wrong for backplane irq = JMR3927_IRQ_IOC_PCIB; + irq = JMR3927_IRQ_IOC_PCID; + break; + case 3: + irq = JMR3927_IRQ_IOC_PCIC; + break; + case 4: + // wrong for backplane irq = JMR3927_IRQ_IOC_PCID; + irq = JMR3927_IRQ_IOC_PCIB; + break; + } + + /* Check OnBoard Ethernet (IDSEL=A24, DevNu=13) */ + if (dev->bus->parent == NULL && + slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(24)) { + extern int jmr3927_ether1_irq; + /* check this irq line was reserved for ether1 */ + if (jmr3927_ether1_irq != JMR3927_IRQ_ETHER0) + irq = JMR3927_IRQ_ETHER0; + else + irq = 0; /* disable */ + } + return irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + /* SMSC SLC90E66 IDE uses irq 14, 15 (default) */ + if (!(dev->vendor == PCI_VENDOR_ID_EFAR && + dev->device == PCI_DEVICE_ID_EFAR_SLC90E66_1)) + return pci_get_irq(dev, pin); + + dev->irq = irq; +} diff --git a/arch/mips/pci/fixup-malta.c b/arch/mips/pci/fixup-malta.c new file mode 100644 index 00000000000..b9296d9942b --- /dev/null +++ b/arch/mips/pci/fixup-malta.c @@ -0,0 +1,103 @@ +#include <linux/init.h> +#include <linux/pci.h> + +/* PCI interrupt pins */ +#define PCIA 1 +#define PCIB 2 +#define PCIC 3 +#define PCID 4 + +/* This table is filled in by interrogating the PIIX4 chip */ +static char pci_irq[5] __initdata; + +static char irq_tab[][5] __initdata = { + /* INTA INTB INTC INTD */ + {0, 0, 0, 0, 0 }, /* 0: GT64120 PCI bridge */ + {0, 0, 0, 0, 0 }, /* 1: Unused */ + {0, 0, 0, 0, 0 }, /* 2: Unused */ + {0, 0, 0, 0, 0 }, /* 3: Unused */ + {0, 0, 0, 0, 0 }, /* 4: Unused */ + {0, 0, 0, 0, 0 }, /* 5: Unused */ + {0, 0, 0, 0, 0 }, /* 6: Unused */ + {0, 0, 0, 0, 0 }, /* 7: Unused */ + {0, 0, 0, 0, 0 }, /* 8: Unused */ + {0, 0, 0, 0, 0 }, /* 9: Unused */ + {0, 0, 0, 0, PCID }, /* 10: PIIX4 USB */ + {0, PCIB, 0, 0, 0 }, /* 11: AMD 79C973 Ethernet */ + {0, PCIC, 0, 0, 0 }, /* 12: Crystal 4281 Sound */ + {0, 0, 0, 0, 0 }, /* 13: Unused */ + {0, 0, 0, 0, 0 }, /* 14: Unused */ + {0, 0, 0, 0, 0 }, /* 15: Unused */ + {0, 0, 0, 0, 0 }, /* 16: Unused */ + {0, 0, 0, 0, 0 }, /* 17: Bonito/SOC-it PCI Bridge*/ + {0, PCIA, PCIB, PCIC, PCID }, /* 18: PCI Slot 1 */ + {0, PCIB, PCIC, PCID, PCIA }, /* 19: PCI Slot 2 */ + {0, PCIC, PCID, PCIA, PCIB }, /* 20: PCI Slot 3 */ + {0, PCID, PCIA, PCIB, PCIC } /* 21: PCI Slot 4 */ +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int virq; + virq = irq_tab[slot][pin]; + return pci_irq[virq]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +static void __init malta_piix_func0_fixup(struct pci_dev *pdev) +{ + unsigned char reg_val; + static int piixirqmap[16] __initdata = { /* PIIX PIRQC[A:D] irq mappings */ + 0, 0, 0, 3, + 4, 5, 6, 7, + 0, 9, 10, 11, + 12, 0, 14, 15 + }; + int i; + + /* Interrogate PIIX4 to get PCI IRQ mapping */ + for (i = 0; i <= 3; i++) { + pci_read_config_byte(pdev, 0x60+i, ®_val); + if (reg_val & 0x80) + pci_irq[PCIA+i] = 0; /* Disabled */ + else + pci_irq[PCIA+i] = piixirqmap[reg_val & 15]; + } + + /* Done by YAMON 2.00 onwards */ + if (PCI_SLOT(pdev->devfn) == 10) { + /* + * Set top of main memory accessible by ISA or DMA + * devices to 16 Mb. + */ + pci_read_config_byte(pdev, 0x69, ®_val); + pci_write_config_byte(pdev, 0x69, reg_val | 0xf0); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, + malta_piix_func0_fixup); + +static void __init malta_piix_func1_fixup(struct pci_dev *pdev) +{ + unsigned char reg_val; + + /* Done by YAMON 2.02 onwards */ + if (PCI_SLOT(pdev->devfn) == 10) { + /* + * IDE Decode enable. + */ + pci_read_config_byte(pdev, 0x41, ®_val); + pci_write_config_byte(pdev, 0x41, reg_val|0x80); + pci_read_config_byte(pdev, 0x43, ®_val); + pci_write_config_byte(pdev, 0x43, reg_val|0x80); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, + malta_piix_func1_fixup); diff --git a/arch/mips/pci/fixup-mpc30x.c b/arch/mips/pci/fixup-mpc30x.c new file mode 100644 index 00000000000..4975846da75 --- /dev/null +++ b/arch/mips/pci/fixup-mpc30x.c @@ -0,0 +1,50 @@ +/* + * fixup-mpc30x.c, The Victor MP-C303/304 specific PCI fixups. + * + * Copyright (C) 2002,2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/vr41xx/mpc30x.h> +#include <asm/vr41xx/vrc4173.h> + +static const int internal_func_irqs[] __initdata = { + VRC4173_CASCADE_IRQ, + VRC4173_AC97_IRQ, + VRC4173_USB_IRQ, +}; + +static const int irq_tab_mpc30x[] __initdata = { + [12] = VRC4173_PCMCIA1_IRQ, + [13] = VRC4173_PCMCIA2_IRQ, + [29] = MQ200_IRQ, +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (slot == 30) + return internal_func_irqs[PCI_FUNC(dev->devfn)]; + + return irq_tab_mpc30x[slot]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-ocelot-c.c b/arch/mips/pci/fixup-ocelot-c.c new file mode 100644 index 00000000000..d45494807a3 --- /dev/null +++ b/arch/mips/pci/fixup-ocelot-c.c @@ -0,0 +1,41 @@ +/* + * Copyright 2002 Momentum Computer Inc. + * Author: Matthew Dharm <mdharm@momenco.com> + * + * Based on work for the Linux port to the Ocelot board, which is + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/momentum/ocelot_g/pci.c + * Board-specific PCI routines for mv64340 controller. + * + * 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. + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int bus = dev->bus->number; + + if (bus == 0 && slot == 1) + return 2; /* PCI-X A */ + if (bus == 1 && slot == 1) + return 12; /* PCI-X B */ + if (bus == 1 && slot == 2) + return 4; /* PCI B */ + +return 0; + panic("Whooops in pcibios_map_irq"); +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-ocelot-g.c b/arch/mips/pci/fixup-ocelot-g.c new file mode 100644 index 00000000000..d7a652e326c --- /dev/null +++ b/arch/mips/pci/fixup-ocelot-g.c @@ -0,0 +1,37 @@ +/* + * 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. + * + * Copyright (C) 2004 Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int bus = dev->bus->number; + + if (bus == 0 && slot == 1) /* Intel 82543 Gigabit MAC */ + return 2; /* irq_nr is 2 for INT0 */ + + if (bus == 0 && slot == 2) /* Intel 82543 Gigabit MAC */ + return 3; /* irq_nr is 3 for INT1 */ + + if (bus == 1 && slot == 3) /* Intel 21555 bridge */ + return 5; /* irq_nr is 8 for INT6 */ + + if (bus == 1 && slot == 4) /* PMC Slot */ + return 9; /* irq_nr is 9 for INT7 */ + + return -1; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-ocelot.c b/arch/mips/pci/fixup-ocelot.c new file mode 100644 index 00000000000..99629bd047c --- /dev/null +++ b/arch/mips/pci/fixup-ocelot.c @@ -0,0 +1,75 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/gt64120/momenco_ocelot/pci.c + * Board-specific PCI routines for gt64120 controller. + * + * 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. + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/pci.h> + + +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_bus *current_bus = bus; + struct pci_dev *devices; + struct list_head *devices_link; + u16 cmd; + + list_for_each(devices_link, &(current_bus->devices)) { + + devices = pci_dev_b(devices_link); + if (devices == NULL) + continue; + + if (PCI_SLOT(devices->devfn) == 1) { + /* + * Slot 1 is primary ether port, i82559 + * we double-check against that assumption + */ + if ((devices->vendor != 0x8086) || + (devices->device != 0x1209)) { + panic("pcibios_fixup_bus: found " + "unexpected PCI device in slot 1."); + } + devices->irq = 2; /* irq_nr is 2 for INT0 */ + } else if (PCI_SLOT(devices->devfn) == 2) { + /* + * Slot 2 is secondary ether port, i21143 + * we double-check against that assumption + */ + if ((devices->vendor != 0x1011) || + (devices->device != 0x19)) { + panic("galileo_pcibios_fixup_bus: " + "found unexpected PCI device in slot 2."); + } + devices->irq = 3; /* irq_nr is 3 for INT1 */ + } else if (PCI_SLOT(devices->devfn) == 4) { + /* PMC Slot 1 */ + devices->irq = 8; /* irq_nr is 8 for INT6 */ + } else if (PCI_SLOT(devices->devfn) == 5) { + /* PMC Slot 1 */ + devices->irq = 9; /* irq_nr is 9 for INT7 */ + } else { + /* We don't have assign interrupts for other devices. */ + devices->irq = 0xff; + } + + /* Assign an interrupt number for the device */ + bus->ops->write_byte(devices, PCI_INTERRUPT_LINE, + devices->irq); + + /* enable master */ + bus->ops->read_word(devices, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER; + bus->ops->write_word(devices, PCI_COMMAND, cmd); + } +} diff --git a/arch/mips/pci/fixup-ocelot3.c b/arch/mips/pci/fixup-ocelot3.c new file mode 100644 index 00000000000..ececc03ec62 --- /dev/null +++ b/arch/mips/pci/fixup-ocelot3.c @@ -0,0 +1,41 @@ +/* + * 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. + * + * Copyright (C) 2004 Montavista Software Inc. + * Author: Manish Lachwani (mlachwani@mvista.com) + * + * Looking at the schematics for the Ocelot-3 board, there are + * two PCI busses and each bus has two PCI slots. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/mipsregs.h> + +/* + * Do platform specific device initialization at + * pci_enable_device() time + */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int bus = dev->bus->number; + + if (bus == 0 && slot == 1) + return 2; /* PCI-X A */ + if (bus == 0 && slot == 2) + return 3; /* PCI-X B */ + if (bus == 1 && slot == 1) + return 4; /* PCI A */ + if (bus == 1 && slot == 2) + return 5; /* PCI B */ + +return 0; + panic("Whooops in pcibios_map_irq"); +} diff --git a/arch/mips/pci/fixup-rbtx4927.c b/arch/mips/pci/fixup-rbtx4927.c new file mode 100644 index 00000000000..de4e443da20 --- /dev/null +++ b/arch/mips/pci/fixup-rbtx4927.c @@ -0,0 +1,140 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Board specific pci fixups for the Toshiba rbtx4927 + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * Copyright (C) 2000-2001 Toshiba Corporation + * + * Copyright (C) 2004 MontaVista Software Inc. + * Author: Manish Lachwani (mlachwani@mvista.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/init.h> + +#include <asm/tx4927/tx4927.h> +#include <asm/tx4927/tx4927_pci.h> + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* look up table for backplane pci irq for slots 17-20 by pin # */ +static unsigned char backplane_pci_irq[4][4] = { + /* PJ6 SLOT: 17, PIN: 1 */ {TX4927_IRQ_IOC_PCIA, + /* PJ6 SLOT: 17, PIN: 2 */ + TX4927_IRQ_IOC_PCIB, + /* PJ6 SLOT: 17, PIN: 3 */ + TX4927_IRQ_IOC_PCIC, + /* PJ6 SLOT: 17, PIN: 4 */ + TX4927_IRQ_IOC_PCID}, + /* SB SLOT: 18, PIN: 1 */ {TX4927_IRQ_IOC_PCIB, + /* SB SLOT: 18, PIN: 2 */ + TX4927_IRQ_IOC_PCIC, + /* SB SLOT: 18, PIN: 3 */ + TX4927_IRQ_IOC_PCID, + /* SB SLOT: 18, PIN: 4 */ + TX4927_IRQ_IOC_PCIA}, + /* PJ5 SLOT: 19, PIN: 1 */ {TX4927_IRQ_IOC_PCIC, + /* PJ5 SLOT: 19, PIN: 2 */ + TX4927_IRQ_IOC_PCID, + /* PJ5 SLOT: 19, PIN: 3 */ + TX4927_IRQ_IOC_PCIA, + /* PJ5 SLOT: 19, PIN: 4 */ + TX4927_IRQ_IOC_PCIB}, + /* PJ4 SLOT: 20, PIN: 1 */ {TX4927_IRQ_IOC_PCID, + /* PJ4 SLOT: 20, PIN: 2 */ + TX4927_IRQ_IOC_PCIA, + /* PJ4 SLOT: 20, PIN: 3 */ + TX4927_IRQ_IOC_PCIB, + /* PJ4 SLOT: 20, PIN: 4 */ + TX4927_IRQ_IOC_PCIC} +}; + +int pci_get_irq(struct pci_dev *dev, int pin) +{ + unsigned char irq = pin; + + DBG("pci_get_irq: pin is %d\n", pin); + /* IRQ rotation */ + irq--; /* 0-3 */ + if (dev->bus->parent == NULL && + PCI_SLOT(dev->devfn) == TX4927_PCIC_IDSEL_AD_TO_SLOT(23)) { + printk("Onboard PCI_SLOT(dev->devfn) is %d\n", + PCI_SLOT(dev->devfn)); + /* IDSEL=A23 is tx4927 onboard pci slot */ + irq = (irq + PCI_SLOT(dev->devfn)) % 4; + irq++; /* 1-4 */ + DBG("irq is now %d\n", irq); + + switch (irq) { + case 1: + irq = TX4927_IRQ_IOC_PCIA; + break; + case 2: + irq = TX4927_IRQ_IOC_PCIB; + break; + case 3: + irq = TX4927_IRQ_IOC_PCIC; + break; + case 4: + irq = TX4927_IRQ_IOC_PCID; + break; + } + } else { + /* PCI Backplane */ + DBG("PCI Backplane PCI_SLOT(dev->devfn) is %d\n", + PCI_SLOT(dev->devfn)); + irq = backplane_pci_irq[PCI_SLOT(dev->devfn) - 17][irq]; + } + DBG("assigned irq %d\n", irq); + return irq; +} + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + unsigned char irq; + + printk("PCI Setup for pin %d \n", pin); + + if (dev->device == 0x9130) /* IDE */ + irq = 14; + else + irq = pci_get_irq(dev, pin); + + return irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-sb1250.c b/arch/mips/pci/fixup-sb1250.c new file mode 100644 index 00000000000..13791b78e59 --- /dev/null +++ b/arch/mips/pci/fixup-sb1250.c @@ -0,0 +1,24 @@ +/* + * arch/mips/pci/fixup-sb1250.c + * + * Copyright (C) 2004 MIPS Technologies, Inc. All rights reserved. + * Author: Maciej W. Rozycki <macro@mips.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. + */ + +#include <linux/init.h> +#include <linux/pci.h> + +/* + * The BCM1250, etc. PCI/HT bridge reports as a host bridge. + */ +static void __init quirk_sb1250_ht(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_HT, + quirk_sb1250_ht); diff --git a/arch/mips/pci/fixup-sni.c b/arch/mips/pci/fixup-sni.c new file mode 100644 index 00000000000..c8ef01a017c --- /dev/null +++ b/arch/mips/pci/fixup-sni.c @@ -0,0 +1,89 @@ +/* + * 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. + * + * SNI specific PCI support for RM200/RM300. + * + * Copyright (C) 1997 - 2000, 2003, 04 Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/mipsregs.h> +#include <asm/sni.h> + +/* + * Shortcuts ... + */ +#define SCSI PCIMT_IRQ_SCSI +#define ETH PCIMT_IRQ_ETHERNET +#define INTA PCIMT_IRQ_INTA +#define INTB PCIMT_IRQ_INTB +#define INTC PCIMT_IRQ_INTC +#define INTD PCIMT_IRQ_INTD + +/* + * Device 0: PCI EISA Bridge (directly routed) + * Device 1: NCR53c810 SCSI (directly routed) + * Device 2: PCnet32 Ethernet (directly routed) + * Device 3: VGA (routed to INTB) + * Device 4: Unused + * Device 5: Slot 2 + * Device 6: Slot 3 + * Device 7: Slot 4 + * + * Documentation says the VGA is device 5 and device 3 is unused but that + * seem to be a documentation error. At least on my RM200C the Cirrus + * Logic CL-GD5434 VGA is device 3. + */ +static char irq_tab_rm200[8][5] __initdata = { + /* INTA INTB INTC INTD */ + { 0, 0, 0, 0, 0 }, /* EISA bridge */ + { SCSI, SCSI, SCSI, SCSI, SCSI }, /* SCSI */ + { ETH, ETH, ETH, ETH, ETH }, /* Ethernet */ + { INTB, INTB, INTB, INTB, INTB }, /* VGA */ + { 0, 0, 0, 0, 0 }, /* Unused */ + { 0, INTB, INTC, INTD, INTA }, /* Slot 2 */ + { 0, INTC, INTD, INTA, INTB }, /* Slot 3 */ + { 0, INTD, INTA, INTB, INTC }, /* Slot 4 */ +}; + +/* + * In Revision D of the RM300 Device 2 has become a normal purpose Slot 1 + * + * The VGA card is optional for RM300 systems. + */ +static char irq_tab_rm300d[8][5] __initdata = { + /* INTA INTB INTC INTD */ + { 0, 0, 0, 0, 0 }, /* EISA bridge */ + { SCSI, SCSI, SCSI, SCSI, SCSI }, /* SCSI */ + { 0, INTC, INTD, INTA, INTB }, /* Slot 1 */ + { INTB, INTB, INTB, INTB, INTB }, /* VGA */ + { 0, 0, 0, 0, 0 }, /* Unused */ + { 0, INTB, INTC, INTD, INTA }, /* Slot 2 */ + { 0, INTC, INTD, INTA, INTB }, /* Slot 3 */ + { 0, INTD, INTA, INTB, INTC }, /* Slot 4 */ +}; + +static inline int is_rm300_revd(void) +{ + unsigned char csmsr = *(volatile unsigned char *)PCIMT_CSMSR; + + return (csmsr & 0xa0) == 0x20; +} + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (is_rm300_revd()) + return irq_tab_rm300d[slot][pin]; + + return irq_tab_rm200[slot][pin]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-tb0219.c b/arch/mips/pci/fixup-tb0219.c new file mode 100644 index 00000000000..850a900f0eb --- /dev/null +++ b/arch/mips/pci/fixup-tb0219.c @@ -0,0 +1,66 @@ +/* + * fixup-tb0219.c, The TANBAC TB0219 specific PCI fixups. + * + * Copyright (C) 2003 Megasolution Inc. <matsu@megasolution.jp> + * Copyright (C) 2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/vr41xx/tb0219.h> + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int irq = -1; + + switch (slot) { + case 12: + vr41xx_set_irq_trigger(TB0219_PCI_SLOT1_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(TB0219_PCI_SLOT1_PIN, + LEVEL_LOW); + irq = TB0219_PCI_SLOT1_IRQ; + break; + case 13: + vr41xx_set_irq_trigger(TB0219_PCI_SLOT2_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(TB0219_PCI_SLOT2_PIN, + LEVEL_LOW); + irq = TB0219_PCI_SLOT2_IRQ; + break; + case 14: + vr41xx_set_irq_trigger(TB0219_PCI_SLOT3_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(TB0219_PCI_SLOT3_PIN, + LEVEL_LOW); + irq = TB0219_PCI_SLOT3_IRQ; + break; + default: + break; + } + + return irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-tb0226.c b/arch/mips/pci/fixup-tb0226.c new file mode 100644 index 00000000000..61513d5d97d --- /dev/null +++ b/arch/mips/pci/fixup-tb0226.c @@ -0,0 +1,85 @@ +/* + * fixup-tb0226.c, The TANBAC TB0226 specific PCI fixups. + * + * Copyright (C) 2002-2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/vr41xx/tb0226.h> + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int irq = -1; + + switch (slot) { + case 12: + vr41xx_set_irq_trigger(GD82559_1_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(GD82559_1_PIN, LEVEL_LOW); + irq = GD82559_1_IRQ; + break; + case 13: + vr41xx_set_irq_trigger(GD82559_2_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(GD82559_2_PIN, LEVEL_LOW); + irq = GD82559_2_IRQ; + break; + case 14: + switch (pin) { + case 1: + vr41xx_set_irq_trigger(UPD720100_INTA_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(UPD720100_INTA_PIN, + LEVEL_LOW); + irq = UPD720100_INTA_IRQ; + break; + case 2: + vr41xx_set_irq_trigger(UPD720100_INTB_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(UPD720100_INTB_PIN, + LEVEL_LOW); + irq = UPD720100_INTB_IRQ; + break; + case 3: + vr41xx_set_irq_trigger(UPD720100_INTC_PIN, + TRIGGER_LEVEL, + SIGNAL_THROUGH); + vr41xx_set_irq_level(UPD720100_INTC_PIN, + LEVEL_LOW); + irq = UPD720100_INTC_IRQ; + break; + default: + break; + } + break; + default: + break; + } + + return irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/fixup-vr4133.c b/arch/mips/pci/fixup-vr4133.c new file mode 100644 index 00000000000..03a0ff2fc99 --- /dev/null +++ b/arch/mips/pci/fixup-vr4133.c @@ -0,0 +1,204 @@ +/* + * arch/mips/vr41xx/nec-cmbvr4133/pci_fixup.c + * + * The NEC CMB-VR4133 Board specific PCI fixups. + * + * Author: Yoichi Yuasa <yyuasa@mvista.com, or source@mvista.com> and + * Alex Sapkov <asapkov@ru.mvista.com> + * + * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * Modified for support in 2.6 + * Author: Manish Lachwani (mlachwani@mvista.com) + * + */ +#include <linux/config.h> +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/io.h> +#include <asm/vr41xx/cmbvr4133.h> + +extern int vr4133_rockhopper; +extern void ali_m1535plus_init(struct pci_dev *dev); +extern void ali_m5229_init(struct pci_dev *dev); + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + /* + * We have to reset AMD PCnet adapter on Rockhopper since + * PMON leaves it enabled and generating interrupts. This leads + * to a lock if some PCI device driver later enables the IRQ line + * shared with PCnet and there is no AMD PCnet driver to catch its + * interrupts. + */ +#ifdef CONFIG_ROCKHOPPER + if (dev->vendor == PCI_VENDOR_ID_AMD && + dev->device == PCI_DEVICE_ID_AMD_LANCE) { + inl(pci_resource_start(dev, 0) + 0x18); + } +#endif + + /* + * we have to open the bridges' windows down to 0 because otherwise + * we cannot access ISA south bridge I/O registers that get mapped from + * 0. for example, 8259 PIC would be unaccessible without that + */ + if(dev->vendor == PCI_VENDOR_ID_INTEL && dev->device == PCI_DEVICE_ID_INTEL_S21152BB) { + pci_write_config_byte(dev, PCI_IO_BASE, 0); + if(dev->bus->number == 0) { + pci_write_config_word(dev, PCI_IO_BASE_UPPER16, 0); + } else { + pci_write_config_word(dev, PCI_IO_BASE_UPPER16, 1); + } + } + + return 0; +} + +/* + * M1535 IRQ mapping + * Feel free to change this, although it shouldn't be needed + */ +#define M1535_IRQ_INTA 7 +#define M1535_IRQ_INTB 9 +#define M1535_IRQ_INTC 10 +#define M1535_IRQ_INTD 11 + +#define M1535_IRQ_USB 9 +#define M1535_IRQ_IDE 14 +#define M1535_IRQ_IDE2 15 +#define M1535_IRQ_PS2 12 +#define M1535_IRQ_RTC 8 +#define M1535_IRQ_FDC 6 +#define M1535_IRQ_AUDIO 5 +#define M1535_IRQ_COM1 4 +#define M1535_IRQ_COM2 4 +#define M1535_IRQ_IRDA 3 +#define M1535_IRQ_KBD 1 +#define M1535_IRQ_TMR 0 + +/* Rockhopper "slots" assignment; this is hard-coded ... */ +#define ROCKHOPPER_M5451_SLOT 1 +#define ROCKHOPPER_M1535_SLOT 2 +#define ROCKHOPPER_M5229_SLOT 11 +#define ROCKHOPPER_M5237_SLOT 15 +#define ROCKHOPPER_PMU_SLOT 12 +/* ... and hard-wired. */ +#define ROCKHOPPER_PCI1_SLOT 3 +#define ROCKHOPPER_PCI2_SLOT 4 +#define ROCKHOPPER_PCI3_SLOT 5 +#define ROCKHOPPER_PCI4_SLOT 6 +#define ROCKHOPPER_PCNET_SLOT 1 + +#define M1535_IRQ_MASK(n) (1 << (n)) + +#define M1535_IRQ_EDGE (M1535_IRQ_MASK(M1535_IRQ_TMR) | \ + M1535_IRQ_MASK(M1535_IRQ_KBD) | \ + M1535_IRQ_MASK(M1535_IRQ_COM1) | \ + M1535_IRQ_MASK(M1535_IRQ_COM2) | \ + M1535_IRQ_MASK(M1535_IRQ_IRDA) | \ + M1535_IRQ_MASK(M1535_IRQ_RTC) | \ + M1535_IRQ_MASK(M1535_IRQ_FDC) | \ + M1535_IRQ_MASK(M1535_IRQ_PS2)) + +#define M1535_IRQ_LEVEL (M1535_IRQ_MASK(M1535_IRQ_IDE) | \ + M1535_IRQ_MASK(M1535_IRQ_USB) | \ + M1535_IRQ_MASK(M1535_IRQ_INTA) | \ + M1535_IRQ_MASK(M1535_IRQ_INTB) | \ + M1535_IRQ_MASK(M1535_IRQ_INTC) | \ + M1535_IRQ_MASK(M1535_IRQ_INTD)) + +struct irq_map_entry { + u16 bus; + u8 slot; + u8 irq; +}; +static struct irq_map_entry int_map[] = { + {1, ROCKHOPPER_M5451_SLOT, M1535_IRQ_AUDIO}, /* Audio controller */ + {1, ROCKHOPPER_PCI1_SLOT, M1535_IRQ_INTD}, /* PCI slot #1 */ + {1, ROCKHOPPER_PCI2_SLOT, M1535_IRQ_INTC}, /* PCI slot #2 */ + {1, ROCKHOPPER_M5237_SLOT, M1535_IRQ_USB}, /* USB host controller */ + {1, ROCKHOPPER_M5229_SLOT, IDE_PRIMARY_IRQ}, /* IDE controller */ + {2, ROCKHOPPER_PCNET_SLOT, M1535_IRQ_INTD}, /* AMD Am79c973 on-board + ethernet */ + {2, ROCKHOPPER_PCI3_SLOT, M1535_IRQ_INTB}, /* PCI slot #3 */ + {2, ROCKHOPPER_PCI4_SLOT, M1535_IRQ_INTC} /* PCI slot #4 */ +}; + +static int pci_intlines[] = + { M1535_IRQ_INTA, M1535_IRQ_INTB, M1535_IRQ_INTC, M1535_IRQ_INTD }; + +/* Determine the Rockhopper IRQ line number for the PCI device */ +int rockhopper_get_irq(struct pci_dev *dev, u8 pin, u8 slot) +{ + struct pci_bus *bus; + int i; + + bus = dev->bus; + if (bus == NULL) + return -1; + + for (i = 0; i < sizeof (int_map) / sizeof (int_map[0]); i++) { + if (int_map[i].bus == bus->number && int_map[i].slot == slot) { + int line; + for (line = 0; line < 4; line++) + if (pci_intlines[line] == int_map[i].irq) + break; + if (line < 4) + return pci_intlines[(line + (pin - 1)) % 4]; + else + return int_map[i].irq; + } + } + return -1; +} + +#ifdef CONFIG_ROCKHOPPER +void i8259_init(void) +{ + outb(0x11, 0x20); /* Master ICW1 */ + outb(I8259_IRQ_BASE, 0x21); /* Master ICW2 */ + outb(0x04, 0x21); /* Master ICW3 */ + outb(0x01, 0x21); /* Master ICW4 */ + outb(0xff, 0x21); /* Master IMW */ + + outb(0x11, 0xa0); /* Slave ICW1 */ + outb(I8259_IRQ_BASE + 8, 0xa1); /* Slave ICW2 */ + outb(0x02, 0xa1); /* Slave ICW3 */ + outb(0x01, 0xa1); /* Slave ICW4 */ + outb(0xff, 0xa1); /* Slave IMW */ + + outb(0x00, 0x4d0); + outb(0x02, 0x4d1); /* USB IRQ9 is level */ +} +#endif + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + extern int pci_probe_only; + pci_probe_only = 1; + +#ifdef CONFIG_ROCKHOPPER + if( dev->bus->number == 1 && vr4133_rockhopper ) { + if(slot == ROCKHOPPER_PCI1_SLOT || slot == ROCKHOPPER_PCI2_SLOT) + dev->irq = CMBVR41XX_INTA_IRQ; + else + dev->irq = rockhopper_get_irq(dev, pin, slot); + } else + dev->irq = CMBVR41XX_INTA_IRQ; +#else + dev->irq = CMBVR41XX_INTA_IRQ; +#endif + + return dev->irq; +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, ali_m1535plus_init); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229, ali_m5229_init); + + diff --git a/arch/mips/pci/fixup-yosemite.c b/arch/mips/pci/fixup-yosemite.c new file mode 100644 index 00000000000..81d77a587a5 --- /dev/null +++ b/arch/mips/pci/fixup-yosemite.c @@ -0,0 +1,41 @@ +/* + * 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/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + if (pin == 0) + return -1; + + return 3; /* Everything goes to one irq bit */ +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/ops-au1000.c b/arch/mips/pci/ops-au1000.c new file mode 100644 index 00000000000..c1c91ca0f9c --- /dev/null +++ b/arch/mips/pci/ops-au1000.c @@ -0,0 +1,325 @@ +/* + * BRIEF MODULE DESCRIPTION + * Alchemy/AMD Au1x00 pci support. + * + * Copyright 2001,2002,2003 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * Support for all devices (greater than 16) added by David Gathright. + * + * 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/init.h> +#include <linux/vmalloc.h> + +#include <asm/mach-au1x00/au1000.h> + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + + +int (*board_pci_idsel)(unsigned int devsel, int assert); + +/* CP0 hazard avoidance. */ +#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \ + "nop; nop; nop; nop;\t" \ + ".set reorder\n\t") + +void mod_wired_entry(int entry, unsigned long entrylo0, + unsigned long entrylo1, unsigned long entryhi, + unsigned long pagemask) +{ + unsigned long old_pagemask; + unsigned long old_ctx; + + /* Save old context and create impossible VPN2 value */ + old_ctx = read_c0_entryhi() & 0xff; + old_pagemask = read_c0_pagemask(); + write_c0_index(entry); + BARRIER; + write_c0_pagemask(pagemask); + write_c0_entryhi(entryhi); + write_c0_entrylo0(entrylo0); + write_c0_entrylo1(entrylo1); + BARRIER; + tlb_write_indexed(); + BARRIER; + write_c0_entryhi(old_ctx); + BARRIER; + write_c0_pagemask(old_pagemask); +} + +struct vm_struct *pci_cfg_vm; +static int pci_cfg_wired_entry; +static int first_cfg = 1; +unsigned long last_entryLo0, last_entryLo1; + +static int config_access(unsigned char access_type, struct pci_bus *bus, + unsigned int dev_fn, unsigned char where, + u32 * data) +{ +#if defined( CONFIG_SOC_AU1500 ) || defined( CONFIG_SOC_AU1550 ) + unsigned int device = PCI_SLOT(dev_fn); + unsigned int function = PCI_FUNC(dev_fn); + unsigned long offset, status; + unsigned long cfg_base; + unsigned long flags; + int error = PCIBIOS_SUCCESSFUL; + unsigned long entryLo0, entryLo1; + + if (device > 19) { + *data = 0xffffffff; + return -1; + } + + local_irq_save(flags); + au_writel(((0x2000 << 16) | (au_readl(Au1500_PCI_STATCMD) & 0xffff)), + Au1500_PCI_STATCMD); + au_sync_udelay(1); + + /* + * We can't ioremap the entire pci config space because it's + * too large. Nor can we call ioremap dynamically because some + * device drivers use the pci config routines from within + * interrupt handlers and that becomes a problem in get_vm_area(). + * We use one wired tlb to handle all config accesses for all + * busses. To improve performance, if the current device + * is the same as the last device accessed, we don't touch the + * tlb. + */ + if (first_cfg) { + /* reserve a wired entry for pci config accesses */ + first_cfg = 0; + pci_cfg_vm = get_vm_area(0x2000, 0); + if (!pci_cfg_vm) + panic (KERN_ERR "PCI unable to get vm area\n"); + pci_cfg_wired_entry = read_c0_wired(); + add_wired_entry(0, 0, (unsigned long)pci_cfg_vm->addr, PM_4K); + last_entryLo0 = last_entryLo1 = 0xffffffff; + } + + /* Since the Au1xxx doesn't do the idsel timing exactly to spec, + * many board vendors implement their own off-chip idsel, so call + * it now. If it doesn't succeed, may as well bail out at this point. + */ + if (board_pci_idsel) { + if (board_pci_idsel(device, 1) == 0) { + *data = 0xffffffff; + local_irq_restore(flags); + return -1; + } + } + + /* setup the config window */ + if (bus->number == 0) { + cfg_base = ((1<<device)<<11); + } else { + cfg_base = 0x80000000 | (bus->number<<16) | (device<<11); + } + + /* setup the lower bits of the 36 bit address */ + offset = (function << 8) | (where & ~0x3); + /* pick up any address that falls below the page mask */ + offset |= cfg_base & ~PAGE_MASK; + + /* page boundary */ + cfg_base = cfg_base & PAGE_MASK; + + entryLo0 = (6 << 26) | (cfg_base >> 6) | (2 << 3) | 7; + entryLo1 = (6 << 26) | (cfg_base >> 6) | (0x1000 >> 6) | (2 << 3) | 7; + + if ((entryLo0 != last_entryLo0) || (entryLo1 != last_entryLo1)) { + mod_wired_entry(pci_cfg_wired_entry, entryLo0, entryLo1, + (unsigned long)pci_cfg_vm->addr, PM_4K); + last_entryLo0 = entryLo0; + last_entryLo1 = entryLo1; + } + + if (access_type == PCI_ACCESS_WRITE) { + au_writel(*data, (int)(pci_cfg_vm->addr + offset)); + } else { + *data = au_readl((int)(pci_cfg_vm->addr + offset)); + } + au_sync_udelay(2); + + DBG("cfg_access %d bus->number %d dev %d at %x *data %x conf %x\n", + access_type, bus->number, device, where, *data, offset); + + /* check master abort */ + status = au_readl(Au1500_PCI_STATCMD); + + if (status & (1<<29)) { + *data = 0xffffffff; + error = -1; + DBG("Au1x Master Abort\n"); + } else if ((status >> 28) & 0xf) { + DBG("PCI ERR detected: status %x\n", status); + *data = 0xffffffff; + error = -1; + } + + /* Take away the idsel. + */ + if (board_pci_idsel) { + (void)board_pci_idsel(device, 0); + } + + local_irq_restore(flags); + return error; +#endif +} + +static int read_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u8 * val) +{ + u32 data; + int ret; + + ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); + if (where & 1) + data >>= 8; + if (where & 2) + data >>= 16; + *val = data & 0xff; + return ret; +} + + +static int read_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u16 * val) +{ + u32 data; + int ret; + + ret = config_access(PCI_ACCESS_READ, bus, devfn, where, &data); + if (where & 2) + data >>= 16; + *val = data & 0xffff; + return ret; +} + +static int read_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 * val) +{ + int ret; + + ret = config_access(PCI_ACCESS_READ, bus, devfn, where, val); + return ret; +} + +static int +write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, + u8 val) +{ + u32 data = 0; + + if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) + return -1; + + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; +} + +static int +write_config_word(struct pci_bus *bus, unsigned int devfn, int where, + u16 val) +{ + u32 data = 0; + + if (config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) + return -1; + + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) + return -1; + + + return PCIBIOS_SUCCESSFUL; +} + +static int +write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, + u32 val) +{ + if (config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val)) + return -1; + + return PCIBIOS_SUCCESSFUL; +} + +static int config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + switch (size) { + case 1: { + u8 _val; + int rc = read_config_byte(bus, devfn, where, &_val); + *val = _val; + return rc; + } + case 2: { + u16 _val; + int rc = read_config_word(bus, devfn, where, &_val); + *val = _val; + return rc; + } + default: + return read_config_dword(bus, devfn, where, val); + } +} + +static int config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + switch (size) { + case 1: + return write_config_byte(bus, devfn, where, (u8) val); + case 2: + return write_config_word(bus, devfn, where, (u16) val); + default: + return write_config_dword(bus, devfn, where, val); + } +} + + +struct pci_ops au1x_pci_ops = { + config_read, + config_write +}; diff --git a/arch/mips/pci/ops-bonito64.c b/arch/mips/pci/ops-bonito64.c new file mode 100644 index 00000000000..4b4e086a7eb --- /dev/null +++ b/arch/mips/pci/ops-bonito64.c @@ -0,0 +1,196 @@ +/* + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * MIPS boards specific PCI support. + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/mips-boards/bonito64.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +/* + * PCI configuration cycle AD bus definition + */ +/* Type 0 */ +#define PCI_CFG_TYPE0_REG_SHF 0 +#define PCI_CFG_TYPE0_FUNC_SHF 8 + +/* Type 1 */ +#define PCI_CFG_TYPE1_REG_SHF 0 +#define PCI_CFG_TYPE1_FUNC_SHF 8 +#define PCI_CFG_TYPE1_DEV_SHF 11 +#define PCI_CFG_TYPE1_BUS_SHF 16 + +static int bonito64_pcibios_config_access(unsigned char access_type, + struct pci_bus *bus, + unsigned int devfn, int where, + u32 * data) +{ + unsigned char busnum = bus->number; + u32 dummy; + u64 pci_addr; + + /* Algorithmics Bonito64 system controller. */ + + if ((busnum == 0) && (PCI_SLOT(devfn) > 21)) { + /* We number bus 0 devices from 0..21 */ + return -1; + } + +#ifdef CONFIG_MIPS_BOARDS_GEN + if ((busnum == 0) && (PCI_SLOT(devfn) == 17)) { + /* MIPS Core boards have Bonito connected as device 17 */ + return -1; + } +#endif + + /* Clear cause register bits */ + BONITO_PCICMD |= (BONITO_PCICMD_MABORT_CLR | + BONITO_PCICMD_MTABORT_CLR); + + /* + * Setup pattern to be used as PCI "address" for + * Type 0 cycle + */ + if (busnum == 0) { + /* IDSEL */ + pci_addr = (u64) 1 << (PCI_SLOT(devfn) + 10); + } else { + /* Bus number */ + pci_addr = busnum << PCI_CFG_TYPE1_BUS_SHF; + + /* Device number */ + pci_addr |= + PCI_SLOT(devfn) << PCI_CFG_TYPE1_DEV_SHF; + } + + /* Function (same for Type 0/1) */ + pci_addr |= PCI_FUNC(devfn) << PCI_CFG_TYPE0_FUNC_SHF; + + /* Register number (same for Type 0/1) */ + pci_addr |= (where & ~0x3) << PCI_CFG_TYPE0_REG_SHF; + + if (busnum == 0) { + /* Type 0 */ + BONITO_PCIMAP_CFG = pci_addr >> 16; + } else { + /* Type 1 */ + BONITO_PCIMAP_CFG = (pci_addr >> 16) | 0x10000; + } + + pci_addr &= 0xffff; + + /* Flush Bonito register block */ + dummy = BONITO_PCIMAP_CFG; + iob(); /* sync */ + + /* Perform access */ + if (access_type == PCI_ACCESS_WRITE) { + *(volatile u32 *) (_pcictrl_bonito_pcicfg + (u32)pci_addr) = *(u32 *) data; + + /* Wait till done */ + while (BONITO_PCIMSTAT & 0xF); + } else { + *(u32 *) data = *(volatile u32 *) (_pcictrl_bonito_pcicfg + (u32)pci_addr); + } + + /* Detect Master/Target abort */ + if (BONITO_PCICMD & (BONITO_PCICMD_MABORT_CLR | + BONITO_PCICMD_MTABORT_CLR)) { + /* Error occurred */ + + /* Clear bits */ + BONITO_PCICMD |= (BONITO_PCICMD_MABORT_CLR | + BONITO_PCICMD_MTABORT_CLR); + + return -1; + } + + return 0; +} + + +/* + * We can't address 8 and 16 bit words directly. Instead we have to + * read/write a 32bit word and mask/modify the data we actually want. + */ +static int bonito64_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + u32 data = 0; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (bonito64_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, + &data)) + return -1; + + if (size == 1) + *val = (data >> ((where & 3) << 3)) & 0xff; + else if (size == 2) + *val = (data >> ((where & 3) << 3)) & 0xffff; + else + *val = data; + + return PCIBIOS_SUCCESSFUL; +} + +static int bonito64_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 data = 0; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (size == 4) + data = val; + else { + if (bonito64_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, + where, &data)) + return -1; + + if (size == 1) + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else if (size == 2) + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + } + + if (bonito64_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where, + &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops bonito64_pci_ops = { + .read = bonito64_pcibios_read, + .write = bonito64_pcibios_write +}; diff --git a/arch/mips/pci/ops-ddb5074.c b/arch/mips/pci/ops-ddb5074.c new file mode 100644 index 00000000000..89f97bef4fc --- /dev/null +++ b/arch/mips/pci/ops-ddb5074.c @@ -0,0 +1,271 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/ddb5476/pci_ops.c + * Define the pci_ops for DB5477. + * + * Much of the code is derived from the original DDB5074 port by + * Geert Uytterhoeven <geert@sonycom.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. + * + */ +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/debug.h> + +#include <asm/ddb5xxx/ddb5xxx.h> + +/* + * config_swap structure records what set of pdar/pmr are used + * to access pci config space. It also provides a place hold the + * original values for future restoring. + */ +struct pci_config_swap { + u32 pdar; + u32 pmr; + u32 config_base; + u32 config_size; + u32 pdar_backup; + u32 pmr_backup; +}; + +/* + * On DDB5476, we have one set of swap registers + */ +struct pci_config_swap ext_pci_swap = { + DDB_PCIW0, + DDB_PCIINIT0, + DDB_PCI_CONFIG_BASE, + DDB_PCI_CONFIG_SIZE +}; + +static int pci_config_workaround = 1; + +/* + * access config space + */ +static inline u32 ddb_access_config_base(struct pci_config_swap *swap, u32 bus, /* 0 means top level bus */ + u32 slot_num) +{ + u32 pci_addr = 0; + u32 pciinit_offset = 0; + u32 virt_addr = swap->config_base; + u32 option; + + if (pci_config_workaround) { + if (slot_num == 5) + slot_num = 14; + } else { + if (slot_num == 5) + return DDB_BASE + DDB_PCI_BASE; + } + + /* minimum pdar (window) size is 2MB */ + db_assert(swap->config_size >= (2 << 20)); + + db_assert(slot_num < (1 << 5)); + db_assert(bus < (1 << 8)); + + /* backup registers */ + swap->pdar_backup = ddb_in32(swap->pdar); + swap->pmr_backup = ddb_in32(swap->pmr); + + /* set the pdar (pci window) register */ + ddb_set_pdar(swap->pdar, swap->config_base, swap->config_size, 32, /* 32 bit wide */ + 0, /* not on local memory bus */ + 0); /* not visible from PCI bus (N/A) */ + + /* + * calcuate the absolute pci config addr; + * according to the spec, we start scanning from adr:11 (0x800) + */ + if (bus == 0) { + /* type 0 config */ + pci_addr = 0x00040000 << slot_num; + } else { + /* type 1 config */ + pci_addr = 0x00040000 << slot_num; + panic + ("ddb_access_config_base: we don't support type 1 config Yet"); + } + + /* + * if pci_addr is less than pci config window size, we set + * pciinit_offset to 0 and adjust the virt_address. + * Otherwise we will try to adjust pciinit_offset. + */ + if (pci_addr < swap->config_size) { + virt_addr = KSEG1ADDR(swap->config_base + pci_addr); + pciinit_offset = 0; + } else { + db_assert((pci_addr & (swap->config_size - 1)) == 0); + virt_addr = KSEG1ADDR(swap->config_base); + pciinit_offset = pci_addr; + } + + /* set the pmr register */ + option = DDB_PCI_ACCESS_32; + if (bus != 0) + option |= DDB_PCI_CFGTYPE1; + ddb_set_pmr(swap->pmr, DDB_PCICMD_CFG, pciinit_offset, option); + + return virt_addr; +} + +static inline void ddb_close_config_base(struct pci_config_swap *swap) +{ + ddb_out32(swap->pdar, swap->pdar_backup); + ddb_out32(swap->pmr, swap->pmr_backup); +} + +static int read_config_dword(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u32 * val) +{ + u32 bus, slot_num, func_num; + u32 base; + + db_assert((where & 3) == 0); + db_assert(where < (1 << 8)); + + /* check if the bus is top-level */ + if (dev->bus->parent != NULL) { + bus = dev->bus->number; + db_assert(bus != 0); + } else { + bus = 0; + } + + slot_num = PCI_SLOT(dev->devfn); + func_num = PCI_FUNC(dev->devfn); + base = ddb_access_config_base(swap, bus, slot_num); + *val = *(volatile u32 *) (base + (func_num << 8) + where); + ddb_close_config_base(swap); + return PCIBIOS_SUCCESSFUL; +} + +static int read_config_word(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u16 * val) +{ + int status; + u32 result; + + db_assert((where & 1) == 0); + + status = read_config_dword(swap, dev, where & ~3, &result); + if (where & 2) + result >>= 16; + *val = result & 0xffff; + return status; +} + +static int read_config_byte(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u8 * val) +{ + int status; + u32 result; + + status = read_config_dword(swap, dev, where & ~3, &result); + if (where & 1) + result >>= 8; + if (where & 2) + result >>= 16; + *val = result & 0xff; + return status; +} + +static int write_config_dword(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u32 val) +{ + u32 bus, slot_num, func_num; + u32 base; + + db_assert((where & 3) == 0); + db_assert(where < (1 << 8)); + + /* check if the bus is top-level */ + if (dev->bus->parent != NULL) { + bus = dev->bus->number; + db_assert(bus != 0); + } else { + bus = 0; + } + + slot_num = PCI_SLOT(dev->devfn); + func_num = PCI_FUNC(dev->devfn); + base = ddb_access_config_base(swap, bus, slot_num); + *(volatile u32 *) (base + (func_num << 8) + where) = val; + ddb_close_config_base(swap); + return PCIBIOS_SUCCESSFUL; +} + +static int write_config_word(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u16 val) +{ + int status, shift = 0; + u32 result; + + db_assert((where & 1) == 0); + + status = read_config_dword(swap, dev, where & ~3, &result); + if (status != PCIBIOS_SUCCESSFUL) + return status; + + if (where & 2) + shift += 16; + result &= ~(0xffff << shift); + result |= val << shift; + return write_config_dword(swap, dev, where & ~3, result); +} + +static int write_config_byte(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u8 val) +{ + int status, shift = 0; + u32 result; + + status = read_config_dword(swap, dev, where & ~3, &result); + if (status != PCIBIOS_SUCCESSFUL) + return status; + + if (where & 2) + shift += 16; + if (where & 1) + shift += 8; + result &= ~(0xff << shift); + result |= val << shift; + return write_config_dword(swap, dev, where & ~3, result); +} + +#define MAKE_PCI_OPS(prefix, rw, unitname, unittype, pciswap) \ +static int prefix##_##rw##_config_##unitname(struct pci_dev *dev, int where, unittype val) \ +{ \ + return rw##_config_##unitname(pciswap, \ + dev, \ + where, \ + val); \ +} + +MAKE_PCI_OPS(extpci, read, byte, u8 *, &ext_pci_swap) + MAKE_PCI_OPS(extpci, read, word, u16 *, &ext_pci_swap) + MAKE_PCI_OPS(extpci, read, dword, u32 *, &ext_pci_swap) + + MAKE_PCI_OPS(extpci, write, byte, u8, &ext_pci_swap) + MAKE_PCI_OPS(extpci, write, word, u16, &ext_pci_swap) + MAKE_PCI_OPS(extpci, write, dword, u32, &ext_pci_swap) + +struct pci_ops ddb5476_ext_pci_ops = { + extpci_read_config_byte, + extpci_read_config_word, + extpci_read_config_dword, + extpci_write_config_byte, + extpci_write_config_word, + extpci_write_config_dword +}; diff --git a/arch/mips/pci/ops-ddb5476.c b/arch/mips/pci/ops-ddb5476.c new file mode 100644 index 00000000000..12da58e75ec --- /dev/null +++ b/arch/mips/pci/ops-ddb5476.c @@ -0,0 +1,286 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/ddb5476/pci_ops.c + * Define the pci_ops for DB5477. + * + * Much of the code is derived from the original DDB5074 port by + * Geert Uytterhoeven <geert@sonycom.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. + * + */ +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/debug.h> + +#include <asm/ddb5xxx/ddb5xxx.h> + +/* + * config_swap structure records what set of pdar/pmr are used + * to access pci config space. It also provides a place hold the + * original values for future restoring. + */ +struct pci_config_swap { + u32 pdar; + u32 pmr; + u32 config_base; + u32 config_size; + u32 pdar_backup; + u32 pmr_backup; +}; + +/* + * On DDB5476, we have one set of swap registers + */ +struct pci_config_swap ext_pci_swap = { + DDB_PCIW0, + DDB_PCIINIT0, + DDB_PCI_CONFIG_BASE, + DDB_PCI_CONFIG_SIZE +}; + +static int pci_config_workaround = 1; + +/* + * access config space + */ +static inline u32 ddb_access_config_base(struct pci_config_swap *swap, u32 bus, /* 0 means top level bus */ + u32 slot_num) +{ + u32 pci_addr = 0; + u32 pciinit_offset = 0; + u32 virt_addr = swap->config_base; + u32 option; + + if (pci_config_workaround) { + /* [jsun] work around Vrc5476 controller itself, returnning + * slot 0 essentially makes vrc5476 invisible + */ + if (slot_num == 12) + slot_num = 0; + +#if 0 + /* BUG : skip P2P bridge for now */ + if (slot_num == 5) + slot_num = 0; +#endif + + } else { + /* now we have to be hornest, returning the true + * PCI config headers for vrc5476 + */ + if (slot_num == 12) { + swap->pdar_backup = ddb_in32(swap->pdar); + swap->pmr_backup = ddb_in32(swap->pmr); + return DDB_BASE + DDB_PCI_BASE; + } + } + + /* minimum pdar (window) size is 2MB */ + db_assert(swap->config_size >= (2 << 20)); + + db_assert(slot_num < (1 << 5)); + db_assert(bus < (1 << 8)); + + /* backup registers */ + swap->pdar_backup = ddb_in32(swap->pdar); + swap->pmr_backup = ddb_in32(swap->pmr); + + /* set the pdar (pci window) register */ + ddb_set_pdar(swap->pdar, swap->config_base, swap->config_size, 32, /* 32 bit wide */ + 0, /* not on local memory bus */ + 0); /* not visible from PCI bus (N/A) */ + + /* + * calcuate the absolute pci config addr; + * according to the spec, we start scanning from adr:11 (0x800) + */ + if (bus == 0) { + /* type 0 config */ + pci_addr = 0x800 << slot_num; + } else { + /* type 1 config */ + pci_addr = (bus << 16) | (slot_num << 11); + /* panic("ddb_access_config_base: we don't support type 1 config Yet"); */ + } + + /* + * if pci_addr is less than pci config window size, we set + * pciinit_offset to 0 and adjust the virt_address. + * Otherwise we will try to adjust pciinit_offset. + */ + if (pci_addr < swap->config_size) { + virt_addr = KSEG1ADDR(swap->config_base + pci_addr); + pciinit_offset = 0; + } else { + db_assert((pci_addr & (swap->config_size - 1)) == 0); + virt_addr = KSEG1ADDR(swap->config_base); + pciinit_offset = pci_addr; + } + + /* set the pmr register */ + option = DDB_PCI_ACCESS_32; + if (bus != 0) + option |= DDB_PCI_CFGTYPE1; + ddb_set_pmr(swap->pmr, DDB_PCICMD_CFG, pciinit_offset, option); + + return virt_addr; +} + +static inline void ddb_close_config_base(struct pci_config_swap *swap) +{ + ddb_out32(swap->pdar, swap->pdar_backup); + ddb_out32(swap->pmr, swap->pmr_backup); +} + +static int read_config_dword(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u32 * val) +{ + u32 bus, slot_num, func_num; + u32 base; + + db_assert((where & 3) == 0); + db_assert(where < (1 << 8)); + + /* check if the bus is top-level */ + if (dev->bus->parent != NULL) { + bus = dev->bus->number; + db_assert(bus != 0); + } else { + bus = 0; + } + + slot_num = PCI_SLOT(dev->devfn); + func_num = PCI_FUNC(dev->devfn); + base = ddb_access_config_base(swap, bus, slot_num); + *val = *(volatile u32 *) (base + (func_num << 8) + where); + ddb_close_config_base(swap); + return PCIBIOS_SUCCESSFUL; +} + +static int read_config_word(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u16 * val) +{ + int status; + u32 result; + + db_assert((where & 1) == 0); + + status = read_config_dword(swap, dev, where & ~3, &result); + if (where & 2) + result >>= 16; + *val = result & 0xffff; + return status; +} + +static int read_config_byte(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u8 * val) +{ + int status; + u32 result; + + status = read_config_dword(swap, dev, where & ~3, &result); + if (where & 1) + result >>= 8; + if (where & 2) + result >>= 16; + *val = result & 0xff; + return status; +} + +static int write_config_dword(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u32 val) +{ + u32 bus, slot_num, func_num; + u32 base; + + db_assert((where & 3) == 0); + db_assert(where < (1 << 8)); + + /* check if the bus is top-level */ + if (dev->bus->parent != NULL) { + bus = dev->bus->number; + db_assert(bus != 0); + } else { + bus = 0; + } + + slot_num = PCI_SLOT(dev->devfn); + func_num = PCI_FUNC(dev->devfn); + base = ddb_access_config_base(swap, bus, slot_num); + *(volatile u32 *) (base + (func_num << 8) + where) = val; + ddb_close_config_base(swap); + return PCIBIOS_SUCCESSFUL; +} + +static int write_config_word(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u16 val) +{ + int status, shift = 0; + u32 result; + + db_assert((where & 1) == 0); + + status = read_config_dword(swap, dev, where & ~3, &result); + if (status != PCIBIOS_SUCCESSFUL) + return status; + + if (where & 2) + shift += 16; + result &= ~(0xffff << shift); + result |= val << shift; + return write_config_dword(swap, dev, where & ~3, result); +} + +static int write_config_byte(struct pci_config_swap *swap, + struct pci_dev *dev, u32 where, u8 val) +{ + int status, shift = 0; + u32 result; + + status = read_config_dword(swap, dev, where & ~3, &result); + if (status != PCIBIOS_SUCCESSFUL) + return status; + + if (where & 2) + shift += 16; + if (where & 1) + shift += 8; + result &= ~(0xff << shift); + result |= val << shift; + return write_config_dword(swap, dev, where & ~3, result); +} + +#define MAKE_PCI_OPS(prefix, rw, unitname, unittype, pciswap) \ +static int prefix##_##rw##_config_##unitname(struct pci_dev *dev, int where, unittype val) \ +{ \ + return rw##_config_##unitname(pciswap, \ + dev, \ + where, \ + val); \ +} + +MAKE_PCI_OPS(extpci, read, byte, u8 *, &ext_pci_swap) + MAKE_PCI_OPS(extpci, read, word, u16 *, &ext_pci_swap) + MAKE_PCI_OPS(extpci, read, dword, u32 *, &ext_pci_swap) + + MAKE_PCI_OPS(extpci, write, byte, u8, &ext_pci_swap) + MAKE_PCI_OPS(extpci, write, word, u16, &ext_pci_swap) + MAKE_PCI_OPS(extpci, write, dword, u32, &ext_pci_swap) + +struct pci_ops ddb5476_ext_pci_ops = { + extpci_read_config_byte, + extpci_read_config_word, + extpci_read_config_dword, + extpci_write_config_byte, + extpci_write_config_word, + extpci_write_config_dword +}; diff --git a/arch/mips/pci/ops-ddb5477.c b/arch/mips/pci/ops-ddb5477.c new file mode 100644 index 00000000000..e955443fedf --- /dev/null +++ b/arch/mips/pci/ops-ddb5477.c @@ -0,0 +1,278 @@ +/*********************************************************************** + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * arch/mips/ddb5xxx/ddb5477/pci_ops.c + * Define the pci_ops for DB5477. + * + * Much of the code is derived from the original DDB5074 port by + * Geert Uytterhoeven <geert@sonycom.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. + *********************************************************************** + */ + +/* + * DDB5477 has two PCI channels, external PCI and IOPIC (internal) + * Therefore we provide two sets of pci_ops. + */ +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/debug.h> + +#include <asm/ddb5xxx/ddb5xxx.h> + +/* + * config_swap structure records what set of pdar/pmr are used + * to access pci config space. It also provides a place hold the + * original values for future restoring. + */ +struct pci_config_swap { + u32 pdar; + u32 pmr; + u32 config_base; + u32 config_size; + u32 pdar_backup; + u32 pmr_backup; +}; + +/* + * On DDB5477, we have two sets of swap registers, for ext PCI and IOPCI. + */ +struct pci_config_swap ext_pci_swap = { + DDB_PCIW0, + DDB_PCIINIT00, + DDB_PCI0_CONFIG_BASE, + DDB_PCI0_CONFIG_SIZE +}; +struct pci_config_swap io_pci_swap = { + DDB_IOPCIW0, + DDB_PCIINIT01, + DDB_PCI1_CONFIG_BASE, + DDB_PCI1_CONFIG_SIZE +}; + + +/* + * access config space + */ +static inline u32 ddb_access_config_base(struct pci_config_swap *swap, u32 bus, /* 0 means top level bus */ + u32 slot_num) +{ + u32 pci_addr = 0; + u32 pciinit_offset = 0; + u32 virt_addr; + u32 option; + + /* minimum pdar (window) size is 2MB */ + db_assert(swap->config_size >= (2 << 20)); + + db_assert(slot_num < (1 << 5)); + db_assert(bus < (1 << 8)); + + /* backup registers */ + swap->pdar_backup = ddb_in32(swap->pdar); + swap->pmr_backup = ddb_in32(swap->pmr); + + /* set the pdar (pci window) register */ + ddb_set_pdar(swap->pdar, swap->config_base, swap->config_size, 32, /* 32 bit wide */ + 0, /* not on local memory bus */ + 0); /* not visible from PCI bus (N/A) */ + + /* + * calcuate the absolute pci config addr; + * according to the spec, we start scanning from adr:11 (0x800) + */ + if (bus == 0) { + /* type 0 config */ + pci_addr = 0x800 << slot_num; + } else { + /* type 1 config */ + pci_addr = (bus << 16) | (slot_num << 11); + } + + /* + * if pci_addr is less than pci config window size, we set + * pciinit_offset to 0 and adjust the virt_address. + * Otherwise we will try to adjust pciinit_offset. + */ + if (pci_addr < swap->config_size) { + virt_addr = KSEG1ADDR(swap->config_base + pci_addr); + pciinit_offset = 0; + } else { + db_assert((pci_addr & (swap->config_size - 1)) == 0); + virt_addr = KSEG1ADDR(swap->config_base); + pciinit_offset = pci_addr; + } + + /* set the pmr register */ + option = DDB_PCI_ACCESS_32; + if (bus != 0) + option |= DDB_PCI_CFGTYPE1; + ddb_set_pmr(swap->pmr, DDB_PCICMD_CFG, pciinit_offset, option); + + return virt_addr; +} + +static inline void ddb_close_config_base(struct pci_config_swap *swap) +{ + ddb_out32(swap->pdar, swap->pdar_backup); + ddb_out32(swap->pmr, swap->pmr_backup); +} + +static int read_config_dword(struct pci_config_swap *swap, + struct pci_bus *bus, u32 devfn, u32 where, + u32 * val) +{ + u32 bus_num, slot_num, func_num; + u32 base; + + db_assert((where & 3) == 0); + db_assert(where < (1 << 8)); + + /* check if the bus is top-level */ + if (bus->parent != NULL) { + bus_num = bus->number; + db_assert(bus_num != 0); + } else { + bus_num = 0; + } + + slot_num = PCI_SLOT(devfn); + func_num = PCI_FUNC(devfn); + base = ddb_access_config_base(swap, bus_num, slot_num); + *val = *(volatile u32 *) (base + (func_num << 8) + where); + ddb_close_config_base(swap); + return PCIBIOS_SUCCESSFUL; +} + +static int read_config_word(struct pci_config_swap *swap, + struct pci_bus *bus, u32 devfn, u32 where, + u16 * val) +{ + int status; + u32 result; + + db_assert((where & 1) == 0); + + status = read_config_dword(swap, bus, devfn, where & ~3, &result); + if (where & 2) + result >>= 16; + *val = result & 0xffff; + return status; +} + +static int read_config_byte(struct pci_config_swap *swap, + struct pci_bus *bus, u32 devfn, u32 where, + u8 * val) +{ + int status; + u32 result; + + status = read_config_dword(swap, bus, devfn, where & ~3, &result); + if (where & 1) + result >>= 8; + if (where & 2) + result >>= 16; + *val = result & 0xff; + + return status; +} + +static int write_config_dword(struct pci_config_swap *swap, + struct pci_bus *bus, u32 devfn, u32 where, + u32 val) +{ + u32 bus_num, slot_num, func_num; + u32 base; + + db_assert((where & 3) == 0); + db_assert(where < (1 << 8)); + + /* check if the bus is top-level */ + if (bus->parent != NULL) { + bus_num = bus->number; + db_assert(bus_num != 0); + } else { + bus_num = 0; + } + + slot_num = PCI_SLOT(devfn); + func_num = PCI_FUNC(devfn); + base = ddb_access_config_base(swap, bus_num, slot_num); + *(volatile u32 *) (base + (func_num << 8) + where) = val; + ddb_close_config_base(swap); + return PCIBIOS_SUCCESSFUL; +} + +static int write_config_word(struct pci_config_swap *swap, + struct pci_bus *bus, u32 devfn, u32 where, u16 val) +{ + int status, shift = 0; + u32 result; + + db_assert((where & 1) == 0); + + status = read_config_dword(swap, bus, devfn, where & ~3, &result); + if (status != PCIBIOS_SUCCESSFUL) + return status; + + if (where & 2) + shift += 16; + result &= ~(0xffff << shift); + result |= val << shift; + return write_config_dword(swap, bus, devfn, where & ~3, result); +} + +static int write_config_byte(struct pci_config_swap *swap, + struct pci_bus *bus, u32 devfn, u32 where, u8 val) +{ + int status, shift = 0; + u32 result; + + status = read_config_dword(swap, bus, devfn, where & ~3, &result); + if (status != PCIBIOS_SUCCESSFUL) + return status; + + if (where & 2) + shift += 16; + if (where & 1) + shift += 8; + result &= ~(0xff << shift); + result |= val << shift; + return write_config_dword(swap, bus, devfn, where & ~3, result); +} + +#define MAKE_PCI_OPS(prefix, rw, pciswap, star) \ +static int prefix##_##rw##_config(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 star val) \ +{ \ + if (size == 1) \ + return rw##_config_byte(pciswap, bus, devfn, where, (u8 star)val); \ + else if (size == 2) \ + return rw##_config_word(pciswap, bus, devfn, where, (u16 star)val); \ + /* Size must be 4 */ \ + return rw##_config_dword(pciswap, bus, devfn, where, val); \ +} + +MAKE_PCI_OPS(extpci, read, &ext_pci_swap, *) +MAKE_PCI_OPS(extpci, write, &ext_pci_swap,) + +MAKE_PCI_OPS(iopci, read, &io_pci_swap, *) +MAKE_PCI_OPS(iopci, write, &io_pci_swap,) + +struct pci_ops ddb5477_ext_pci_ops = { + .read = extpci_read_config, + .write = extpci_write_config +}; + + +struct pci_ops ddb5477_io_pci_ops = { + .read = iopci_read_config, + .write = iopci_write_config +}; diff --git a/arch/mips/pci/ops-gt64111.c b/arch/mips/pci/ops-gt64111.c new file mode 100644 index 00000000000..c5b0fc184c2 --- /dev/null +++ b/arch/mips/pci/ops-gt64111.c @@ -0,0 +1,100 @@ +/* + * 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. + * + * Copyright (C) 1995, 1996, 1997, 2002 by Ralf Baechle + * Copyright (C) 2001, 2002, 2003 by Liam Davies (ldavies@agile.tv) + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/pci.h> +#include <asm/io.h> +#include <asm/gt64120.h> + +#include <asm/cobalt/cobalt.h> + +/* + * Accessing device 31 hangs the GT64120. Not sure if this will also hang + * the GT64111, let's be paranoid for now. + */ +static inline int pci_range_ck(struct pci_bus *bus, unsigned int devfn) +{ + if (bus->number == 0 && devfn == PCI_DEVFN(31, 0)) + return -1; + + return 0; +} + +static int gt64111_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + if (pci_range_ck(bus, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 4: + PCI_CFG_SET(devfn, where); + *val = GALILEO_INL(GT_PCI0_CFGDATA_OFS); + return PCIBIOS_SUCCESSFUL; + + case 2: + PCI_CFG_SET(devfn, (where & ~0x3)); + *val = GALILEO_INL(GT_PCI0_CFGDATA_OFS) + >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; + + case 1: + PCI_CFG_SET(devfn, (where & ~0x3)); + *val = GALILEO_INL(GT_PCI0_CFGDATA_OFS) + >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; + } + + return PCIBIOS_BAD_REGISTER_NUMBER; +} + +static int gt64111_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 tmp; + + if (pci_range_ck(bus, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 4: + PCI_CFG_SET(devfn, where); + GALILEO_OUTL(val, GT_PCI0_CFGDATA_OFS); + + return PCIBIOS_SUCCESSFUL; + + case 2: + PCI_CFG_SET(devfn, (where & ~0x3)); + tmp = GALILEO_INL(GT_PCI0_CFGDATA_OFS); + tmp &= ~(0xffff << ((where & 0x3) * 8)); + tmp |= (val << ((where & 0x3) * 8)); + GALILEO_OUTL(tmp, GT_PCI0_CFGDATA_OFS); + + return PCIBIOS_SUCCESSFUL; + + case 1: + PCI_CFG_SET(devfn, (where & ~0x3)); + tmp = GALILEO_INL(GT_PCI0_CFGDATA_OFS); + tmp &= ~(0xff << ((where & 0x3) * 8)); + tmp |= (val << ((where & 0x3) * 8)); + GALILEO_OUTL(tmp, GT_PCI0_CFGDATA_OFS); + + return PCIBIOS_SUCCESSFUL; + } + + return PCIBIOS_BAD_REGISTER_NUMBER; +} + +struct pci_ops gt64111_pci_ops = { + .read = gt64111_pci_read_config, + .write = gt64111_pci_write_config, +}; diff --git a/arch/mips/pci/ops-gt64120.c b/arch/mips/pci/ops-gt64120.c new file mode 100644 index 00000000000..7b99dfa33df --- /dev/null +++ b/arch/mips/pci/ops-gt64120.c @@ -0,0 +1,154 @@ +/* + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999, 2000 MIPS Technologies, Inc. All rights reserved. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> + +#include <asm/gt64120.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +/* + * PCI configuration cycle AD bus definition + */ +/* Type 0 */ +#define PCI_CFG_TYPE0_REG_SHF 0 +#define PCI_CFG_TYPE0_FUNC_SHF 8 + +/* Type 1 */ +#define PCI_CFG_TYPE1_REG_SHF 0 +#define PCI_CFG_TYPE1_FUNC_SHF 8 +#define PCI_CFG_TYPE1_DEV_SHF 11 +#define PCI_CFG_TYPE1_BUS_SHF 16 + +static int gt64120_pcibios_config_access(unsigned char access_type, + struct pci_bus *bus, unsigned int devfn, int where, u32 * data) +{ + unsigned char busnum = bus->number; + u32 intr; + + if ((busnum == 0) && (PCI_SLOT(devfn) == 0)) + /* Galileo itself is devfn 0, don't move it around */ + return -1; + + if ((busnum == 0) && (devfn >= PCI_DEVFN(31, 0))) + return -1; /* Because of a bug in the galileo (for slot 31). */ + + /* Clear cause register bits */ + GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | + GT_INTRCAUSE_TARABORT0_BIT)); + + /* Setup address */ + GT_WRITE(GT_PCI0_CFGADDR_OFS, + (busnum << GT_PCI0_CFGADDR_BUSNUM_SHF) | + (devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | + ((where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF) | + GT_PCI0_CFGADDR_CONFIGEN_BIT); + + if (access_type == PCI_ACCESS_WRITE) { + if (busnum == 0 && PCI_SLOT(devfn) == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); + } else + __GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); + } else { + if (busnum == 0 && PCI_SLOT(devfn) == 0) { + /* + * The Galileo system controller is acting + * differently than other devices. + */ + *data = GT_READ(GT_PCI0_CFGDATA_OFS); + } else + *data = __GT_READ(GT_PCI0_CFGDATA_OFS); + } + + /* Check for master or target abort */ + intr = GT_READ(GT_INTRCAUSE_OFS); + + if (intr & (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)) { + /* Error occurred */ + + /* Clear bits */ + GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | + GT_INTRCAUSE_TARABORT0_BIT)); + + return -1; + } + + return 0; +} + + +/* + * We can't address 8 and 16 bit words directly. Instead we have to + * read/write a 32bit word and mask/modify the data we actually want. + */ +static int gt64120_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + u32 data = 0; + + if (gt64120_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, + &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 1) + *val = (data >> ((where & 3) << 3)) & 0xff; + else if (size == 2) + *val = (data >> ((where & 3) << 3)) & 0xffff; + else + *val = data; + + return PCIBIOS_SUCCESSFUL; +} + +static int gt64120_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 data = 0; + + if (size == 4) + data = val; + else { + if (gt64120_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, + where, &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 1) + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else if (size == 2) + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + } + + if (gt64120_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where, + &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops gt64120_pci_ops = { + .read = gt64120_pcibios_read, + .write = gt64120_pcibios_write +}; diff --git a/arch/mips/pci/ops-gt96100.c b/arch/mips/pci/ops-gt96100.c new file mode 100644 index 00000000000..9e4ea6627e2 --- /dev/null +++ b/arch/mips/pci/ops-gt96100.c @@ -0,0 +1,169 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Galileo EV96100 board specific pci support. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * This file was derived from Carsten Langgaard's + * arch/mips/mips-boards/generic/pci.c + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * 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/init.h> + +#include <asm/delay.h> +#include <asm/gt64120.h> +#include <asm/galileo-boards/ev96100.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +static int static gt96100_config_access(unsigned char access_type, + struct pci_bus *bus, unsigned int devfn, int where, u32 * data) +{ + unsigned char bus = bus->number; + u32 intr; + + /* + * Because of a bug in the galileo (for slot 31). + */ + if (bus == 0 && devfn >= PCI_DEVFN(31, 0)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* Clear cause register bits */ + GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | + GT_INTRCAUSE_TARABORT0_BIT)); + + /* Setup address */ + GT_WRITE(GT_PCI0_CFGADDR_OFS, + (bus << GT_PCI0_CFGADDR_BUSNUM_SHF) | + (devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | + ((where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF) | + GT_PCI0_CFGADDR_CONFIGEN_BIT); + udelay(2); + + + if (access_type == PCI_ACCESS_WRITE) { + if (devfn != 0) + *data = le32_to_cpu(*data); + GT_WRITE(GT_PCI0_CFGDATA_OFS, *data); + } else { + *data = GT_READ(GT_PCI0_CFGDATA_OFS); + if (devfn != 0) + *data = le32_to_cpu(*data); + } + + udelay(2); + + /* Check for master or target abort */ + intr = GT_READ(GT_INTRCAUSE_OFS); + + if (intr & (GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)) { + /* Error occured */ + + /* Clear bits */ + GT_WRITE(GT_INTRCAUSE_OFS, ~(GT_INTRCAUSE_MASABORT0_BIT | + GT_INTRCAUSE_TARABORT0_BIT)); + return -1; + } + return 0; +} + +/* + * We can't address 8 and 16 bit words directly. Instead we have to + * read/write a 32bit word and mask/modify the data we actually want. + */ +static int gt96100_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + u32 data = 0; + + if (gt96100_config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *val = (data >> ((where & 3) << 3)) & 0xff; + break; + + case 2: + *val = (data >> ((where & 3) << 3)) & 0xffff; + break; + + case 4: + *val = data; + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int gt96100_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 data = 0; + + switch (size) { + case 1: + if (gt96100_config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) + return -1; + + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (gt96100_config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; + + case 2: + if (gt96100_config_access(PCI_ACCESS_READ, bus, devfn, where, &data)) + return -1; + + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (gt96100_config_access(PCI_ACCESS_WRITE, dev, where, &data)) + return -1; + + + return PCIBIOS_SUCCESSFUL; + + case 4: + if (gt96100_config_access(PCI_ACCESS_WRITE, dev, where, &val)) + return -1; + + return PCIBIOS_SUCCESSFUL; + } +} + +struct pci_ops gt96100_pci_ops = { + .read = gt96100_pcibios_read, + .write = gt96100_pcibios_write +}; diff --git a/arch/mips/pci/ops-it8172.c b/arch/mips/pci/ops-it8172.c new file mode 100644 index 00000000000..b7a8b9a6f9d --- /dev/null +++ b/arch/mips/pci/ops-it8172.c @@ -0,0 +1,215 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * IT8172 system controller specific pci support. + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * 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/init.h> + +#include <asm/it8172/it8172.h> +#include <asm/it8172/it8172_pci.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +static struct resource pci_mem_resource_1; + +static struct resource pci_io_resource = { + "io pci IO space", + 0x14018000, + 0x17FFFFFF, + IORESOURCE_IO +}; + +static struct resource pci_mem_resource_0 = { + "ext pci memory space 0/1", + 0x10101000, + 0x13FFFFFF, + IORESOURCE_MEM, + &pci_mem_resource_0, + NULL, + &pci_mem_resource_1 +}; + +static struct resource pci_mem_resource_1 = { + "ext pci memory space 2/3", + 0x1A000000, + 0x1FBFFFFF, + IORESOURCE_MEM, + &pci_mem_resource_0, + NULL, + NULL +}; + +extern struct pci_ops it8172_pci_ops; + +struct pci_controller it8172_controller = { + .pci_ops = &it8172_pci_ops, + .io_resource = &pci_io_resource, + .mem_resource = &pci_mem_resource_0, +}; + +static int it8172_pcibios_config_access(unsigned char access_type, + struct pci_bus *bus, + unsigned int devfn, int where, + u32 * data) +{ + /* + * config cycles are on 4 byte boundary only + */ + + /* Setup address */ + IT_WRITE(IT_CONFADDR, (bus->number << IT_BUSNUM_SHF) | + (devfn << IT_FUNCNUM_SHF) | (where & ~0x3)); + + if (access_type == PCI_ACCESS_WRITE) { + IT_WRITE(IT_CONFDATA, *data); + } else { + IT_READ(IT_CONFDATA, *data); + } + + /* + * Revisit: check for master or target abort. + */ + return 0; +} + + +/* + * We can't address 8 and 16 bit words directly. Instead we have to + * read/write a 32bit word and mask/modify the data we actually want. + */ +static write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) +{ + u32 data = 0; + + switch (size) { + case 1: + if (it8172_pcibios_config_access + (PCI_ACCESS_READ, dev, where, &data)) + return -1; + + *val = (data >> ((where & 3) << 3)) & 0xff; + + return PCIBIOS_SUCCESSFUL; + + case 2: + + if (where & 1) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (it8172_pcibios_config_access + (PCI_ACCESS_READ, dev, where, &data)) + return -1; + + *val = (data >> ((where & 3) << 3)) & 0xffff; + DBG("cfg read word: bus %d dev_fn %x where %x: val %x\n", + dev->bus->number, dev->devfn, where, *val); + + return PCIBIOS_SUCCESSFUL; + + case 4: + + if (where & 3) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (it8172_pcibios_config_access + (PCI_ACCESS_READ, dev, where, &data)) + return -1; + + *val = data; + + return PCIBIOS_SUCCESSFUL; + } +} + + +static write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) +{ + u32 data = 0; + + switch (size) { + case 1: + if (it8172_pcibios_config_access + (PCI_ACCESS_READ, dev, where, &data)) + return -1; + + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (it8172_pcibios_config_access + (PCI_ACCESS_WRITE, dev, where, &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; + + case 2: + if (where & 1) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (it8172_pcibios_config_access + (PCI_ACCESS_READ, dev, where, &data)) + eturn - 1; + + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + + if (it8172_pcibios_config_access + (PCI_ACCESS_WRITE, dev, where, &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; + + case 4: + if (where & 3) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (it8172_pcibios_config_access + (PCI_ACCESS_WRITE, dev, where, &val)) + return -1; + + return PCIBIOS_SUCCESSFUL; + } +} + +struct pci_ops it8172_pci_ops = { + .read = read_config, + .write = write_config, +}; diff --git a/arch/mips/pci/ops-mace.c b/arch/mips/pci/ops-mace.c new file mode 100644 index 00000000000..8008e31c5e8 --- /dev/null +++ b/arch/mips/pci/ops-mace.c @@ -0,0 +1,91 @@ +/* + * 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. + * + * Copyright (C) 2000, 2001 Keith M Wesolowski + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <asm/pci.h> +#include <asm/ip32/mace.h> + +#if 0 +# define DPRINTK(args...) printk(args); +#else +# define DPRINTK(args...) +#endif + +/* + * O2 has up to 5 PCI devices connected into the MACE bridge. The device + * map looks like this: + * + * 0 aic7xxx 0 + * 1 aic7xxx 1 + * 2 expansion slot + * 3 N/C + * 4 N/C + */ + +#define chkslot(_bus,_devfn) \ +do { \ + if ((_bus)->number > 0 || PCI_SLOT (_devfn) < 1 \ + || PCI_SLOT (_devfn) > 3) \ + return PCIBIOS_DEVICE_NOT_FOUND; \ +} while (0) + +#define mkaddr(_devfn, _reg) \ +((((_devfn) & 0xffUL) << 8) | ((_reg) & 0xfcUL)) + +static int +mace_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int reg, int size, u32 *val) +{ + chkslot(bus, devfn); + mace->pci.config_addr = mkaddr(devfn, reg); + switch (size) { + case 1: + *val = mace->pci.config_data.b[(reg & 3) ^ 3]; + break; + case 2: + *val = mace->pci.config_data.w[((reg >> 1) & 1) ^ 1]; + break; + case 4: + *val = mace->pci.config_data.l; + break; + } + + DPRINTK("read%d: reg=%08x,val=%02x\n", size * 8, reg, *val); + + return PCIBIOS_SUCCESSFUL; +} + +static int +mace_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int reg, int size, u32 val) +{ + chkslot(bus, devfn); + mace->pci.config_addr = mkaddr(devfn, reg); + switch (size) { + case 1: + mace->pci.config_data.b[(reg & 3) ^ 3] = val; + break; + case 2: + mace->pci.config_data.w[((reg >> 1) & 1) ^ 1] = val; + break; + case 4: + mace->pci.config_data.l = val; + break; + } + + DPRINTK("write%d: reg=%08x,val=%02x\n", size * 8, reg, val); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops mace_pci_ops = { + .read = mace_pci_read_config, + .write = mace_pci_write_config, +}; diff --git a/arch/mips/pci/ops-marvell.c b/arch/mips/pci/ops-marvell.c new file mode 100644 index 00000000000..1ac5c59199d --- /dev/null +++ b/arch/mips/pci/ops-marvell.c @@ -0,0 +1,93 @@ +/* + * 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. + * + * Copyright (C) 2003, 2004 Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> + +#include <asm/marvell.h> + +static int mv_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + struct mv_pci_controller *mvbc = bus->sysdata; + unsigned long address_reg, data_reg; + u32 address; + + address_reg = mvbc->config_addr; + data_reg = mvbc->config_vreg; + + /* Accessing device 31 crashes those Marvells. Since years. + Will they ever make sane controllers ... */ + if (PCI_SLOT(devfn) == 31) + return PCIBIOS_DEVICE_NOT_FOUND; + + address = (bus->number << 16) | (devfn << 8) | + (where & 0xfc) | 0x80000000; + + /* start the configuration cycle */ + MV_WRITE(address_reg, address); + + switch (size) { + case 1: + *val = MV_READ_8(data_reg + (where & 0x3)); + break; + + case 2: + *val = MV_READ_16(data_reg + (where & 0x3)); + break; + + case 4: + *val = MV_READ(data_reg); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int mv_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct mv_pci_controller *mvbc = bus->sysdata; + unsigned long address_reg, data_reg; + u32 address; + + address_reg = mvbc->config_addr; + data_reg = mvbc->config_vreg; + + /* Accessing device 31 crashes those Marvells. Since years. + Will they ever make sane controllers ... */ + if (PCI_SLOT(devfn) == 31) + return PCIBIOS_DEVICE_NOT_FOUND; + + address = (bus->number << 16) | (devfn << 8) | + (where & 0xfc) | 0x80000000; + + /* start the configuration cycle */ + MV_WRITE(address_reg, address); + + switch (size) { + case 1: + MV_WRITE_8(data_reg + (where & 0x3), val); + break; + + case 2: + MV_WRITE_16(data_reg + (where & 0x3), val); + break; + + case 4: + MV_WRITE(data_reg, val); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops mv_pci_ops = { + .read = mv_read_config, + .write = mv_write_config +}; diff --git a/arch/mips/pci/ops-msc.c b/arch/mips/pci/ops-msc.c new file mode 100644 index 00000000000..7bc099643a9 --- /dev/null +++ b/arch/mips/pci/ops-msc.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 1999, 2000, 2004, 2005 MIPS Technologies, Inc. + * All rights reserved. + * Authors: Carsten Langgaard <carstenl@mips.com> + * Maciej W. Rozycki <macro@mips.com> + * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * 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., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * MIPS boards specific PCI support. + * + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/mips-boards/msc01_pci.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +/* + * PCI configuration cycle AD bus definition + */ +/* Type 0 */ +#define PCI_CFG_TYPE0_REG_SHF 0 +#define PCI_CFG_TYPE0_FUNC_SHF 8 + +/* Type 1 */ +#define PCI_CFG_TYPE1_REG_SHF 0 +#define PCI_CFG_TYPE1_FUNC_SHF 8 +#define PCI_CFG_TYPE1_DEV_SHF 11 +#define PCI_CFG_TYPE1_BUS_SHF 16 + +static int msc_pcibios_config_access(unsigned char access_type, + struct pci_bus *bus, unsigned int devfn, int where, u32 * data) +{ + unsigned char busnum = bus->number; + unsigned char type; + u32 intr; + +#ifdef CONFIG_MIPS_BOARDS_GEN + if ((busnum == 0) && (PCI_SLOT(devfn) == 17)) { + /* MIPS Core boards have SOCit connected as device 17 */ + return -1; + } +#endif + + /* Clear status register bits. */ + MSC_WRITE(MSC01_PCI_INTSTAT, + (MSC01_PCI_INTCFG_MA_BIT | MSC01_PCI_INTCFG_TA_BIT)); + + /* Setup address */ + if (busnum == 0) + type = 0; /* Type 0 */ + else + type = 1; /* Type 1 */ + + MSC_WRITE(MSC01_PCI_CFGADDR, + ((busnum << MSC01_PCI_CFGADDR_BNUM_SHF) | + (PCI_SLOT(devfn) << MSC01_PCI_CFGADDR_DNUM_SHF) + | (PCI_FUNC(devfn) << + MSC01_PCI_CFGADDR_FNUM_SHF) | ((where / + 4) << + MSC01_PCI_CFGADDR_RNUM_SHF) + | (type))); + + /* Perform access */ + if (access_type == PCI_ACCESS_WRITE) + MSC_WRITE(MSC01_PCI_CFGDATA, *data); + else + MSC_READ(MSC01_PCI_CFGDATA, *data); + + /* Detect Master/Target abort */ + MSC_READ(MSC01_PCI_INTSTAT, intr); + if (intr & (MSC01_PCI_INTCFG_MA_BIT | + MSC01_PCI_INTCFG_TA_BIT)) { + /* Error occurred */ + + /* Clear bits */ + MSC_READ(MSC01_PCI_INTSTAT, intr); + MSC_WRITE(MSC01_PCI_INTSTAT, + (MSC01_PCI_INTCFG_MA_BIT | + MSC01_PCI_INTCFG_TA_BIT)); + + return -1; + } + + return 0; +} + + +/* + * We can't address 8 and 16 bit words directly. Instead we have to + * read/write a 32bit word and mask/modify the data we actually want. + */ +static int msc_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + u32 data = 0; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (msc_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, + &data)) + return -1; + + if (size == 1) + *val = (data >> ((where & 3) << 3)) & 0xff; + else if (size == 2) + *val = (data >> ((where & 3) << 3)) & 0xffff; + else + *val = data; + + return PCIBIOS_SUCCESSFUL; +} + +static int msc_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 data = 0; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (size == 4) + data = val; + else { + if (msc_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, + where, &data)) + return -1; + + if (size == 1) + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else if (size == 2) + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + } + + if (msc_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where, + &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops msc_pci_ops = { + .read = msc_pcibios_read, + .write = msc_pcibios_write +}; diff --git a/arch/mips/pci/ops-nile4.c b/arch/mips/pci/ops-nile4.c new file mode 100644 index 00000000000..a7169928b35 --- /dev/null +++ b/arch/mips/pci/ops-nile4.c @@ -0,0 +1,147 @@ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/bootinfo.h> + +#include <asm/lasat/lasat.h> +#include <asm/gt64120.h> +#include <asm/nile4.h> + +#define PCI_ACCESS_READ 0 +#define PCI_ACCESS_WRITE 1 + +#define LO(reg) (reg / 4) +#define HI(reg) (reg / 4 + 1) + +volatile unsigned long *const vrc_pciregs = (void *) Vrc5074_BASE; + +static spinlock_t nile4_pci_lock; + +static int nile4_pcibios_config_access(unsigned char access_type, + struct pci_bus *bus, unsigned int devfn, int where, u32 * val) +{ + unsigned char busnum = bus->number; + u32 adr, mask, err; + + if ((busnum == 0) && (PCI_SLOT(devfn) > 8)) + /* The addressing scheme chosen leaves room for just + * 8 devices on the first busnum (besides the PCI + * controller itself) */ + return PCIBIOS_DEVICE_NOT_FOUND; + + if ((busnum == 0) && (devfn == PCI_DEVFN(0, 0))) { + /* Access controller registers directly */ + if (access_type == PCI_ACCESS_WRITE) { + vrc_pciregs[(0x200 + where) >> 2] = *val; + } else { + *val = vrc_pciregs[(0x200 + where) >> 2]; + } + return PCIBIOS_SUCCESSFUL; + } + + /* Temporarily map PCI Window 1 to config space */ + mask = vrc_pciregs[LO(NILE4_PCIINIT1)]; + vrc_pciregs[LO(NILE4_PCIINIT1)] = 0x0000001a | (busnum ? 0x200 : 0); + + /* Clear PCI Error register. This also clears the Error Type + * bits in the Control register */ + vrc_pciregs[LO(NILE4_PCIERR)] = 0; + vrc_pciregs[HI(NILE4_PCIERR)] = 0; + + /* Setup address */ + if (busnum == 0) + adr = + KSEG1ADDR(PCI_WINDOW1) + + ((1 << (PCI_SLOT(devfn) + 15)) | (PCI_FUNC(devfn) << 8) + | (where & ~3)); + else + adr = KSEG1ADDR(PCI_WINDOW1) | (busnum << 16) | (devfn << 8) | + (where & ~3); + + if (access_type == PCI_ACCESS_WRITE) + *(u32 *) adr = *val; + else + *val = *(u32 *) adr; + + /* Check for master or target abort */ + err = (vrc_pciregs[HI(NILE4_PCICTRL)] >> 5) & 0x7; + + /* Restore PCI Window 1 */ + vrc_pciregs[LO(NILE4_PCIINIT1)] = mask; + + if (err) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static int nile4_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + unsigned long flags; + u32 data = 0; + int err; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + spin_lock_irqsave(&nile4_pci_lock, flags); + err = nile4_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, + &data); + spin_unlock_irqrestore(&nile4_pci_lock, flags); + + if (err) + return err; + + if (size == 1) + *val = (data >> ((where & 3) << 3)) & 0xff; + else if (size == 2) + *val = (data >> ((where & 3) << 3)) & 0xffff; + else + *val = data; + + return PCIBIOS_SUCCESSFUL; +} + +static int nile4_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + unsigned long flags; + u32 data = 0; + int err; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + spin_lock_irqsave(&nile4_pci_lock, flags); + err = nile4_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where, + &data); + spin_unlock_irqrestore(&nile4_pci_lock, flags); + + if (err) + return err; + + if (size == 1) + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else if (size == 2) + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else + data = val; + + if (nile4_pcibios_config_access + (PCI_ACCESS_WRITE, bus, devfn, where, &data)) + return -1; + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops nile4_pci_ops = { + .read = nile4_pcibios_read, + .write = nile4_pcibios_write, +}; diff --git a/arch/mips/pci/ops-sni.c b/arch/mips/pci/ops-sni.c new file mode 100644 index 00000000000..62bdd19c7f8 --- /dev/null +++ b/arch/mips/pci/ops-sni.c @@ -0,0 +1,89 @@ +/* + * 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. + * + * SNI specific PCI support for RM200/RM300. + * + * Copyright (C) 1997 - 2000, 2003 Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <asm/sni.h> + +/* + * It seems that on the RM200 only lower 3 bits of the 5 bit PCI device + * address are decoded. We therefore manually have to reject attempts at + * reading outside this range. Being on the paranoid side we only do this + * test for bus 0 and hope forwarding and decoding work properly for any + * subordinated busses. + * + * ASIC PCI only supports type 1 config cycles. + */ +static int set_config_address(unsigned int busno, unsigned int devfn, int reg) +{ + if ((devfn > 255) || (reg > 255)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (busno == 0 && devfn >= PCI_DEVFN(8, 0)) + return PCIBIOS_DEVICE_NOT_FOUND; + + *(volatile u32 *)PCIMT_CONFIG_ADDRESS = + ((busno & 0xff) << 16) | + ((devfn & 0xff) << 8) | + (reg & 0xfc); + + return PCIBIOS_SUCCESSFUL; +} + +static int pcimt_read(struct pci_bus *bus, unsigned int devfn, int reg, + int size, u32 * val) +{ + int res; + + if ((res = set_config_address(bus->number, devfn, reg))) + return res; + + switch (size) { + case 1: + *val = *(volatile u8 *) (PCIMT_CONFIG_DATA + (reg & 3)); + break; + case 2: + *val = *(volatile u16 *) (PCIMT_CONFIG_DATA + (reg & 2)); + break; + case 4: + *val = *(volatile u32 *) PCIMT_CONFIG_DATA; + break; + } + + return 0; +} + +static int pcimt_write(struct pci_bus *bus, unsigned int devfn, int reg, + int size, u32 val) +{ + int res; + + if ((res = set_config_address(bus->number, devfn, reg))) + return res; + + switch (size) { + case 1: + *(volatile u8 *) (PCIMT_CONFIG_DATA + (reg & 3)) = val; + break; + case 2: + *(volatile u16 *) (PCIMT_CONFIG_DATA + (reg & 2)) = val; + break; + case 4: + *(volatile u32 *) PCIMT_CONFIG_DATA = val; + break; + } + + return 0; +} + +struct pci_ops sni_pci_ops = { + .read = pcimt_read, + .write = pcimt_write, +}; diff --git a/arch/mips/pci/ops-titan-ht.c b/arch/mips/pci/ops-titan-ht.c new file mode 100644 index 00000000000..46c636c27e0 --- /dev/null +++ b/arch/mips/pci/ops-titan-ht.c @@ -0,0 +1,125 @@ +/* + * 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 <linux/delay.h> +#include <asm/io.h> + +#include <asm/titan_dep.h> + +static int titan_ht_config_read_dword(struct pci_bus *bus, unsigned int devfn, + int offset, u32 * val) +{ + volatile uint32_t address; + int busno; + + busno = bus->number; + + address = (busno << 16) | (devfn << 8) | (offset & 0xfc) | 0x80000000; + if (busno != 0) + address |= 1; + + /* + * RM9000 HT Errata: Issue back to back HT config + * transcations. Issue a BIU sync before and + * after the HT cycle + */ + + *(volatile int32_t *) 0xfb0000f0 |= 0x2; + + udelay(30); + + *(volatile int32_t *) 0xfb0006f8 = address; + *(val) = *(volatile int32_t *) 0xfb0006fc; + + udelay(30); + + * (volatile int32_t *) 0xfb0000f0 |= 0x2; + + return PCIBIOS_SUCCESSFUL; +} + +static int titan_ht_config_read(struct pci_bus *bus, unsigned int devfn, + int offset, int size, u32 * val) +{ + uint32_t dword; + + titan_ht_config_read_dword(bus, devfn, offset, &dword); + + dword >>= ((offset & 3) << 3); + dword &= (0xffffffffU >> ((4 - size) << 8)); + + return PCIBIOS_SUCCESSFUL; +} + +static inline int titan_ht_config_write_dword(struct pci_bus *bus, + unsigned int devfn, int offset, u32 val) +{ + volatile uint32_t address; + int busno; + + busno = bus->number; + + address = (busno << 16) | (devfn << 8) | (offset & 0xfc) | 0x80000000; + if (busno != 0) + address |= 1; + + *(volatile int32_t *) 0xfb0000f0 |= 0x2; + + udelay(30); + + *(volatile int32_t *) 0xfb0006f8 = address; + *(volatile int32_t *) 0xfb0006fc = val; + + udelay(30); + + *(volatile int32_t *) 0xfb0000f0 |= 0x2; + + return PCIBIOS_SUCCESSFUL; +} + +static int titan_ht_config_write(struct pci_bus *bus, unsigned int devfn, + int offset, int size, u32 val) +{ + uint32_t val1, val2, mask; + + titan_ht_config_read_dword(bus, devfn, offset, &val2); + + val1 = val << ((offset & 3) << 3); + mask = ~(0xffffffffU >> ((4 - size) << 8)); + val2 &= ~(mask << ((offset & 3) << 8)); + + titan_ht_config_write_dword(bus, devfn, offset, val1 | val2); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops titan_ht_pci_ops = { + .read = titan_ht_config_read, + .write = titan_ht_config_write, +}; diff --git a/arch/mips/pci/ops-titan.c b/arch/mips/pci/ops-titan.c new file mode 100644 index 00000000000..233ec6f2054 --- /dev/null +++ b/arch/mips/pci/ops-titan.c @@ -0,0 +1,100 @@ +/* + * 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 <asm/titan_dep.h> + +static int titan_read_config(struct pci_bus *bus, unsigned int devfn, int reg, + int size, u32 * val) +{ + uint32_t address, tmp; + int dev, busno, func; + + busno = bus->number; + dev = PCI_SLOT(devfn); + func = PCI_FUNC(devfn); + + address = (busno << 16) | (dev << 11) | (func << 8) | + (reg & 0xfc) | 0x80000000; + + + /* start the configuration cycle */ + TITAN_WRITE(TITAN_PCI_0_CONFIG_ADDRESS, address); + tmp = TITAN_READ(TITAN_PCI_0_CONFIG_DATA) >> ((reg & 3) << 3); + + switch (size) { + case 1: + tmp &= 0xff; + case 2: + tmp &= 0xffff; + } + *val = tmp; + + return PCIBIOS_SUCCESSFUL; +} + +static int titan_write_config(struct pci_bus *bus, unsigned int devfn, int reg, + int size, u32 val) +{ + uint32_t address; + int dev, busno, func; + + busno = bus->number; + dev = PCI_SLOT(devfn); + func = PCI_FUNC(devfn); + + address = (busno << 16) | (dev << 11) | (func << 8) | + (reg & 0xfc) | 0x80000000; + + /* start the configuration cycle */ + TITAN_WRITE(TITAN_PCI_0_CONFIG_ADDRESS, address); + + /* write the data */ + switch (size) { + case 1: + TITAN_WRITE_8(TITAN_PCI_0_CONFIG_DATA + (~reg & 0x3), val); + break; + + case 2: + TITAN_WRITE_16(TITAN_PCI_0_CONFIG_DATA + (~reg & 0x2), val); + break; + + case 4: + TITAN_WRITE(TITAN_PCI_0_CONFIG_DATA, val); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +/* + * Titan PCI structure + */ +struct pci_ops titan_pci_ops = { + titan_read_config, + titan_write_config, +}; diff --git a/arch/mips/pci/ops-tx3927.c b/arch/mips/pci/ops-tx3927.c new file mode 100644 index 00000000000..0e0daadc303 --- /dev/null +++ b/arch/mips/pci/ops-tx3927.c @@ -0,0 +1,391 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ahennessy@mvista.com + * + * Copyright (C) 2000-2001 Toshiba Corporation + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * Based on arch/mips/ddb5xxx/ddb5477/pci_ops.c + * + * Define the pci_ops for JMR3927. + * + * Much of the code is derived from the original DDB5074 port by + * Geert Uytterhoeven <geert@sonycom.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/init.h> + +#include <asm/addrspace.h> +#include <asm/jmr3927/jmr3927.h> +#include <asm/debug.h> + +static inline int mkaddr(unsigned char bus, unsigned char dev_fn, + unsigned char where) +{ + if (bus == 0 && dev_fn >= PCI_DEVFN(TX3927_PCIC_MAX_DEVNU, 0)) + return PCIBIOS_DEVICE_NOT_FOUND; + + tx3927_pcicptr->ica = ((bus & 0xff) << 0x10) | + ((dev_fn & 0xff) << 0x08) | + (where & 0xfc); + + /* clear M_ABORT and Disable M_ABORT Int. */ + tx3927_pcicptr->pcistat |= PCI_STATUS_REC_MASTER_ABORT; + tx3927_pcicptr->pcistatim &= ~PCI_STATUS_REC_MASTER_ABORT; + + return PCIBIOS_SUCCESSFUL; +} + +static inline int check_abort(void) +{ + if (tx3927_pcicptr->pcistat & PCI_STATUS_REC_MASTER_ABORT) + tx3927_pcicptr->pcistat |= PCI_STATUS_REC_MASTER_ABORT; + tx3927_pcicptr->pcistatim |= PCI_STATUS_REC_MASTER_ABORT; + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static int jmr3927_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + int ret, busno; + + /* check if the bus is top-level */ + if (bus->parent != NULL) + busno = bus->number; + + ret = mkaddr(busno, devfn, where); + if (ret) + return ret; + + switch (size) { + case 1: + *val = *(volatile u8 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 3)); + break; + + case 2: + *val = le16_to_cpu(*(volatile u16 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 3))); + break; + + case 4: + *val = le32_to_cpu(tx3927_pcicptr->icd); + break; + } + + return check_abort(); +} + +static int jmr3927_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + int ret, busno; + + /* check if the bus is top-level */ + if (bus->parent != NULL) + bus = bus->number; + else + bus = 0; + + ret = mkaddr(busno, devfn, where); + if (ret) + return ret; + + switch (size) { + case 1: + *(volatile u8 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 3)) = val; + break; + + case 2: + *(volatile u16 *) (unsigned longulong) & tx3927_pcicptr->icd | (where & 2)) = + cpu_to_le16(val); + break; + + case 4: + tx3927_pcicptr->icd = cpu_to_le32(val); + } + + if (tx3927_pcicptr->pcistat & PCI_STATUS_REC_MASTER_ABORT) + tx3927_pcicptr->pcistat |= PCI_STATUS_REC_MASTER_ABORT; + tx3927_pcicptr->pcistatim |= PCI_STATUS_REC_MASTER_ABORT; + return PCIBIOS_DEVICE_NOT_FOUND; + + return check_abort(); +} + +struct pci_ops jmr3927_pci_ops = { + jmr3927_pcibios_read_config, + jmr3927_pcibios_write_config, +}; + + +#ifndef JMR3927_INIT_INDIRECT_PCI + +inline unsigned long tc_readl(volatile __u32 * addr) +{ + return readl(addr); +} + +inline void tc_writel(unsigned long data, volatile __u32 * addr) +{ + writel(data, addr); +} +#else + +unsigned long tc_readl(volatile __u32 * addr) +{ + unsigned long val; + + addr = PHYSADDR(addr); + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) addr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_MEMREAD << PCI_IPCIBE_ICMD_SHIFT) | + PCI_IPCIBE_IBE_LONG; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + val = + le32_to_cpu(*(volatile u32 *) (ulong) & tx3927_pcicptr-> + ipcidata); + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; + return val; +} + +void tc_writel(unsigned long data, volatile __u32 * addr) +{ + addr = PHYSADDR(addr); + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcidata = + cpu_to_le32(data); + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) addr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_MEMWRITE << PCI_IPCIBE_ICMD_SHIFT) | + PCI_IPCIBE_IBE_LONG; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; +} + +unsigned char tx_ioinb(unsigned char *addr) +{ + unsigned long val; + __u32 ioaddr; + int offset; + int byte; + + ioaddr = (unsigned long) addr; + offset = ioaddr & 0x3; + if (offset == 0) + byte = 0x7; + else if (offset == 1) + byte = 0xb; + else if (offset == 2) + byte = 0xd; + else if (offset == 3) + byte = 0xe; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) ioaddr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | byte; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + val = + le32_to_cpu(*(volatile u32 *) (ulong) & tx3927_pcicptr-> + ipcidata); + val = val & 0xff; + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; + return val; +} + +void tx_iooutb(unsigned long data, unsigned char *addr) +{ + __u32 ioaddr; + int offset; + int byte; + + data = data | (data << 8) | (data << 16) | (data << 24); + ioaddr = (unsigned long) addr; + offset = ioaddr & 0x3; + if (offset == 0) + byte = 0x7; + else if (offset == 1) + byte = 0xb; + else if (offset == 2) + byte = 0xd; + else if (offset == 3) + byte = 0xe; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcidata = data; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) ioaddr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | byte; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; +} + +unsigned short tx_ioinw(unsigned short *addr) +{ + unsigned long val; + __u32 ioaddr; + int offset; + int byte; + + ioaddr = (unsigned long) addr; + offset = ioaddr & 0x3; + if (offset == 0) + byte = 0x3; + else if (offset == 2) + byte = 0xc; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) ioaddr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | byte; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + val = + le32_to_cpu(*(volatile u32 *) (ulong) & tx3927_pcicptr-> + ipcidata); + val = val & 0xffff; + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; + return val; + +} + +void tx_iooutw(unsigned long data, unsigned short *addr) +{ + __u32 ioaddr; + int offset; + int byte; + + data = data | (data << 16); + ioaddr = (unsigned long) addr; + offset = ioaddr & 0x3; + if (offset == 0) + byte = 0x3; + else if (offset == 2) + byte = 0xc; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcidata = data; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) ioaddr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | byte; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; +} + +unsigned long tx_ioinl(unsigned int *addr) +{ + unsigned long val; + __u32 ioaddr; + + ioaddr = (unsigned long) addr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) ioaddr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_IOREAD << PCI_IPCIBE_ICMD_SHIFT) | + PCI_IPCIBE_IBE_LONG; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + val = + le32_to_cpu(*(volatile u32 *) (ulong) & tx3927_pcicptr-> + ipcidata); + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; + return val; +} + +void tx_iooutl(unsigned long data, unsigned int *addr) +{ + __u32 ioaddr; + + ioaddr = (unsigned long) addr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcidata = + cpu_to_le32(data); + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipciaddr = + (unsigned long) ioaddr; + *(volatile u32 *) (ulong) & tx3927_pcicptr->ipcibe = + (PCI_IPCIBE_ICMD_IOWRITE << PCI_IPCIBE_ICMD_SHIFT) | + PCI_IPCIBE_IBE_LONG; + while (!(tx3927_pcicptr->istat & PCI_ISTAT_IDICC)); + /* clear by setting */ + tx3927_pcicptr->istat |= PCI_ISTAT_IDICC; +} + +void tx_insbyte(unsigned char *addr, void *buffer, unsigned int count) +{ + unsigned char *ptr = (unsigned char *) buffer; + + while (count--) { + *ptr++ = tx_ioinb(addr); + } +} + +void tx_insword(unsigned short *addr, void *buffer, unsigned int count) +{ + unsigned short *ptr = (unsigned short *) buffer; + + while (count--) { + *ptr++ = tx_ioinw(addr); + } +} + +void tx_inslong(unsigned int *addr, void *buffer, unsigned int count) +{ + unsigned long *ptr = (unsigned long *) buffer; + + while (count--) { + *ptr++ = tx_ioinl(addr); + } +} + +void tx_outsbyte(unsigned char *addr, void *buffer, unsigned int count) +{ + unsigned char *ptr = (unsigned char *) buffer; + + while (count--) { + tx_iooutb(*ptr++, addr); + } +} + +void tx_outsword(unsigned short *addr, void *buffer, unsigned int count) +{ + unsigned short *ptr = (unsigned short *) buffer; + + while (count--) { + tx_iooutw(*ptr++, addr); + } +} + +void tx_outslong(unsigned int *addr, void *buffer, unsigned int count) +{ + unsigned long *ptr = (unsigned long *) buffer; + + while (count--) { + tx_iooutl(*ptr++, addr); + } +} +#endif diff --git a/arch/mips/pci/ops-tx4927.c b/arch/mips/pci/ops-tx4927.c new file mode 100644 index 00000000000..2a9d7227fe8 --- /dev/null +++ b/arch/mips/pci/ops-tx4927.c @@ -0,0 +1,209 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ahennessy@mvista.com + * + * Copyright (C) 2000-2001 Toshiba Corporation + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * Based on arch/mips/ddb5xxx/ddb5477/pci_ops.c + * + * Define the pci_ops for the Toshiba rbtx4927 + * + * Much of the code is derived from the original DDB5074 port by + * Geert Uytterhoeven <geert@sonycom.com> + * + * Copyright 2004 MontaVista Software Inc. + * Author: Manish Lachwani (mlachwani@mvista.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/init.h> + +#include <asm/addrspace.h> +#include <asm/byteorder.h> +#include <asm/tx4927/tx4927_pci.h> + +/* initialize in setup */ +struct resource pci_io_resource = { + .name = "TX4927 PCI IO SPACE", + .start = 0x1000, + .end = (0x1000 + (TX4927_PCIIO_SIZE)) - 1, + .flags = IORESOURCE_IO +}; + +/* initialize in setup */ +struct resource pci_mem_resource = { + .name = "TX4927 PCI MEM SPACE", + .start = TX4927_PCIMEM, + .end = TX4927_PCIMEM + TX4927_PCIMEM_SIZE - 1, + .flags = IORESOURCE_MEM +}; + +static int mkaddr(int bus, int dev_fn, int where, int *flagsp) +{ + if (bus > 0) { + /* Type 1 configuration */ + tx4927_pcicptr->g2pcfgadrs = ((bus & 0xff) << 0x10) | + ((dev_fn & 0xff) << 0x08) | (where & 0xfc) | 1; + } else { + if (dev_fn >= PCI_DEVFN(TX4927_PCIC_MAX_DEVNU, 0)) + return -1; + + /* Type 0 configuration */ + tx4927_pcicptr->g2pcfgadrs = ((bus & 0xff) << 0x10) | + ((dev_fn & 0xff) << 0x08) | (where & 0xfc); + } + /* clear M_ABORT and Disable M_ABORT Int. */ + tx4927_pcicptr->pcistatus = + (tx4927_pcicptr->pcistatus & 0x0000ffff) | + (PCI_STATUS_REC_MASTER_ABORT << 16); + tx4927_pcicptr->pcimask &= ~PCI_STATUS_REC_MASTER_ABORT; + return 0; +} + +static int check_abort(int flags) +{ + int code = PCIBIOS_SUCCESSFUL; + if (tx4927_pcicptr-> + pcistatus & (PCI_STATUS_REC_MASTER_ABORT << 16)) { + tx4927_pcicptr->pcistatus = + (tx4927_pcicptr-> + pcistatus & 0x0000ffff) | (PCI_STATUS_REC_MASTER_ABORT + << 16); + tx4927_pcicptr->pcimask |= PCI_STATUS_REC_MASTER_ABORT; + code = PCIBIOS_DEVICE_NOT_FOUND; + } + return code; +} + +static int tx4927_pcibios_read_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 * val) +{ + int flags, retval, dev, busno, func; + + busno = bus->number; + dev = PCI_SLOT(devfn); + func = PCI_FUNC(devfn); + + /* check if the bus is top-level */ + if (bus->parent != NULL) { + busno = bus->number; + } else { + busno = 0; + } + + if (mkaddr(busno, devfn, where, &flags)) + return -1; + + switch (size) { + case 1: + *val = *(volatile u8 *) ((ulong) & tx4927_pcicptr-> + g2pcfgdata | +#ifdef __LITTLE_ENDIAN + (where & 3)); +#else + ((where & 0x3) ^ 0x3)); +#endif + break; + case 2: + *val = *(volatile u16 *) ((ulong) & tx4927_pcicptr-> + g2pcfgdata | +#ifdef __LITTLE_ENDIAN + (where & 3)); +#else + ((where & 0x3) ^ 0x2)); +#endif + break; + case 4: + *val = tx4927_pcicptr->g2pcfgdata; + break; + } + + retval = check_abort(flags); + if (retval == PCIBIOS_DEVICE_NOT_FOUND) + *val = 0xffffffff; + + return retval; +} + +static int tx4927_pcibios_write_config(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 val) +{ + int flags, dev, busno, func; + busno = bus->number; + dev = PCI_SLOT(devfn); + func = PCI_FUNC(devfn); + + /* check if the bus is top-level */ + if (bus->parent != NULL) { + busno = bus->number; + } else { + busno = 0; + } + + if (mkaddr(busno, devfn, where, &flags)) + return -1; + + switch (size) { + case 1: + *(volatile u8 *) ((ulong) & tx4927_pcicptr-> + g2pcfgdata | +#ifdef __LITTLE_ENDIAN + (where & 3)) = val; +#else + ((where & 0x3) ^ 0x3)) = val; +#endif + break; + + case 2: + *(volatile u16 *) ((ulong) & tx4927_pcicptr-> + g2pcfgdata | +#ifdef __LITTLE_ENDIAN + (where & 3)) = val; +#else + ((where & 0x3) ^ 0x2)) = val; +#endif + break; + case 4: + tx4927_pcicptr->g2pcfgdata = val; + break; + } + + return check_abort(flags); +} + +struct pci_ops tx4927_pci_ops = { + tx4927_pcibios_read_config, + tx4927_pcibios_write_config +}; + +/* + * h/w only supports devices 0x00 to 0x14 + */ +struct pci_controller tx4927_controller = { + .pci_ops = &tx4927_pci_ops, + .io_resource = &pci_io_resource, + .mem_resource = &pci_mem_resource, +}; diff --git a/arch/mips/pci/ops-vr41xx.c b/arch/mips/pci/ops-vr41xx.c new file mode 100644 index 00000000000..44654605e46 --- /dev/null +++ b/arch/mips/pci/ops-vr41xx.c @@ -0,0 +1,126 @@ +/* + * ops-vr41xx.c, PCI configuration routines for the PCIU of NEC VR4100 series. + * + * Copyright (C) 2001-2003 MontaVista Software Inc. + * Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com> + * Copyright (C) 2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Changes: + * MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com> + * - New creation, NEC VR4122 and VR4131 are supported. + */ +#include <linux/pci.h> +#include <linux/types.h> + +#include <asm/io.h> + +#define PCICONFDREG KSEG1ADDR(0x0f000c14) +#define PCICONFAREG KSEG1ADDR(0x0f000c18) + +static inline int set_pci_configuration_address(unsigned char number, + unsigned int devfn, int where) +{ + if (number == 0) { + /* + * Type 0 configuration + */ + if (PCI_SLOT(devfn) < 11 || where > 0xff) + return -EINVAL; + + writel((1U << PCI_SLOT(devfn)) | (PCI_FUNC(devfn) << 8) | + (where & 0xfc), PCICONFAREG); + } else { + /* + * Type 1 configuration + */ + if (where > 0xff) + return -EINVAL; + + writel(((uint32_t)number << 16) | ((devfn & 0xff) << 8) | + (where & 0xfc) | 1U, PCICONFAREG); + } + + return 0; +} + +static int pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, + int size, uint32_t *val) +{ + uint32_t data; + + *val = 0xffffffffU; + if (set_pci_configuration_address(bus->number, devfn, where) < 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + data = readl(PCICONFDREG); + + switch (size) { + case 1: + *val = (data >> ((where & 3) << 3)) & 0xffU; + break; + case 2: + *val = (data >> ((where & 2) << 3)) & 0xffffU; + break; + case 4: + *val = data; + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, + int size, uint32_t val) +{ + uint32_t data; + int shift; + + if (set_pci_configuration_address(bus->number, devfn, where) < 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + data = readl(PCICONFDREG); + + switch (size) { + case 1: + shift = (where & 3) << 3; + data &= ~(0xffU << shift); + data |= ((val & 0xffU) << shift); + break; + case 2: + shift = (where & 2) << 3; + data &= ~(0xffffU << shift); + data |= ((val & 0xffffU) << shift); + break; + case 4: + data = val; + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + + writel(data, PCICONFDREG); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops vr41xx_pci_ops = { + .read = pci_config_read, + .write = pci_config_write, +}; diff --git a/arch/mips/pci/pci-ddb5074.c b/arch/mips/pci/pci-ddb5074.c new file mode 100644 index 00000000000..73f9ceeb2f5 --- /dev/null +++ b/arch/mips/pci/pci-ddb5074.c @@ -0,0 +1,79 @@ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> + +#include <asm/debug.h> + +#include <asm/ddb5xxx/ddb5xxx.h> + +static struct resource extpci_io_resource = { + "pci IO space", + 0x1000, /* leave some room for ISA bus */ + DDB_PCI_IO_SIZE - 1, + IORESOURCE_IO +}; + +static struct resource extpci_mem_resource = { + "pci memory space", + DDB_PCI_MEM_BASE + 0x00100000, /* leave 1 MB for RTC */ + DDB_PCI_MEM_BASE + DDB_PCI_MEM_SIZE - 1, + IORESOURCE_MEM +}; + +extern struct pci_ops ddb5476_ext_pci_ops; + +struct pci_controller ddb5476_controller = { + .pci_ops = &ddb5476_ext_pci_ops, + .io_resource = &extpci_io_resource, + .mem_resource = &extpci_mem_resource, +}; + +#define PCI_EXT_INTA 8 +#define PCI_EXT_INTB 9 +#define PCI_EXT_INTC 10 +#define PCI_EXT_INTD 11 +#define PCI_EXT_INTE 12 + +#define MAX_SLOT_NUM 14 + +static unsigned char irq_map[MAX_SLOT_NUM] = { + [ 0] = nile4_to_irq(PCI_EXT_INTE), + [ 1] = nile4_to_irq(PCI_EXT_INTA), + [ 2] = nile4_to_irq(PCI_EXT_INTA), + [ 3] = nile4_to_irq(PCI_EXT_INTB), + [ 4] = nile4_to_irq(PCI_EXT_INTC), + [ 5] = nile4_to_irq(NILE4_INT_UART), + [10] = nile4_to_irq(PCI_EXT_INTE), + [13] = nile4_to_irq(PCI_EXT_INTE), +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_map[slot]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +void __init ddb_pci_reset_bus(void) +{ + u32 temp; + + /* + * I am not sure about the "official" procedure, the following + * steps work as far as I know: + * We first set PCI cold reset bit (bit 31) in PCICTRL-H. + * Then we clear the PCI warm reset bit (bit 30) to 0 in PCICTRL-H. + * The same is true for both PCI channels. + */ + temp = ddb_in32(DDB_PCICTRL + 4); + temp |= 0x80000000; + ddb_out32(DDB_PCICTRL + 4, temp); + temp &= ~0xc0000000; + ddb_out32(DDB_PCICTRL + 4, temp); + +} diff --git a/arch/mips/pci/pci-ddb5476.c b/arch/mips/pci/pci-ddb5476.c new file mode 100644 index 00000000000..90dd4950980 --- /dev/null +++ b/arch/mips/pci/pci-ddb5476.c @@ -0,0 +1,93 @@ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> + +#include <asm/debug.h> + +#include <asm/ddb5xxx/ddb5xxx.h> + +static struct resource extpci_io_resource = { + "pci IO space", + 0x1000, /* leave some room for ISA bus */ + DDB_PCI_IO_SIZE - 1, + IORESOURCE_IO +}; + +static struct resource extpci_mem_resource = { + "pci memory space", + DDB_PCI_MEM_BASE + 0x00100000, /* leave 1 MB for RTC */ + DDB_PCI_MEM_BASE + DDB_PCI_MEM_SIZE - 1, + IORESOURCE_MEM +}; + +extern struct pci_ops ddb5476_ext_pci_ops; + +struct pci_controller ddb5476_controller = { + .pci_ops = &ddb5476_ext_pci_ops, + .io_resource = &extpci_io_resource, + .mem_resource = &extpci_mem_resource +}; + + +/* + * we fix up irqs based on the slot number. + * The first entry is at AD:11. + * + * This does not work for devices on sub-buses yet. + */ + +/* + * temporary + */ + +#define PCI_EXT_INTA 8 +#define PCI_EXT_INTB 9 +#define PCI_EXT_INTC 10 +#define PCI_EXT_INTD 11 +#define PCI_EXT_INTE 12 + +/* + * based on ddb5477 manual page 11 + */ +#define MAX_SLOT_NUM 21 +static unsigned char irq_map[MAX_SLOT_NUM] = { + [ 2] = 9, /* AD:13 USB */ + [ 3] = 10, /* AD:14 PMU */ + [ 5] = 0, /* AD:16 P2P bridge */ + [ 6] = nile4_to_irq(PCI_EXT_INTB), /* AD:17 */ + [ 7] = nile4_to_irq(PCI_EXT_INTC), /* AD:18 */ + [ 8] = nile4_to_irq(PCI_EXT_INTD), /* AD:19 */ + [ 9] = nile4_to_irq(PCI_EXT_INTA), /* AD:20 */ + [13] = 14, /* AD:24 HD controller, M5229 */ +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_map[slot]; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +void __init ddb_pci_reset_bus(void) +{ + u32 temp; + + /* + * I am not sure about the "official" procedure, the following + * steps work as far as I know: + * We first set PCI cold reset bit (bit 31) in PCICTRL-H. + * Then we clear the PCI warm reset bit (bit 30) to 0 in PCICTRL-H. + * The same is true for both PCI channels. + */ + temp = ddb_in32(DDB_PCICTRL + 4); + temp |= 0x80000000; + ddb_out32(DDB_PCICTRL + 4, temp); + temp &= ~0xc0000000; + ddb_out32(DDB_PCICTRL + 4, temp); + +} diff --git a/arch/mips/pci/pci-ddb5477.c b/arch/mips/pci/pci-ddb5477.c new file mode 100644 index 00000000000..4ddd53eaf65 --- /dev/null +++ b/arch/mips/pci/pci-ddb5477.c @@ -0,0 +1,207 @@ +/* + * PCI code for DDB5477. + * + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> + +#include <asm/bootinfo.h> +#include <asm/debug.h> + +#include <asm/ddb5xxx/ddb5xxx.h> + +static struct resource extpci_io_resource = { + "ext pci IO space", + DDB_PCI0_IO_BASE - DDB_PCI_IO_BASE + 0x4000, + DDB_PCI0_IO_BASE - DDB_PCI_IO_BASE + DDB_PCI0_IO_SIZE - 1, + IORESOURCE_IO +}; + +static struct resource extpci_mem_resource = { + "ext pci memory space", + DDB_PCI0_MEM_BASE + 0x100000, + DDB_PCI0_MEM_BASE + DDB_PCI0_MEM_SIZE - 1, + IORESOURCE_MEM +}; + +static struct resource iopci_io_resource = { + "io pci IO space", + DDB_PCI1_IO_BASE - DDB_PCI_IO_BASE, + DDB_PCI1_IO_BASE - DDB_PCI_IO_BASE + DDB_PCI1_IO_SIZE - 1, + IORESOURCE_IO +}; + +static struct resource iopci_mem_resource = { + "ext pci memory space", + DDB_PCI1_MEM_BASE, + DDB_PCI1_MEM_BASE + DDB_PCI1_MEM_SIZE - 1, + IORESOURCE_MEM +}; + +extern struct pci_ops ddb5477_ext_pci_ops; +extern struct pci_ops ddb5477_io_pci_ops; + +struct pci_controller ddb5477_ext_controller = { + .pci_ops = &ddb5477_ext_pci_ops, + .io_resource = &extpci_io_resource, + .mem_resource = &extpci_mem_resource +}; + +struct pci_controller ddb5477_io_controller = { + .pci_ops = &ddb5477_io_pci_ops, + .io_resource = &iopci_io_resource, + .mem_resource = &iopci_mem_resource +}; + + + +/* + * we fix up irqs based on the slot number. + * The first entry is at AD:11. + * Fortunately this works because, although we have two pci buses, + * they all have different slot numbers (except for rockhopper slot 20 + * which is handled below). + * + */ + +/* + * irq mapping : device -> pci int # -> vrc4377 irq# , + * ddb5477 board manual page 4 and vrc5477 manual page 46 + */ + +/* + * based on ddb5477 manual page 11 + */ +#define MAX_SLOT_NUM 21 +static unsigned char irq_map[MAX_SLOT_NUM] = { + /* SLOT: 0, AD:11 */ 0xff, + /* SLOT: 1, AD:12 */ 0xff, + /* SLOT: 2, AD:13 */ 0xff, + /* SLOT: 3, AD:14 */ 0xff, + /* SLOT: 4, AD:15 */ VRC5477_IRQ_INTA, /* onboard tulip */ + /* SLOT: 5, AD:16 */ VRC5477_IRQ_INTB, /* slot 1 */ + /* SLOT: 6, AD:17 */ VRC5477_IRQ_INTC, /* slot 2 */ + /* SLOT: 7, AD:18 */ VRC5477_IRQ_INTD, /* slot 3 */ + /* SLOT: 8, AD:19 */ VRC5477_IRQ_INTE, /* slot 4 */ + /* SLOT: 9, AD:20 */ 0xff, + /* SLOT: 10, AD:21 */ 0xff, + /* SLOT: 11, AD:22 */ 0xff, + /* SLOT: 12, AD:23 */ 0xff, + /* SLOT: 13, AD:24 */ 0xff, + /* SLOT: 14, AD:25 */ 0xff, + /* SLOT: 15, AD:26 */ 0xff, + /* SLOT: 16, AD:27 */ 0xff, + /* SLOT: 17, AD:28 */ 0xff, + /* SLOT: 18, AD:29 */ VRC5477_IRQ_IOPCI_INTC, /* vrc5477 ac97 */ + /* SLOT: 19, AD:30 */ VRC5477_IRQ_IOPCI_INTB, /* vrc5477 usb peri */ + /* SLOT: 20, AD:31 */ VRC5477_IRQ_IOPCI_INTA, /* vrc5477 usb host */ +}; +static unsigned char rockhopperII_irq_map[MAX_SLOT_NUM] = { + /* SLOT: 0, AD:11 */ 0xff, + /* SLOT: 1, AD:12 */ VRC5477_IRQ_INTB, /* onboard AMD PCNET */ + /* SLOT: 2, AD:13 */ 0xff, + /* SLOT: 3, AD:14 */ 0xff, + /* SLOT: 4, AD:15 */ 14, /* M5229 ide ISA irq */ + /* SLOT: 5, AD:16 */ VRC5477_IRQ_INTD, /* slot 3 */ + /* SLOT: 6, AD:17 */ VRC5477_IRQ_INTA, /* slot 4 */ + /* SLOT: 7, AD:18 */ VRC5477_IRQ_INTD, /* slot 5 */ + /* SLOT: 8, AD:19 */ 0, /* M5457 modem nop */ + /* SLOT: 9, AD:20 */ VRC5477_IRQ_INTA, /* slot 2 */ + /* SLOT: 10, AD:21 */ 0xff, + /* SLOT: 11, AD:22 */ 0xff, + /* SLOT: 12, AD:23 */ 0xff, + /* SLOT: 13, AD:24 */ 0xff, + /* SLOT: 14, AD:25 */ 0xff, + /* SLOT: 15, AD:26 */ 0xff, + /* SLOT: 16, AD:27 */ 0xff, + /* SLOT: 17, AD:28 */ 0, /* M7101 PMU nop */ + /* SLOT: 18, AD:29 */ VRC5477_IRQ_IOPCI_INTC, /* vrc5477 ac97 */ + /* SLOT: 19, AD:30 */ VRC5477_IRQ_IOPCI_INTB, /* vrc5477 usb peri */ + /* SLOT: 20, AD:31 */ VRC5477_IRQ_IOPCI_INTA, /* vrc5477 usb host */ +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int slot_num; + unsigned char *slot_irq_map; + unsigned char irq; + + /* + * We ignore the swizzled slot and pin values. The original + * pci_fixup_irq() codes largely base irq number on the dev slot + * numbers because except for one case they are unique even + * though there are multiple pci buses. + */ + + if (mips_machtype == MACH_NEC_ROCKHOPPERII) + slot_irq_map = rockhopperII_irq_map; + else + slot_irq_map = irq_map; + + slot_num = PCI_SLOT(dev->devfn); + irq = slot_irq_map[slot_num]; + + db_assert(slot_num < MAX_SLOT_NUM); + + db_assert(irq != 0xff); + + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); + + if (mips_machtype == MACH_NEC_ROCKHOPPERII) { + /* hack to distinquish overlapping slot 20s, one + * on bus 0 (ALI USB on the M1535 on the backplane), + * and one on bus 2 (NEC USB controller on the CPU board) + * Make the M1535 USB - ISA IRQ number 9. + */ + if (slot_num == 20 && dev->bus->number == 0) { + pci_write_config_byte(dev, + PCI_INTERRUPT_LINE, + 9); + irq = 9; + } + + } + + return irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +void ddb_pci_reset_bus(void) +{ + u32 temp; + + /* + * I am not sure about the "official" procedure, the following + * steps work as far as I know: + * We first set PCI cold reset bit (bit 31) in PCICTRL-H. + * Then we clear the PCI warm reset bit (bit 30) to 0 in PCICTRL-H. + * The same is true for both PCI channels. + */ + temp = ddb_in32(DDB_PCICTL0_H); + temp |= 0x80000000; + ddb_out32(DDB_PCICTL0_H, temp); + temp &= ~0xc0000000; + ddb_out32(DDB_PCICTL0_H, temp); + + temp = ddb_in32(DDB_PCICTL1_H); + temp |= 0x80000000; + ddb_out32(DDB_PCICTL1_H, temp); + temp &= ~0xc0000000; + ddb_out32(DDB_PCICTL1_H, temp); +} diff --git a/arch/mips/pci/pci-ev96100.c b/arch/mips/pci/pci-ev96100.c new file mode 100644 index 00000000000..f9457ea00de --- /dev/null +++ b/arch/mips/pci/pci-ev96100.c @@ -0,0 +1,63 @@ +/* + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. + * + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * 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/init.h> + +static struct resource pci_io_resource = { + .name = "io pci IO space", + .start = 0x10000000, + .end = 0x11ffffff, + .flags = IORESOURCE_IO +}; + +static struct resource pci_mem_resource = { + .name = "ext pci memory space", + .start = 0x12000000, + .end = 0x13ffffff, + .flags = IORESOURCE_MEM +}; + +extern struct pci_ops gt96100_pci_ops; + +struct pci_controller ev96100_controller = { + .pci_ops = >96100_pci_ops, + .io_resource = &pci_io_resource, + .mem_resource = &pci_mem_resource, +}; + +static void ev96100_pci_init(void) +{ + register_pci_controller(&ev96100_controller); +} + +arch_initcall(ev96100_pci_init); diff --git a/arch/mips/pci/pci-ip27.c b/arch/mips/pci/pci-ip27.c new file mode 100644 index 00000000000..068e0e508e1 --- /dev/null +++ b/arch/mips/pci/pci-ip27.c @@ -0,0 +1,489 @@ +/* + * 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. + * + * Copyright (C) 2003 Christoph Hellwig (hch@lst.de) + * Copyright (C) 1999, 2000, 04 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <asm/sn/arch.h> +#include <asm/pci/bridge.h> +#include <asm/paccess.h> +#include <asm/sn/intr.h> +#include <asm/sn/sn0/hub.h> + +extern unsigned int allocate_irqno(void); + +/* + * Max #PCI busses we can handle; ie, max #PCI bridges. + */ +#define MAX_PCI_BUSSES 40 + +/* + * Max #PCI devices (like scsi controllers) we handle on a bus. + */ +#define MAX_DEVICES_PER_PCIBUS 8 + +/* + * XXX: No kmalloc available when we do our crosstalk scan, + * we should try to move it later in the boot process. + */ +static struct bridge_controller bridges[MAX_PCI_BUSSES]; + +/* + * Translate from irq to software PCI bus number and PCI slot. + */ +struct bridge_controller *irq_to_bridge[MAX_PCI_BUSSES * MAX_DEVICES_PER_PCIBUS]; +int irq_to_slot[MAX_PCI_BUSSES * MAX_DEVICES_PER_PCIBUS]; + +/* + * The Bridge ASIC supports both type 0 and type 1 access. Type 1 is + * not really documented, so right now I can't write code which uses it. + * Therefore we use type 0 accesses for now even though they won't work + * correcly for PCI-to-PCI bridges. + * + * The function is complicated by the ultimate brokeness of the IOC3 chip + * which is used in SGI systems. The IOC3 can only handle 32-bit PCI + * accesses and does only decode parts of it's address space. + */ + +static int pci_conf0_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * value) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + bridge_t *bridge = bc->base; + int slot = PCI_SLOT(devfn); + int fn = PCI_FUNC(devfn); + volatile void *addr; + u32 cf, shift, mask; + int res; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID]; + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at it for real ... + */ + if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) + goto oh_my_gawd; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)]; + + if (size == 1) + res = get_dbe(*value, (u8 *) addr); + else if (size == 2) + res = get_dbe(*value, (u16 *) addr); + else + res = get_dbe(*value, (u32 *) addr); + + return res ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; + +oh_my_gawd: + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at the wrong register. + */ + if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) { + *value = 0; + return PCIBIOS_SUCCESSFUL; + } + + /* + * IOC3 is fucked fucked beyond believe ... Don't try to access + * anything but 32-bit words ... + */ + addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; + + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + shift = ((where & 3) << 3); + mask = (0xffffffffU >> ((4 - size) << 3)); + *value = (cf >> shift) & mask; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * value) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + bridge_t *bridge = bc->base; + int busno = bus->number; + int slot = PCI_SLOT(devfn); + int fn = PCI_FUNC(devfn); + volatile void *addr; + u32 cf, shift, mask; + int res; + + bridge->b_pci_cfg = (busno << 16) | (slot << 11); + addr = &bridge->b_type1_cfg.c[(fn << 8) | PCI_VENDOR_ID]; + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at it for real ... + */ + if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) + goto oh_my_gawd; + + bridge->b_pci_cfg = (busno << 16) | (slot << 11); + addr = &bridge->b_type1_cfg.c[(fn << 8) | (where ^ (4 - size))]; + + if (size == 1) + res = get_dbe(*value, (u8 *) addr); + else if (size == 2) + res = get_dbe(*value, (u16 *) addr); + else + res = get_dbe(*value, (u32 *) addr); + + return res ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; + +oh_my_gawd: + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at the wrong register. + */ + if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) { + *value = 0; + return PCIBIOS_SUCCESSFUL; + } + + /* + * IOC3 is fucked fucked beyond believe ... Don't try to access + * anything but 32-bit words ... + */ + bridge->b_pci_cfg = (busno << 16) | (slot << 11); + addr = &bridge->b_type1_cfg.c[(fn << 8) | where]; + + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + shift = ((where & 3) << 3); + mask = (0xffffffffU >> ((4 - size) << 3)); + *value = (cf >> shift) & mask; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * value) +{ + if (bus->number > 0) + return pci_conf1_read_config(bus, devfn, where, size, value); + + return pci_conf0_read_config(bus, devfn, where, size, value); +} + +static int pci_conf0_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + bridge_t *bridge = bc->base; + int slot = PCI_SLOT(devfn); + int fn = PCI_FUNC(devfn); + volatile void *addr; + u32 cf, shift, mask, smask; + int res; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[PCI_VENDOR_ID]; + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at it for real ... + */ + if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) + goto oh_my_gawd; + + addr = &bridge->b_type0_cfg_dev[slot].f[fn].c[where ^ (4 - size)]; + + if (size == 1) { + res = put_dbe(value, (u8 *) addr); + } else if (size == 2) { + res = put_dbe(value, (u16 *) addr); + } else { + res = put_dbe(value, (u32 *) addr); + } + + if (res) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; + +oh_my_gawd: + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to touch the wrong register. + */ + if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) + return PCIBIOS_SUCCESSFUL; + + /* + * IOC3 is fucked fucked beyond believe ... Don't try to access + * anything but 32-bit words ... + */ + addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; + + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + shift = ((where & 3) << 3); + mask = (0xffffffffU >> ((4 - size) << 3)); + smask = mask << shift; + + cf = (cf & ~smask) | ((value & mask) << shift); + if (put_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_conf1_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(bus); + bridge_t *bridge = bc->base; + int slot = PCI_SLOT(devfn); + int fn = PCI_FUNC(devfn); + int busno = bus->number; + volatile void *addr; + u32 cf, shift, mask, smask; + int res; + + bridge->b_pci_cfg = (busno << 16) | (slot << 11); + addr = &bridge->b_type1_cfg.c[(fn << 8) | PCI_VENDOR_ID]; + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to look at it for real ... + */ + if (cf == (PCI_VENDOR_ID_SGI | (PCI_DEVICE_ID_SGI_IOC3 << 16))) + goto oh_my_gawd; + + addr = &bridge->b_type1_cfg.c[(fn << 8) | (where ^ (4 - size))]; + + if (size == 1) { + res = put_dbe(value, (u8 *) addr); + } else if (size == 2) { + res = put_dbe(value, (u16 *) addr); + } else { + res = put_dbe(value, (u32 *) addr); + } + + if (res) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; + +oh_my_gawd: + + /* + * IOC3 is fucked fucked beyond believe ... Don't even give the + * generic PCI code a chance to touch the wrong register. + */ + if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) + return PCIBIOS_SUCCESSFUL; + + /* + * IOC3 is fucked fucked beyond believe ... Don't try to access + * anything but 32-bit words ... + */ + addr = &bridge->b_type0_cfg_dev[slot].f[fn].l[where >> 2]; + + if (get_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + shift = ((where & 3) << 3); + mask = (0xffffffffU >> ((4 - size) << 3)); + smask = mask << shift; + + cf = (cf & ~smask) | ((value & mask) << shift); + if (put_dbe(cf, (u32 *) addr)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; +} + +static int pci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + if (bus->number > 0) + return pci_conf1_write_config(bus, devfn, where, size, value); + + return pci_conf0_write_config(bus, devfn, where, size, value); +} + +static struct pci_ops bridge_pci_ops = { + .read = pci_read_config, + .write = pci_write_config, +}; + +int __init bridge_probe(nasid_t nasid, int widget_id, int masterwid) +{ + unsigned long offset = NODE_OFFSET(nasid); + struct bridge_controller *bc; + static int num_bridges = 0; + bridge_t *bridge; + int slot; + + printk("a bridge\n"); + + /* XXX: kludge alert.. */ + if (!num_bridges) + ioport_resource.end = ~0UL; + + bc = &bridges[num_bridges]; + + bc->pc.pci_ops = &bridge_pci_ops; + bc->pc.mem_resource = &bc->mem; + bc->pc.io_resource = &bc->io; + + bc->pc.index = num_bridges; + + bc->mem.name = "Bridge PCI MEM"; + bc->pc.mem_offset = offset; + bc->mem.start = 0; + bc->mem.end = ~0UL; + bc->mem.flags = IORESOURCE_MEM; + + bc->io.name = "Bridge IO MEM"; + bc->pc.io_offset = offset; + bc->io.start = 0UL; + bc->io.end = ~0UL; + bc->io.flags = IORESOURCE_IO; + + bc->irq_cpu = smp_processor_id(); + bc->widget_id = widget_id; + bc->nasid = nasid; + + bc->baddr = (u64)masterwid << 60; + bc->baddr |= (1UL << 56); /* Barrier set */ + + /* + * point to this bridge + */ + bridge = (bridge_t *) RAW_NODE_SWIN_BASE(nasid, widget_id); + + /* + * Clear all pending interrupts. + */ + bridge->b_int_rst_stat = BRIDGE_IRR_ALL_CLR; + + /* + * Until otherwise set up, assume all interrupts are from slot 0 + */ + bridge->b_int_device = 0x0; + + /* + * swap pio's to pci mem and io space (big windows) + */ + bridge->b_wid_control |= BRIDGE_CTRL_IO_SWAP | + BRIDGE_CTRL_MEM_SWAP; + + /* + * Hmm... IRIX sets additional bits in the address which + * are documented as reserved in the bridge docs. + */ + bridge->b_wid_int_upper = 0x8000 | (masterwid << 16); + bridge->b_wid_int_lower = 0x01800090; /* PI_INT_PEND_MOD off*/ + bridge->b_dir_map = (masterwid << 20); /* DMA */ + bridge->b_int_enable = 0; + + for (slot = 0; slot < 8; slot ++) { + bridge->b_device[slot].reg |= BRIDGE_DEV_SWAP_DIR; + bc->pci_int[slot] = -1; + } + bridge->b_wid_tflush; /* wait until Bridge PIO complete */ + + bc->base = bridge; + + register_pci_controller(&bc->pc); + + num_bridges++; + + return 0; +} + +/* + * All observed requests have pin == 1. We could have a global here, that + * gets incremented and returned every time - unfortunately, pci_map_irq + * may be called on the same device over and over, and need to return the + * same value. On O2000, pin can be 0 or 1, and PCI slots can be [0..7]. + * + * A given PCI device, in general, should be able to intr any of the cpus + * on any one of the hubs connected to its xbow. + */ +int __devinit pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); + int irq = bc->pci_int[slot]; + + if (irq == -1) { + irq = bc->pci_int[slot] = request_bridge_irq(bc); + if (irq < 0) + panic("Can't allocate interrupt for PCI device %s\n", + pci_name(dev)); + } + + irq_to_bridge[irq] = bc; + irq_to_slot[irq] = slot; + + return irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +/* + * Device might live on a subordinate PCI bus. XXX Walk up the chain of buses + * to find the slot number in sense of the bridge device register. + * XXX This also means multiple devices might rely on conflicting bridge + * settings. + */ + +static inline void pci_disable_swapping(struct pci_dev *dev) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); + bridge_t *bridge = bc->base; + int slot = PCI_SLOT(dev->devfn); + + /* Turn off byte swapping */ + bridge->b_device[slot].reg &= ~BRIDGE_DEV_SWAP_DIR; + bridge->b_widget.w_tflush; /* Flush */ +} + +static inline void pci_enable_swapping(struct pci_dev *dev) +{ + struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus); + bridge_t *bridge = bc->base; + int slot = PCI_SLOT(dev->devfn); + + /* Turn on byte swapping */ + bridge->b_device[slot].reg |= BRIDGE_DEV_SWAP_DIR; + bridge->b_widget.w_tflush; /* Flush */ +} + +static void __init pci_fixup_ioc3(struct pci_dev *d) +{ + pci_disable_swapping(d); +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + pci_fixup_ioc3); diff --git a/arch/mips/pci/pci-ip32.c b/arch/mips/pci/pci-ip32.c new file mode 100644 index 00000000000..1faeb034f06 --- /dev/null +++ b/arch/mips/pci/pci-ip32.c @@ -0,0 +1,145 @@ +/* + * 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. + * + * Copyright (C) 2000, 2001 Keith M Wesolowski + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <asm/ip32/mace.h> +#include <asm/ip32/ip32_ints.h> + +#undef DEBUG_MACE_PCI + +/* + * Handle errors from the bridge. This includes master and target aborts, + * various command and address errors, and the interrupt test. This gets + * registered on the bridge error irq. It's conceivable that some of these + * conditions warrant a panic. Anybody care to say which ones? + */ +static irqreturn_t macepci_error(int irq, void *dev, struct pt_regs *regs) +{ + char s; + unsigned int flags = mace->pci.error; + unsigned int addr = mace->pci.error_addr; + + if (flags & MACEPCI_ERROR_MEMORY_ADDR) + s = 'M'; + else if (flags & MACEPCI_ERROR_CONFIG_ADDR) + s = 'C'; + else + s = 'X'; + + if (flags & MACEPCI_ERROR_MASTER_ABORT) { + printk("MACEPCI: Master abort at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_MASTER_ABORT; + } + if (flags & MACEPCI_ERROR_TARGET_ABORT) { + printk("MACEPCI: Target abort at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_TARGET_ABORT; + } + if (flags & MACEPCI_ERROR_DATA_PARITY_ERR) { + printk("MACEPCI: Data parity error at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_DATA_PARITY_ERR; + } + if (flags & MACEPCI_ERROR_RETRY_ERR) { + printk("MACEPCI: Retry error at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_RETRY_ERR; + } + if (flags & MACEPCI_ERROR_ILLEGAL_CMD) { + printk("MACEPCI: Illegal command at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_ILLEGAL_CMD; + } + if (flags & MACEPCI_ERROR_SYSTEM_ERR) { + printk("MACEPCI: System error at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_SYSTEM_ERR; + } + if (flags & MACEPCI_ERROR_PARITY_ERR) { + printk("MACEPCI: Parity error at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_PARITY_ERR; + } + if (flags & MACEPCI_ERROR_OVERRUN) { + printk("MACEPCI: Overrun error at 0x%08x (%c)\n", addr, s); + flags &= ~MACEPCI_ERROR_OVERRUN; + } + if (flags & MACEPCI_ERROR_SIG_TABORT) { + printk("MACEPCI: Signaled target abort (clearing)\n"); + flags &= ~MACEPCI_ERROR_SIG_TABORT; + } + if (flags & MACEPCI_ERROR_INTERRUPT_TEST) { + printk("MACEPCI: Interrupt test triggered (clearing)\n"); + flags &= ~MACEPCI_ERROR_INTERRUPT_TEST; + } + + mace->pci.error = flags; + + return IRQ_HANDLED; +} + + +extern struct pci_ops mace_pci_ops; +#ifdef CONFIG_MIPS64 +static struct resource mace_pci_mem_resource = { + .name = "SGI O2 PCI MEM", + .start = MACEPCI_HI_MEMORY, + .end = 0x2FFFFFFFFUL, + .flags = IORESOURCE_MEM, +}; +static struct resource mace_pci_io_resource = { + .name = "SGI O2 PCI IO", + .start = 0x00000000UL, + .end = 0xffffffffUL, + .flags = IORESOURCE_IO, +}; +#define MACE_PCI_MEM_OFFSET 0x200000000 +#else +static struct resource mace_pci_mem_resource = { + .name = "SGI O2 PCI MEM", + .start = MACEPCI_LOW_MEMORY, + .end = MACEPCI_LOW_MEMORY + 0x2000000 - 1, + .flags = IORESOURCE_MEM, +}; +static struct resource mace_pci_io_resource = { + .name = "SGI O2 PCI IO", + .start = 0x00000000, + .end = 0xFFFFFFFF, + .flags = IORESOURCE_IO, +}; +#define MACE_PCI_MEM_OFFSET (MACEPCI_LOW_MEMORY - 0x80000000) +#endif +static struct pci_controller mace_pci_controller = { + .pci_ops = &mace_pci_ops, + .mem_resource = &mace_pci_mem_resource, + .io_resource = &mace_pci_io_resource, + .iommu = 0, + .mem_offset = MACE_PCI_MEM_OFFSET, + .io_offset = 0, +}; + +static int __init mace_init(void) +{ + PCIBIOS_MIN_IO = 0x1000; + + /* Clear any outstanding errors and enable interrupts */ + mace->pci.error_addr = 0; + mace->pci.error = 0; + mace->pci.control = 0xff008500; + + printk("MACE PCI rev %d\n", mace->pci.rev); + + BUG_ON(request_irq(MACE_PCI_BRIDGE_IRQ, macepci_error, 0, + "MACE PCI error", NULL)); + + ioport_resource.end = mace_pci_io_resource.end; + register_pci_controller(&mace_pci_controller); + + return 0; +} + +arch_initcall(mace_init); diff --git a/arch/mips/pci/pci-jmr3927.c b/arch/mips/pci/pci-jmr3927.c new file mode 100644 index 00000000000..95a028769e5 --- /dev/null +++ b/arch/mips/pci/pci-jmr3927.c @@ -0,0 +1,58 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ahennessy@mvista.com + * + * Copyright (C) 2000-2001 Toshiba Corporation + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * 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/init.h> + +#include <asm/jmr3927/jmr3927.h> +#include <asm/debug.h> + +struct resource pci_io_resource = { + "IO MEM", + 0x1000, /* reserve regacy I/O space */ + 0x1000 + JMR3927_PCIIO_SIZE - 1, + IORESOURCE_IO +}; + +struct resource pci_mem_resource = { + "PCI MEM", + JMR3927_PCIMEM, + JMR3927_PCIMEM + JMR3927_PCIMEM_SIZE - 1, + IORESOURCE_MEM +}; + +extern struct pci_ops jmr3927_pci_ops; + +struct pci_controller jmr3927_controller = { + .pci_ops = &jmr3927_pci_ops, + .io_resource = &pci_io_resource, + .mem_resource = &pci_mem_resource, + .mem_offset = JMR3927_PCIMEM; +}; diff --git a/arch/mips/pci/pci-lasat.c b/arch/mips/pci/pci-lasat.c new file mode 100644 index 00000000000..ae3cc4b254b --- /dev/null +++ b/arch/mips/pci/pci-lasat.c @@ -0,0 +1,95 @@ +/* + * 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. + * + * Copyright (C) 2000, 2001, 04 Keith M Wesolowski + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <asm/bootinfo.h> + +extern struct pci_ops nile4_pci_ops; +extern struct pci_ops gt64120_pci_ops; +static struct resource lasat_pci_mem_resource = { + .name = "LASAT PCI MEM", + .start = 0x18000000, + .end = 0x19FFFFFF, + .flags = IORESOURCE_MEM, +}; + +static struct resource lasat_pci_io_resource = { + .name = "LASAT PCI IO", + .start = 0x1a000000, + .end = 0x1bFFFFFF, + .flags = IORESOURCE_IO, +}; + +static struct pci_controller lasat_pci_controller = { + .mem_resource = &lasat_pci_mem_resource, + .io_resource = &lasat_pci_io_resource, +}; + +static int __init lasat_pci_setup(void) +{ + printk("PCI: starting\n"); + + switch (mips_machtype) { + case MACH_LASAT_100: + lasat_pci_controller.pci_ops = >64120_pci_ops; + break; + case MACH_LASAT_200: + lasat_pci_controller.pci_ops = &nile4_pci_ops; + break; + default: + panic("pcibios_init: mips_machtype incorrect"); + } + + register_pci_controller(&lasat_pci_controller); + return 0; +} +early_initcall(lasat_pci_setup); + +#define LASATINT_ETH1 0 +#define LASATINT_ETH0 1 +#define LASATINT_HDC 2 +#define LASATINT_COMP 3 +#define LASATINT_HDLC 4 +#define LASATINT_PCIA 5 +#define LASATINT_PCIB 6 +#define LASATINT_PCIC 7 +#define LASATINT_PCID 8 + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + switch (slot) { + case 1: + return LASATINT_PCIA; /* Expansion Module 0 */ + case 2: + return LASATINT_PCIB; /* Expansion Module 1 */ + case 3: + return LASATINT_PCIC; /* Expansion Module 2 */ + case 4: + return LASATINT_ETH1; /* Ethernet 1 (LAN 2) */ + case 5: + return LASATINT_ETH0; /* Ethernet 0 (LAN 1) */ + case 6: + return LASATINT_HDC; /* IDE controller */ + default: + return 0xff; /* Illegal */ + } + + return -1; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} diff --git a/arch/mips/pci/pci-ocelot-c.c b/arch/mips/pci/pci-ocelot-c.c new file mode 100644 index 00000000000..1d84d36e034 --- /dev/null +++ b/arch/mips/pci/pci-ocelot-c.c @@ -0,0 +1,143 @@ +/* + * 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. + * + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + */ + +#include <linux/types.h> +#include <linux/pci.h> +#include <asm/mv64340.h> + +#include <linux/init.h> + +/* + * We assume the address ranges have already been setup appropriately by + * the firmware. PMON in case of the Ocelot C does that. + */ +static struct resource mv_pci_io_mem0_resource = { + .name = "MV64340 PCI0 IO MEM", + .flags = IORESOURCE_IO +}; + +static struct resource mv_pci_mem0_resource = { + .name = "MV64340 PCI0 MEM", + .flags = IORESOURCE_MEM +}; + +static struct mv_pci_controller mv_bus0_controller = { + .pcic = { + .pci_ops = &mv_pci_ops, + .mem_resource = &mv_pci_mem0_resource, + .io_resource = &mv_pci_io_mem0_resource, + }, + .config_addr = MV64340_PCI_0_CONFIG_ADDR, + .config_vreg = MV64340_PCI_0_CONFIG_DATA_VIRTUAL_REG, +}; + +static uint32_t mv_io_base, mv_io_size; + +static void mv64340_pci0_init(void) +{ + uint32_t mem0_base, mem0_size; + uint32_t io_base, io_size; + + io_base = MV_READ(MV64340_PCI_0_IO_BASE_ADDR) << 16; + io_size = (MV_READ(MV64340_PCI_0_IO_SIZE) + 1) << 16; + mem0_base = MV_READ(MV64340_PCI_0_MEMORY0_BASE_ADDR) << 16; + mem0_size = (MV_READ(MV64340_PCI_0_MEMORY0_SIZE) + 1) << 16; + + mv_pci_io_mem0_resource.start = 0; + mv_pci_io_mem0_resource.end = io_size - 1; + mv_pci_mem0_resource.start = mem0_base; + mv_pci_mem0_resource.end = mem0_base + mem0_size - 1; + mv_bus0_controller.pcic.mem_offset = mem0_base; + mv_bus0_controller.pcic.io_offset = 0; + + ioport_resource.end = io_size - 1; + + register_pci_controller(&mv_bus0_controller.pcic); + + mv_io_base = io_base; + mv_io_size = io_size; +} + +static struct resource mv_pci_io_mem1_resource = { + .name = "MV64340 PCI1 IO MEM", + .flags = IORESOURCE_IO +}; + +static struct resource mv_pci_mem1_resource = { + .name = "MV64340 PCI1 MEM", + .flags = IORESOURCE_MEM +}; + +static struct mv_pci_controller mv_bus1_controller = { + .pcic = { + .pci_ops = &mv_pci_ops, + .mem_resource = &mv_pci_mem1_resource, + .io_resource = &mv_pci_io_mem1_resource, + }, + .config_addr = MV64340_PCI_1_CONFIG_ADDR, + .config_vreg = MV64340_PCI_1_CONFIG_DATA_VIRTUAL_REG, +}; + +static __init void mv64340_pci1_init(void) +{ + uint32_t mem0_base, mem0_size; + uint32_t io_base, io_size; + + io_base = MV_READ(MV64340_PCI_1_IO_BASE_ADDR) << 16; + io_size = (MV_READ(MV64340_PCI_1_IO_SIZE) + 1) << 16; + mem0_base = MV_READ(MV64340_PCI_1_MEMORY0_BASE_ADDR) << 16; + mem0_size = (MV_READ(MV64340_PCI_1_MEMORY0_SIZE) + 1) << 16; + + /* + * Here we assume the I/O window of second bus to be contiguous with + * the first. A gap is no problem but would waste address space for + * remapping the port space. + */ + mv_pci_io_mem1_resource.start = mv_io_size; + mv_pci_io_mem1_resource.end = mv_io_size + io_size - 1; + mv_pci_mem1_resource.start = mem0_base; + mv_pci_mem1_resource.end = mem0_base + mem0_size - 1; + mv_bus1_controller.pcic.mem_offset = mem0_base; + mv_bus1_controller.pcic.io_offset = 0; + + ioport_resource.end = io_base + io_size -mv_io_base - 1; + + register_pci_controller(&mv_bus1_controller.pcic); + + mv_io_size = io_base + io_size - mv_io_base; +} + +static __init int __init ocelot_c_pci_init(void) +{ + unsigned long io_v_base; + uint32_t enable; + + enable = ~MV_READ(MV64340_BASE_ADDR_ENABLE); + + /* + * We require at least one enabled I/O or PCI memory window or we + * will ignore this PCI bus. We ignore PCI windows 1, 2 and 3. + */ + if (enable & (0x01 << 9) || enable & (0x01 << 10)) + mv64340_pci0_init(); + + if (enable & (0x01 << 14) || enable & (0x01 << 15)) + mv64340_pci1_init(); + + if (mv_io_size) { + io_v_base = (unsigned long) ioremap(mv_io_base, mv_io_size); + if (!io_v_base) + panic("Could not ioremap I/O port range"); + + set_io_port_base(io_v_base); + } + + return 0; +} + +arch_initcall(ocelot_c_pci_init); diff --git a/arch/mips/pci/pci-ocelot-g.c b/arch/mips/pci/pci-ocelot-g.c new file mode 100644 index 00000000000..1e3430154fa --- /dev/null +++ b/arch/mips/pci/pci-ocelot-g.c @@ -0,0 +1,97 @@ +/* + * 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. + * + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * This doesn't really fly - but I don't have a GT64240 system for testing. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <asm/gt64240.h> + +/* + * We assume these address ranges have been programmed into the GT-64240 by + * the firmware. PMON in case of the Ocelot G does that. Note the size of + * the I/O range is completly stupid; I/O mappings are limited to at most + * 256 bytes by the PCI spec and deprecated; and just to make things worse + * apparently many devices don't decode more than 64k of I/O space. + */ + +#define gt_io_size 0x20000000UL +#define gt_io_base 0xe0000000UL + +static struct resource gt_pci_mem0_resource = { + .name = "MV64240 PCI0 MEM", + .start = 0xc0000000UL, + .end = 0xcfffffffUL, + .flags = IORESOURCE_MEM +}; + +static struct resource gt_pci_io_mem0_resource = { + .name = "MV64240 PCI0 IO MEM", + .start = 0xe0000000UL, + .end = 0xefffffffUL, + .flags = IORESOURCE_IO +}; + +static struct mv_pci_controller gt_bus0_controller = { + .pcic = { + .pci_ops = &mv_pci_ops, + .mem_resource = >_pci_mem0_resource, + .mem_offset = 0xc0000000UL, + .io_resource = >_pci_io_mem0_resource, + .io_offset = 0x00000000UL + }, + .config_addr = PCI_0CONFIGURATION_ADDRESS, + .config_vreg = PCI_0CONFIGURATION_DATA_VIRTUAL_REGISTER, +}; + +static struct resource gt_pci_mem1_resource = { + .name = "MV64240 PCI1 MEM", + .start = 0xd0000000UL, + .end = 0xdfffffffUL, + .flags = IORESOURCE_MEM +}; + +static struct resource gt_pci_io_mem1_resource = { + .name = "MV64240 PCI1 IO MEM", + .start = 0xf0000000UL, + .end = 0xffffffffUL, + .flags = IORESOURCE_IO +}; + +static struct mv_pci_controller gt_bus1_controller = { + .pcic = { + .pci_ops = &mv_pci_ops, + .mem_resource = >_pci_mem1_resource, + .mem_offset = 0xd0000000UL, + .io_resource = >_pci_io_mem1_resource, + .io_offset = 0x10000000UL + }, + .config_addr = PCI_1CONFIGURATION_ADDRESS, + .config_vreg = PCI_1CONFIGURATION_DATA_VIRTUAL_REGISTER, +}; + +static __init int __init ocelot_g_pci_init(void) +{ + unsigned long io_v_base; + + if (gt_io_size) { + io_v_base = (unsigned long) ioremap(gt_io_base, gt_io_size); + if (!io_v_base) + panic("Could not ioremap I/O port range"); + + set_io_port_base(io_v_base); + } + + register_pci_controller(>_bus0_controller.pcic); + register_pci_controller(>_bus1_controller.pcic); + + return 0; +} + +arch_initcall(ocelot_g_pci_init); diff --git a/arch/mips/pci/pci-ocelot.c b/arch/mips/pci/pci-ocelot.c new file mode 100644 index 00000000000..3da8a4ee6ba --- /dev/null +++ b/arch/mips/pci/pci-ocelot.c @@ -0,0 +1,107 @@ +/* + * 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 + * + * Copyright 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 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/init.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/cache.h> +#include <asm/pci.h> +#include <asm/io.h> +#include <asm/gt64120.h> + +static inline unsigned int pci0ReadConfigReg(unsigned int offset) +{ + 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); + GT_READ(GT_PCI0_CFGDATA_OFS, &data); + + return data; +} + +static inline void pci0WriteConfigReg(unsigned int offset, 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); + GT_WRITE(GT_PCI0_CFGDATA_OFS, data); +} + +static struct resource ocelot_mem_resource = { + iomem_resource.start = GT_PCI_MEM_BASE; + iomem_resource.end = GT_PCI_MEM_BASE + GT_PCI_MEM_BASE - 1; +}; + +static struct resource ocelot_io_resource = { + ioport_resource.start = GT_PCI_IO_BASE; + ioport_resource.end = GT_PCI_IO_BASE + GT_PCI_IO_SIZE - 1; +}; + +static struct pci_controller ocelot_pci_controller = { + .pci_ops = gt64120_pci_ops; + .mem_resource = &ocelot_mem_resource; + .io_resource = &ocelot_io_resource; +}; + +static int __init ocelot_pcibios_init(void) +{ + u32 tmp; + + GT_READ(GT_PCI0_CMD_OFS, &tmp); + GT_READ(GT_PCI0_BARE_OFS, &tmp); + + /* + * You have to enable bus mastering to configure any other + * card on the bus. + */ + tmp = pci0ReadConfigReg(PCI_COMMAND); + tmp |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR; + pci0WriteConfigReg(PCI_COMMAND, tmp); + + register_pci_controller(&ocelot_pci_controller); +} + +arch_initcall(ocelot_pcibios_init); diff --git a/arch/mips/pci/pci-sb1250.c b/arch/mips/pci/pci-sb1250.c new file mode 100644 index 00000000000..7cca3bde59b --- /dev/null +++ b/arch/mips/pci/pci-sb1250.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2001,2002,2003 Broadcom Corporation + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * BCM1250-specific PCI support + * + * This module provides the glue between Linux's PCI subsystem + * and the hardware. We basically provide glue for accessing + * configuration space, and set up the translation for I/O + * space accesses. + * + * To access configuration space, we use ioremap. In the 32-bit + * kernel, this consumes either 4 or 8 page table pages, and 16MB of + * kernel mapped memory. Hopefully neither of these should be a huge + * problem. + */ +#include <linux/config.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/tty.h> + +#include <asm/io.h> + +#include <asm/sibyte/sb1250_defs.h> +#include <asm/sibyte/sb1250_regs.h> +#include <asm/sibyte/sb1250_scd.h> +#include <asm/sibyte/board.h> + +/* + * Macros for calculating offsets into config space given a device + * structure or dev/fun/reg + */ +#define CFGOFFSET(bus,devfn,where) (((bus)<<16) + ((devfn)<<8) + (where)) +#define CFGADDR(bus,devfn,where) CFGOFFSET((bus)->number,(devfn),where) + +static void *cfg_space; + +#define PCI_BUS_ENABLED 1 +#define LDT_BUS_ENABLED 2 +#define PCI_DEVICE_MODE 4 + +static int sb1250_bus_status = 0; + +#define PCI_BRIDGE_DEVICE 0 +#define LDT_BRIDGE_DEVICE 1 + +#ifdef CONFIG_SIBYTE_HAS_LDT +/* + * HT's level-sensitive interrupts require EOI, which is generated + * through a 4MB memory-mapped region + */ +unsigned long ldt_eoi_space; +#endif + +/* + * Read/write 32-bit values in config space. + */ +static inline u32 READCFG32(u32 addr) +{ + return *(u32 *) (cfg_space + (addr & ~3)); +} + +static inline void WRITECFG32(u32 addr, u32 data) +{ + *(u32 *) (cfg_space + (addr & ~3)) = data; +} + +int pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return dev->irq; +} + +/* Do platform specific device initialization at pci_enable_device() time */ +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +/* + * Some checks before doing config cycles: + * In PCI Device Mode, hide everything on bus 0 except the LDT host + * bridge. Otherwise, access is controlled by bridge MasterEn bits. + */ +static int sb1250_pci_can_access(struct pci_bus *bus, int devfn) +{ + u32 devno; + + if (!(sb1250_bus_status & (PCI_BUS_ENABLED | PCI_DEVICE_MODE))) + return 0; + + if (bus->number == 0) { + devno = PCI_SLOT(devfn); + if (devno == LDT_BRIDGE_DEVICE) + return (sb1250_bus_status & LDT_BUS_ENABLED) != 0; + else if (sb1250_bus_status & PCI_DEVICE_MODE) + return 0; + else + return 1; + } else + return 1; +} + +/* + * Read/write access functions for various sizes of values + * in config space. Return all 1's for disallowed accesses + * for a kludgy but adequate simulation of master aborts. + */ + +static int sb1250_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + u32 data = 0; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (sb1250_pci_can_access(bus, devfn)) + data = READCFG32(CFGADDR(bus, devfn, where)); + else + data = 0xFFFFFFFF; + + if (size == 1) + *val = (data >> ((where & 3) << 3)) & 0xff; + else if (size == 2) + *val = (data >> ((where & 3) << 3)) & 0xffff; + else + *val = data; + + return PCIBIOS_SUCCESSFUL; +} + +static int sb1250_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + u32 cfgaddr = CFGADDR(bus, devfn, where); + u32 data = 0; + + if ((size == 2) && (where & 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + else if ((size == 4) && (where & 3)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + if (!sb1250_pci_can_access(bus, devfn)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + data = READCFG32(cfgaddr); + + if (size == 1) + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else if (size == 2) + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + else + data = val; + + WRITECFG32(cfgaddr, data); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops sb1250_pci_ops = { + .read = sb1250_pcibios_read, + .write = sb1250_pcibios_write, +}; + +static struct resource sb1250_mem_resource = { + .name = "SB1250 PCI MEM", + .start = 0x40000000UL, + .end = 0x5fffffffUL, + .flags = IORESOURCE_MEM, +}; + +static struct resource sb1250_io_resource = { + .name = "SB1250 PCI I/O", + .start = 0x00000000UL, + .end = 0x01ffffffUL, + .flags = IORESOURCE_IO, +}; + +struct pci_controller sb1250_controller = { + .pci_ops = &sb1250_pci_ops, + .mem_resource = &sb1250_mem_resource, + .io_resource = &sb1250_io_resource, +}; + +static int __init sb1250_pcibios_init(void) +{ + uint32_t cmdreg; + uint64_t reg; + extern int pci_probe_only; + + /* CFE will assign PCI resources */ + pci_probe_only = 1; + + /* Avoid ISA compat ranges. */ + PCIBIOS_MIN_IO = 0x00008000UL; + PCIBIOS_MIN_MEM = 0x01000000UL; + + /* Set I/O resource limits. */ + ioport_resource.end = 0x01ffffffUL; /* 32MB accessible by sb1250 */ + iomem_resource.end = 0xffffffffUL; /* no HT support yet */ + + cfg_space = + ioremap(A_PHYS_LDTPCI_CFG_MATCH_BITS, 16 * 1024 * 1024); + + /* + * See if the PCI bus has been configured by the firmware. + */ + reg = *((volatile uint64_t *) IOADDR(A_SCD_SYSTEM_CFG)); + if (!(reg & M_SYS_PCI_HOST)) { + sb1250_bus_status |= PCI_DEVICE_MODE; + } else { + cmdreg = + READCFG32(CFGOFFSET + (0, PCI_DEVFN(PCI_BRIDGE_DEVICE, 0), + PCI_COMMAND)); + if (!(cmdreg & PCI_COMMAND_MASTER)) { + printk + ("PCI: Skipping PCI probe. Bus is not initialized.\n"); + iounmap(cfg_space); + return 0; + } + sb1250_bus_status |= PCI_BUS_ENABLED; + } + + /* + * Establish mappings in KSEG2 (kernel virtual) to PCI I/O + * space. Use "match bytes" policy to make everything look + * little-endian. So, you need to also set + * CONFIG_SWAP_IO_SPACE, but this is the combination that + * works correctly with most of Linux's drivers. + * XXX ehs: Should this happen in PCI Device mode? + */ + + set_io_port_base((unsigned long) + ioremap(A_PHYS_LDTPCI_IO_MATCH_BYTES, 65536)); + isa_slot_offset = (unsigned long) + ioremap(A_PHYS_LDTPCI_IO_MATCH_BYTES_32, 1024 * 1024); + +#ifdef CONFIG_SIBYTE_HAS_LDT + /* + * Also check the LDT bridge's enable, just in case we didn't + * initialize that one. + */ + + cmdreg = READCFG32(CFGOFFSET(0, PCI_DEVFN(LDT_BRIDGE_DEVICE, 0), + PCI_COMMAND)); + if (cmdreg & PCI_COMMAND_MASTER) { + sb1250_bus_status |= LDT_BUS_ENABLED; + + /* + * Need bits 23:16 to convey vector number. Note that + * this consumes 4MB of kernel-mapped memory + * (Kseg2/Kseg3) for 32-bit kernel. + */ + ldt_eoi_space = (unsigned long) + ioremap(A_PHYS_LDT_SPECIAL_MATCH_BYTES, + 4 * 1024 * 1024); + } +#endif + + register_pci_controller(&sb1250_controller); + +#ifdef CONFIG_VGA_CONSOLE + take_over_console(&vga_con, 0, MAX_NR_CONSOLES - 1, 1); +#endif + return 0; +} +arch_initcall(sb1250_pcibios_init); diff --git a/arch/mips/pci/pci-vr41xx.c b/arch/mips/pci/pci-vr41xx.c new file mode 100644 index 00000000000..f3ccbf7fada --- /dev/null +++ b/arch/mips/pci/pci-vr41xx.c @@ -0,0 +1,291 @@ +/* + * pci-vr41xx.c, PCI Control Unit routines for the NEC VR4100 series. + * + * Copyright (C) 2001-2003 MontaVista Software Inc. + * Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com> + * Copyright (C) 2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +/* + * Changes: + * MontaVista Software Inc. <yyuasa@mvista.com> or <source@mvista.com> + * - New creation, NEC VR4122 and VR4131 are supported. + */ +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include <asm/cpu.h> +#include <asm/io.h> +#include <asm/vr41xx/vr41xx.h> + +#include "pci-vr41xx.h" + +extern struct pci_ops vr41xx_pci_ops; + +static struct pci_master_address_conversion pci_master_memory1 = { + .bus_base_address = PCI_MASTER_MEM1_BUS_BASE_ADDRESS, + .address_mask = PCI_MASTER_MEM1_ADDRESS_MASK, + .pci_base_address = PCI_MASTER_MEM1_PCI_BASE_ADDRESS, +}; + +static struct pci_target_address_conversion pci_target_memory1 = { + .address_mask = PCI_TARGET_MEM1_ADDRESS_MASK, + .bus_base_address = PCI_TARGET_MEM1_BUS_BASE_ADDRESS, +}; + +static struct pci_master_address_conversion pci_master_io = { + .bus_base_address = PCI_MASTER_IO_BUS_BASE_ADDRESS, + .address_mask = PCI_MASTER_IO_ADDRESS_MASK, + .pci_base_address = PCI_MASTER_IO_PCI_BASE_ADDRESS, +}; + +static struct pci_mailbox_address pci_mailbox = { + .base_address = PCI_MAILBOX_BASE_ADDRESS, +}; + +static struct pci_target_address_window pci_target_window1 = { + .base_address = PCI_TARGET_WINDOW1_BASE_ADDRESS, +}; + +static struct resource pci_mem_resource = { + .name = "PCI Memory resources", + .start = PCI_MEM_RESOURCE_START, + .end = PCI_MEM_RESOURCE_END, + .flags = IORESOURCE_MEM, +}; + +static struct resource pci_io_resource = { + .name = "PCI I/O resources", + .start = PCI_IO_RESOURCE_START, + .end = PCI_IO_RESOURCE_END, + .flags = IORESOURCE_IO, +}; + +static struct pci_controller_unit_setup vr41xx_pci_controller_unit_setup = { + .master_memory1 = &pci_master_memory1, + .target_memory1 = &pci_target_memory1, + .master_io = &pci_master_io, + .exclusive_access = CANNOT_LOCK_FROM_DEVICE, + .wait_time_limit_from_irdy_to_trdy = 0, + .mailbox = &pci_mailbox, + .target_window1 = &pci_target_window1, + .master_latency_timer = 0x80, + .retry_limit = 0, + .arbiter_priority_control = PCI_ARBITRATION_MODE_FAIR, + .take_away_gnt_mode = PCI_TAKE_AWAY_GNT_DISABLE, +}; + +static struct pci_controller vr41xx_pci_controller = { + .pci_ops = &vr41xx_pci_ops, + .mem_resource = &pci_mem_resource, + .io_resource = &pci_io_resource, +}; + +void __init vr41xx_pciu_setup(struct pci_controller_unit_setup *setup) +{ + vr41xx_pci_controller_unit_setup = *setup; +} + +static int __init vr41xx_pciu_init(void) +{ + struct pci_controller_unit_setup *setup; + struct pci_master_address_conversion *master; + struct pci_target_address_conversion *target; + struct pci_mailbox_address *mailbox; + struct pci_target_address_window *window; + unsigned long vtclock, pci_clock_max; + uint32_t val; + + setup = &vr41xx_pci_controller_unit_setup; + + /* Disable PCI interrupt */ + vr41xx_disable_pciint(); + + /* Supply VTClock to PCIU */ + vr41xx_supply_clock(PCIU_CLOCK); + + /* Dummy write, waiting for supply of VTClock. */ + vr41xx_disable_pciint(); + + /* Select PCI clock */ + if (setup->pci_clock_max != 0) + pci_clock_max = setup->pci_clock_max; + else + pci_clock_max = PCI_CLOCK_MAX; + vtclock = vr41xx_get_vtclock_frequency(); + if (vtclock < pci_clock_max) + writel(EQUAL_VTCLOCK, PCICLKSELREG); + else if ((vtclock / 2) < pci_clock_max) + writel(HALF_VTCLOCK, PCICLKSELREG); + else if (current_cpu_data.processor_id >= PRID_VR4131_REV2_1 && + (vtclock / 3) < pci_clock_max) + writel(ONE_THIRD_VTCLOCK, PCICLKSELREG); + else if ((vtclock / 4) < pci_clock_max) + writel(QUARTER_VTCLOCK, PCICLKSELREG); + else { + printk(KERN_ERR "PCI Clock is over 33MHz.\n"); + return -EINVAL; + } + + /* Supply PCI clock by PCI bus */ + vr41xx_supply_clock(PCI_CLOCK); + + if (setup->master_memory1 != NULL) { + master = setup->master_memory1; + val = IBA(master->bus_base_address) | + MASTER_MSK(master->address_mask) | + WINEN | + PCIA(master->pci_base_address); + writel(val, PCIMMAW1REG); + } else { + val = readl(PCIMMAW1REG); + val &= ~WINEN; + writel(val, PCIMMAW1REG); + } + + if (setup->master_memory2 != NULL) { + master = setup->master_memory2; + val = IBA(master->bus_base_address) | + MASTER_MSK(master->address_mask) | + WINEN | + PCIA(master->pci_base_address); + writel(val, PCIMMAW2REG); + } else { + val = readl(PCIMMAW2REG); + val &= ~WINEN; + writel(val, PCIMMAW2REG); + } + + if (setup->target_memory1 != NULL) { + target = setup->target_memory1; + val = TARGET_MSK(target->address_mask) | + WINEN | + ITA(target->bus_base_address); + writel(val, PCITAW1REG); + } else { + val = readl(PCITAW1REG); + val &= ~WINEN; + writel(val, PCITAW1REG); + } + + if (setup->target_memory2 != NULL) { + target = setup->target_memory2; + val = TARGET_MSK(target->address_mask) | + WINEN | + ITA(target->bus_base_address); + writel(val, PCITAW2REG); + } else { + val = readl(PCITAW2REG); + val &= ~WINEN; + writel(val, PCITAW2REG); + } + + if (setup->master_io != NULL) { + master = setup->master_io; + val = IBA(master->bus_base_address) | + MASTER_MSK(master->address_mask) | + WINEN | + PCIIA(master->pci_base_address); + writel(val, PCIMIOAWREG); + } else { + val = readl(PCIMIOAWREG); + val &= ~WINEN; + writel(val, PCIMIOAWREG); + } + + if (setup->exclusive_access == CANNOT_LOCK_FROM_DEVICE) + writel(UNLOCK, PCIEXACCREG); + else + writel(0, PCIEXACCREG); + + if (current_cpu_data.cputype == CPU_VR4122) + writel(TRDYV(setup->wait_time_limit_from_irdy_to_trdy), PCITRDYVREG); + + writel(MLTIM(setup->master_latency_timer), LATTIMEREG); + + if (setup->mailbox != NULL) { + mailbox = setup->mailbox; + val = MBADD(mailbox->base_address) | TYPE_32BITSPACE | + MSI_MEMORY | PREF_APPROVAL; + writel(val, MAILBAREG); + } + + if (setup->target_window1) { + window = setup->target_window1; + val = PMBA(window->base_address) | TYPE_32BITSPACE | + MSI_MEMORY | PREF_APPROVAL; + writel(val, PCIMBA1REG); + } + + if (setup->target_window2) { + window = setup->target_window2; + val = PMBA(window->base_address) | TYPE_32BITSPACE | + MSI_MEMORY | PREF_APPROVAL; + writel(val, PCIMBA2REG); + } + + val = readl(RETVALREG); + val &= ~RTYVAL_MASK; + val |= RTYVAL(setup->retry_limit); + writel(val, RETVALREG); + + val = readl(PCIAPCNTREG); + val &= ~(TKYGNT | PAPC); + + switch (setup->arbiter_priority_control) { + case PCI_ARBITRATION_MODE_ALTERNATE_0: + val |= PAPC_ALTERNATE_0; + break; + case PCI_ARBITRATION_MODE_ALTERNATE_B: + val |= PAPC_ALTERNATE_B; + break; + default: + val |= PAPC_FAIR; + break; + } + + if (setup->take_away_gnt_mode == PCI_TAKE_AWAY_GNT_ENABLE) + val |= TKYGNT_ENABLE; + + writel(val, PCIAPCNTREG); + + writel(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR, COMMANDREG); + + /* Clear bus error */ + readl(BUSERRADREG); + + writel(BLOODY_CONFIG_DONE, PCIENREG); + + if (setup->mem_resource != NULL) + vr41xx_pci_controller.mem_resource = setup->mem_resource; + + if (setup->io_resource != NULL) { + vr41xx_pci_controller.io_resource = setup->io_resource; + } else { + set_io_port_base(IO_PORT_BASE); + ioport_resource.start = IO_PORT_RESOURCE_START; + ioport_resource.end = IO_PORT_RESOURCE_END; + } + + register_pci_controller(&vr41xx_pci_controller); + + return 0; +} + +arch_initcall(vr41xx_pciu_init); diff --git a/arch/mips/pci/pci-vr41xx.h b/arch/mips/pci/pci-vr41xx.h new file mode 100644 index 00000000000..23815c8b903 --- /dev/null +++ b/arch/mips/pci/pci-vr41xx.h @@ -0,0 +1,151 @@ +/* + * pci-vr41xx.h, Include file for PCI Control Unit of the NEC VR4100 series. + * + * Copyright (C) 2002 MontaVista Software Inc. + * Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com> + * Copyright (C) 2004 Yoichi Yuasa <yuasa@hh.iij4u.or.jp> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PCI_VR41XX_H +#define __PCI_VR41XX_H + +#define PCIMMAW1REG KSEG1ADDR(0x0f000c00) +#define PCIMMAW2REG KSEG1ADDR(0x0f000c04) +#define PCITAW1REG KSEG1ADDR(0x0f000c08) +#define PCITAW2REG KSEG1ADDR(0x0f000c0c) +#define PCIMIOAWREG KSEG1ADDR(0x0f000c10) + #define IBA(addr) ((addr) & 0xff000000U) + #define MASTER_MSK(mask) (((mask) >> 11) & 0x000fe000U) + #define PCIA(addr) (((addr) >> 24) & 0x000000ffU) + #define TARGET_MSK(mask) (((mask) >> 8) & 0x000fe000U) + #define ITA(addr) (((addr) >> 24) & 0x000000ffU) + #define PCIIA(addr) (((addr) >> 24) & 0x000000ffU) + #define WINEN 0x1000U +#define PCICONFDREG KSEG1ADDR(0x0f000c14) +#define PCICONFAREG KSEG1ADDR(0x0f000c18) +#define PCIMAILREG KSEG1ADDR(0x0f000c1c) +#define BUSERRADREG KSEG1ADDR(0x0f000c24) + #define EA(reg) ((reg) &0xfffffffc) + +#define INTCNTSTAREG KSEG1ADDR(0x0f000c28) + #define MABTCLR 0x80000000U + #define TRDYCLR 0x40000000U + #define PARCLR 0x20000000U + #define MBCLR 0x10000000U + #define SERRCLR 0x08000000U + #define RTYCLR 0x04000000U + #define MABCLR 0x02000000U + #define TABCLR 0x01000000U + /* RFU */ + #define MABTMSK 0x00008000U + #define TRDYMSK 0x00004000U + #define PARMSK 0x00002000U + #define MBMSK 0x00001000U + #define SERRMSK 0x00000800U + #define RTYMSK 0x00000400U + #define MABMSK 0x00000200U + #define TABMSK 0x00000100U + #define IBAMABT 0x00000080U + #define TRDYRCH 0x00000040U + #define PAR 0x00000020U + #define MB 0x00000010U + #define PCISERR 0x00000008U + #define RTYRCH 0x00000004U + #define MABORT 0x00000002U + #define TABORT 0x00000001U + +#define PCIEXACCREG KSEG1ADDR(0x0f000c2c) + #define UNLOCK 0x2U + #define EAREQ 0x1U +#define PCIRECONTREG KSEG1ADDR(0x0f000c30) + #define RTRYCNT(reg) ((reg) & 0x000000ffU) +#define PCIENREG KSEG1ADDR(0x0f000c34) + #define BLOODY_CONFIG_DONE 0x4U +#define PCICLKSELREG KSEG1ADDR(0x0f000c38) + #define EQUAL_VTCLOCK 0x2U + #define HALF_VTCLOCK 0x0U + #define ONE_THIRD_VTCLOCK 0x3U + #define QUARTER_VTCLOCK 0x1U +#define PCITRDYVREG KSEG1ADDR(0x0f000c3c) + #define TRDYV(val) ((uint32_t)(val) & 0xffU) +#define PCICLKRUNREG KSEG1ADDR(0x0f000c60) + +#define VENDORIDREG KSEG1ADDR(0x0f000d00) +#define DEVICEIDREG KSEG1ADDR(0x0f000d00) +#define COMMANDREG KSEG1ADDR(0x0f000d04) +#define STATUSREG KSEG1ADDR(0x0f000d04) +#define REVIDREG KSEG1ADDR(0x0f000d08) +#define CLASSREG KSEG1ADDR(0x0f000d08) +#define CACHELSREG KSEG1ADDR(0x0f000d0c) +#define LATTIMEREG KSEG1ADDR(0x0f000d0c) + #define MLTIM(val) (((uint32_t)(val) << 7) & 0xff00U) +#define MAILBAREG KSEG1ADDR(0x0f000d10) +#define PCIMBA1REG KSEG1ADDR(0x0f000d14) +#define PCIMBA2REG KSEG1ADDR(0x0f000d18) + #define MBADD(base) ((base) & 0xfffff800U) + #define PMBA(base) ((base) & 0xffe00000U) + #define PREF 0x8U + #define PREF_APPROVAL 0x8U + #define PREF_DISAPPROVAL 0x0U + #define TYPE 0x6U + #define TYPE_32BITSPACE 0x0U + #define MSI 0x1U + #define MSI_MEMORY 0x0U +#define INTLINEREG KSEG1ADDR(0x0f000d3c) +#define INTPINREG KSEG1ADDR(0x0f000d3c) +#define RETVALREG KSEG1ADDR(0x0f000d40) +#define PCIAPCNTREG KSEG1ADDR(0x0f000d40) + #define TKYGNT 0x04000000U + #define TKYGNT_ENABLE 0x04000000U + #define TKYGNT_DISABLE 0x00000000U + #define PAPC 0x03000000U + #define PAPC_ALTERNATE_B 0x02000000U + #define PAPC_ALTERNATE_0 0x01000000U + #define PAPC_FAIR 0x00000000U + #define RTYVAL(val) (((uint32_t)(val) << 7) & 0xff00U) + #define RTYVAL_MASK 0xff00U + +#define PCI_CLOCK_MAX 33333333U + +/* + * Default setup + */ +#define PCI_MASTER_MEM1_BUS_BASE_ADDRESS 0x10000000U +#define PCI_MASTER_MEM1_ADDRESS_MASK 0x7c000000U +#define PCI_MASTER_MEM1_PCI_BASE_ADDRESS 0x10000000U + +#define PCI_TARGET_MEM1_ADDRESS_MASK 0x08000000U +#define PCI_TARGET_MEM1_BUS_BASE_ADDRESS 0x00000000U + +#define PCI_MASTER_IO_BUS_BASE_ADDRESS 0x16000000U +#define PCI_MASTER_IO_ADDRESS_MASK 0x7e000000U +#define PCI_MASTER_IO_PCI_BASE_ADDRESS 0x00000000U + +#define PCI_MAILBOX_BASE_ADDRESS 0x00000000U + +#define PCI_TARGET_WINDOW1_BASE_ADDRESS 0x00000000U + +#define IO_PORT_BASE KSEG1ADDR(PCI_MASTER_IO_BUS_BASE_ADDRESS) +#define IO_PORT_RESOURCE_START PCI_MASTER_IO_PCI_BASE_ADDRESS +#define IO_PORT_RESOURCE_END (~PCI_MASTER_IO_ADDRESS_MASK & PCI_MASTER_ADDRESS_MASK) + +#define PCI_IO_RESOURCE_START 0x01000000UL +#define PCI_IO_RESOURCE_END 0x01ffffffUL + +#define PCI_MEM_RESOURCE_START 0x11000000UL +#define PCI_MEM_RESOURCE_END 0x13ffffffUL + +#endif /* __PCI_VR41XX_H */ diff --git a/arch/mips/pci/pci-yosemite.c b/arch/mips/pci/pci-yosemite.c new file mode 100644 index 00000000000..dac9ed4b0cc --- /dev/null +++ b/arch/mips/pci/pci-yosemite.c @@ -0,0 +1,60 @@ +/* + * 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. + * + * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <asm/titan_dep.h> + +extern struct pci_ops titan_pci_ops; + +static struct resource py_mem_resource = { + "Titan PCI MEM", 0xe0000000UL, 0xe3ffffffUL, IORESOURCE_MEM +}; + +/* + * PMON really reserves 16MB of I/O port space but that's stupid, nothing + * needs that much since allocations are limited to 256 bytes per device + * anyway. So we just claim 64kB here. + */ +#define TITAN_IO_SIZE 0x0000ffffUL +#define TITAN_IO_BASE 0xe8000000UL + +static struct resource py_io_resource = { + "Titan IO MEM", 0x00001000UL, TITAN_IO_SIZE - 1, IORESOURCE_IO, +}; + +static struct pci_controller py_controller = { + .pci_ops = &titan_pci_ops, + .mem_resource = &py_mem_resource, + .mem_offset = 0x00000000UL, + .io_resource = &py_io_resource, + .io_offset = 0x00000000UL +}; + +static char ioremap_failed[] __initdata = "Could not ioremap I/O port range"; + +static int __init pmc_yosemite_setup(void) +{ + unsigned long io_v_base; + + io_v_base = (unsigned long) ioremap(TITAN_IO_BASE, TITAN_IO_SIZE); + if (!io_v_base) + panic(ioremap_failed); + + set_io_port_base(io_v_base); + TITAN_WRITE(RM9000x2_OCD_LKM7, TITAN_READ(RM9000x2_OCD_LKM7) | 1); + + ioport_resource.end = TITAN_IO_SIZE - 1; + + register_pci_controller(&py_controller); + + return 0; +} + +arch_initcall(pmc_yosemite_setup); diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c new file mode 100644 index 00000000000..8141dffac24 --- /dev/null +++ b/arch/mips/pci/pci.c @@ -0,0 +1,304 @@ +/* + * 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. + * + * Copyright (C) 2003, 04 Ralf Baechle (ralf@linux-mips.org) + */ +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/bootmem.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> + +/* + * Indicate whether we respect the PCI setup left by the firmware. + * + * Make this long-lived so that we know when shutting down + * whether we probed only or not. + */ +int pci_probe_only; + +#define PCI_ASSIGN_ALL_BUSSES 1 + +unsigned int pci_probe = PCI_ASSIGN_ALL_BUSSES; + +/* + * The PCI controller list. + */ + +struct pci_controller *hose_head, **hose_tail = &hose_head; +struct pci_controller *pci_isa_hose; + +unsigned long PCIBIOS_MIN_IO = 0x0000; +unsigned long PCIBIOS_MIN_MEM = 0; + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void +pcibios_align_resource(void *data, struct resource *res, + unsigned long size, unsigned long align) +{ + struct pci_dev *dev = data; + struct pci_controller *hose = dev->sysdata; + unsigned long start = res->start; + + if (res->flags & IORESOURCE_IO) { + /* Make sure we start at our min on all hoses */ + if (start < PCIBIOS_MIN_IO + hose->io_resource->start) + start = PCIBIOS_MIN_IO + hose->io_resource->start; + + /* + * Put everything into 0x00-0xff region modulo 0x400 + */ + if (start & 0x300) + start = (start + 0x3ff) & ~0x3ff; + } else if (res->flags & IORESOURCE_MEM) { + /* Make sure we start at our min on all hoses */ + if (start < PCIBIOS_MIN_MEM + hose->mem_resource->start) + start = PCIBIOS_MIN_MEM + hose->mem_resource->start; + } + + res->start = start; +} + +struct pci_controller * __init alloc_pci_controller(void) +{ + return alloc_bootmem(sizeof(struct pci_controller)); +} + +void __init register_pci_controller(struct pci_controller *hose) +{ + *hose_tail = hose; + hose_tail = &hose->next; +} + +/* Most MIPS systems have straight-forward swizzling needs. */ + +static inline u8 bridge_swizzle(u8 pin, u8 slot) +{ + return (((pin - 1) + slot) % 4) + 1; +} + +static u8 __init common_swizzle(struct pci_dev *dev, u8 *pinp) +{ + u8 pin = *pinp; + + while (dev->bus->parent) { + pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + /* Move up the chain of bridges. */ + dev = dev->bus->self; + } + *pinp = pin; + + /* The slot is the slot of the last bridge. */ + return PCI_SLOT(dev->devfn); +} + +static int __init pcibios_init(void) +{ + struct pci_controller *hose; + struct pci_bus *bus; + int next_busno; + int need_domain_info = 0; + + /* Scan all of the recorded PCI controllers. */ + for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + + if (request_resource(&iomem_resource, hose->mem_resource) < 0) + goto out; + if (request_resource(&ioport_resource, hose->io_resource) < 0) + goto out_free_mem_resource; + + if (!hose->iommu) + PCI_DMA_BUS_IS_PHYS = 1; + + bus = pci_scan_bus(next_busno, hose->pci_ops, hose); + hose->bus = bus; + hose->need_domain_info = need_domain_info; + next_busno = bus->subordinate + 1; + /* Don't allow 8-bit bus number overflow inside the hose - + reserve some space for bridges. */ + if (next_busno > 224) { + next_busno = 0; + need_domain_info = 1; + } + continue; + +out_free_mem_resource: + release_resource(hose->mem_resource); + +out: + printk(KERN_WARNING + "Skipping PCI bus scan due to resource conflict\n"); + } + + if (!pci_probe_only) + pci_assign_unassigned_resources(); + pci_fixup_irqs(common_swizzle, pcibios_map_irq); + + return 0; +} + +subsys_initcall(pcibios_init); + +static int pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1<<idx))) + continue; + + 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 (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n", + pci_name(dev), lat); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +unsigned int pcibios_assign_all_busses(void) +{ + return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0; +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int err; + + if ((err = pcibios_enable_resources(dev, mask)) < 0) + return err; + + return pcibios_plat_dev_init(dev); +} + +static void __init pcibios_fixup_device_resources(struct pci_dev *dev, + struct pci_bus *bus) +{ + /* Update device resources. */ + struct pci_controller *hose = (struct pci_controller *)bus->sysdata; + unsigned long offset = 0; + int i; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + if (!dev->resource[i].start) + continue; + if (dev->resource[i].flags & IORESOURCE_IO) + offset = hose->io_offset; + else if (dev->resource[i].flags & IORESOURCE_MEM) + offset = hose->mem_offset; + + dev->resource[i].start += offset; + dev->resource[i].end += offset; + } +} + +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ + /* Propagate hose info into the subordinate devices. */ + + struct pci_controller *hose = bus->sysdata; + struct list_head *ln; + struct pci_dev *dev = bus->self; + + if (!dev) { + bus->resource[0] = hose->io_resource; + bus->resource[1] = hose->mem_resource; + } else if (pci_probe_only && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + pci_read_bridge_bases(bus); + pcibios_fixup_device_resources(dev, bus); + } + + for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { + struct pci_dev *dev = pci_dev_b(ln); + + if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) + pcibios_fixup_device_resources(dev, bus); + } +} + +void __init +pcibios_update_irq(struct pci_dev *dev, int irq) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); +} + +void __devinit +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + struct pci_controller *hose = (struct pci_controller *)dev->sysdata; + unsigned long offset = 0; + + if (res->flags & IORESOURCE_IO) + offset = hose->io_offset; + else if (res->flags & IORESOURCE_MEM) + offset = hose->mem_offset; + + region->start = res->start - offset; + region->end = res->end - offset; +} + +#ifdef CONFIG_HOTPLUG +EXPORT_SYMBOL(pcibios_resource_to_bus); +EXPORT_SYMBOL(PCIBIOS_MIN_IO); +EXPORT_SYMBOL(PCIBIOS_MIN_MEM); +#endif + +char *pcibios_setup(char *str) +{ + return str; +} |