Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

65
arch/mips/pci/Makefile Normal file
View file

@ -0,0 +1,65 @@
#
# Makefile for the PCI specific kernel interface routines under Linux.
#
obj-y += pci.o
#
# PCI bus host bridge specific code
#
obj-$(CONFIG_MIPS_BONITO64) += ops-bonito64.o
obj-$(CONFIG_PCI_GT64XXX_PCI0) += ops-gt64xxx_pci0.o
obj-$(CONFIG_MIPS_MSC) += ops-msc.o
obj-$(CONFIG_MIPS_NILE4) += ops-nile4.o
obj-$(CONFIG_SOC_TX3927) += ops-tx3927.o
obj-$(CONFIG_PCI_VR41XX) += ops-vr41xx.o pci-vr41xx.o
obj-$(CONFIG_NEC_MARKEINS) += ops-emma2rh.o pci-emma2rh.o fixup-emma2rh.o
obj-$(CONFIG_PCI_TX4927) += ops-tx4927.o
obj-$(CONFIG_BCM47XX) += pci-bcm47xx.o
obj-$(CONFIG_BCM63XX) += pci-bcm63xx.o fixup-bcm63xx.o \
ops-bcm63xx.o
obj-$(CONFIG_MIPS_ALCHEMY) += pci-alchemy.o
obj-$(CONFIG_SOC_AR71XX) += pci-ar71xx.o
obj-$(CONFIG_PCI_AR724X) += pci-ar724x.o
obj-$(CONFIG_MIPS_PCI_VIRTIO) += pci-virtio-guest.o
#
# These are still pretty much in the old state, watch, go blind.
#
obj-$(CONFIG_LASAT) += pci-lasat.o
obj-$(CONFIG_MIPS_COBALT) += fixup-cobalt.o
obj-$(CONFIG_LEMOTE_FULOONG2E) += fixup-fuloong2e.o ops-loongson2.o
obj-$(CONFIG_LEMOTE_MACH2F) += fixup-lemote2f.o ops-loongson2.o
obj-$(CONFIG_LOONGSON_MACH3X) += fixup-loongson3.o ops-loongson3.o
obj-$(CONFIG_MIPS_MALTA) += fixup-malta.o pci-malta.o
obj-$(CONFIG_PMC_MSP7120_GW) += fixup-pmcmsp.o ops-pmcmsp.o
obj-$(CONFIG_PMC_MSP7120_EVAL) += fixup-pmcmsp.o ops-pmcmsp.o
obj-$(CONFIG_PMC_MSP7120_FPGA) += fixup-pmcmsp.o ops-pmcmsp.o
obj-$(CONFIG_SGI_IP27) += ops-bridge.o 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_SIBYTE_BCM112X) += fixup-sb1250.o pci-sb1250.o
obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o
obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o
obj-$(CONFIG_LANTIQ) += fixup-lantiq.o
obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o
obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o
obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o
obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o
obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o
obj-$(CONFIG_TOSHIBA_JMR3927) += fixup-jmr3927.o
obj-$(CONFIG_SOC_TX4927) += pci-tx4927.o
obj-$(CONFIG_SOC_TX4938) += pci-tx4938.o
obj-$(CONFIG_SOC_TX4939) += pci-tx4939.o
obj-$(CONFIG_TOSHIBA_RBTX4927) += fixup-rbtx4927.o
obj-$(CONFIG_TOSHIBA_RBTX4938) += fixup-rbtx4938.o
obj-$(CONFIG_VICTOR_MPC30X) += fixup-mpc30x.o
obj-$(CONFIG_ZAO_CAPCELLA) += fixup-capcella.o
obj-$(CONFIG_MIKROTIK_RB532) += pci-rc32434.o ops-rc32434.o fixup-rc32434.o
obj-$(CONFIG_CAVIUM_OCTEON_SOC) += pci-octeon.o pcie-octeon.o
obj-$(CONFIG_CPU_XLR) += pci-xlr.o
obj-$(CONFIG_CPU_XLP) += pci-xlp.o
ifdef CONFIG_PCI_MSI
obj-$(CONFIG_CAVIUM_OCTEON_SOC) += msi-octeon.o
obj-$(CONFIG_CPU_XLP) += msi-xlp.o
endif

View file

@ -0,0 +1,21 @@
/*
* 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) 2008 Maxime Bizon <mbizon@freebox.fr>
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <bcm63xx_cpu.h>
int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return bcm63xx_get_irq_number(IRQ_PCI);
}
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}

View file

@ -0,0 +1,50 @@
/*
* fixup-cappcela.c, The ZAO Networks Capcella specific PCI fixups.
*
* Copyright (C) 2002,2004 Yoichi Yuasa <yuasa@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
*/
#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(const 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;
}

View file

@ -0,0 +1,193 @@
/*
* 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 <cobalt.h>
#include <irq.h>
/*
* PCI slot numbers
*/
#define COBALT_PCICONF_CPU 0x06
#define COBALT_PCICONF_ETH0 0x07
#define COBALT_PCICONF_RAQSCSI 0x08
#define COBALT_PCICONF_VIA 0x09
#define COBALT_PCICONF_PCISLOT 0x0A
#define COBALT_PCICONF_ETH1 0x0C
/*
* The Cobalt board ID information. The boards have an ID number wired
* into the VIA that is available in the high nibble of register 94.
*/
#define VIA_COBALT_BRD_ID_REG 0x94
#define VIA_COBALT_BRD_REG_to_ID(reg) ((unsigned char)(reg) >> 4)
static void qube_raq_galileo_early_fixup(struct pci_dev *dev)
{
if (dev->devfn == PCI_DEVFN(0, 0) &&
(dev->class >> 8) == PCI_CLASS_MEMORY_OTHER) {
dev->class = (PCI_CLASS_BRIDGE_HOST << 8) | (dev->class & 0xff);
printk(KERN_INFO "Galileo: fixed bridge class\n");
}
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_GT64111,
qube_raq_galileo_early_fixup);
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, &lt);
if (lt < 64)
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8);
}
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)
{
if (dev->devfn != PCI_DEVFN(0, 0))
return;
/* 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, 8);
/*
* The code described by the comment below has been removed
* as it causes bus mastering by the Ethernet controllers
* to break under any kind of network load. We always set
* the retry timeouts to their maximum.
*
* --x--x--x--x--x--x--x--x--x--x--x--x--x--x--x--x--x--x--x--x--
*
* 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.
*/
printk(KERN_INFO "Galileo: revision %u\n", dev->revision);
#if 0
if (dev->revision >= 0x10) {
/* New Galileo, assumes PCI stop line to VIA is connected. */
GT_WRITE(GT_PCI0_TOR_OFS, 0x4020);
} else if (dev->revision == 0x1 || dev->revision == 0x2)
#endif
{
signed int timeo;
/* XXX WE MUST DO THIS ELSE GALILEO LOCKS UP! -DaveM */
timeo = GT_READ(GT_PCI0_TOR_OFS);
/* Old Galileo, assumes PCI STOP line to VIA is disconnected. */
GT_WRITE(GT_PCI0_TOR_OFS,
(0xff << 16) | /* retry count */
(0xff << 8) | /* timeout 1 */
0xff); /* timeout 0 */
/* enable PCI retry exceeded interrupt */
GT_WRITE(GT_INTRMASK_OFS, GT_INTR_RETRYCTR0_MSK | GT_READ(GT_INTRMASK_OFS));
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_GT64111,
qube_raq_galileo_fixup);
int cobalt_board_id;
static void qube_raq_via_board_id_fixup(struct pci_dev *dev)
{
u8 id;
int retval;
retval = pci_read_config_byte(dev, VIA_COBALT_BRD_ID_REG, &id);
if (retval) {
panic("Cannot read board ID");
return;
}
cobalt_board_id = VIA_COBALT_BRD_REG_to_ID(id);
printk(KERN_INFO "Cobalt board ID: %d\n", cobalt_board_id);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_0,
qube_raq_via_board_id_fixup);
static char irq_tab_qube1[] __initdata = {
[COBALT_PCICONF_CPU] = 0,
[COBALT_PCICONF_ETH0] = QUBE1_ETH0_IRQ,
[COBALT_PCICONF_RAQSCSI] = SCSI_IRQ,
[COBALT_PCICONF_VIA] = 0,
[COBALT_PCICONF_PCISLOT] = PCISLOT_IRQ,
[COBALT_PCICONF_ETH1] = 0
};
static char irq_tab_cobalt[] __initdata = {
[COBALT_PCICONF_CPU] = 0,
[COBALT_PCICONF_ETH0] = ETH0_IRQ,
[COBALT_PCICONF_RAQSCSI] = SCSI_IRQ,
[COBALT_PCICONF_VIA] = 0,
[COBALT_PCICONF_PCISLOT] = PCISLOT_IRQ,
[COBALT_PCICONF_ETH1] = ETH1_IRQ
};
static char irq_tab_raq2[] __initdata = {
[COBALT_PCICONF_CPU] = 0,
[COBALT_PCICONF_ETH0] = ETH0_IRQ,
[COBALT_PCICONF_RAQSCSI] = RAQ2_SCSI_IRQ,
[COBALT_PCICONF_VIA] = 0,
[COBALT_PCICONF_PCISLOT] = PCISLOT_IRQ,
[COBALT_PCICONF_ETH1] = ETH1_IRQ
};
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (cobalt_board_id <= COBALT_BRD_ID_QUBE1)
return irq_tab_qube1[slot];
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;
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/ddb5xxx/ddb5477/pci.c
*
* Copyright 2001 MontaVista Software Inc.
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <asm/bootinfo.h>
#include <asm/emma/emma2rh.h>
#define EMMA2RH_PCI_HOST_SLOT 0x09
#define EMMA2RH_USB_SLOT 0x03
#define PCI_DEVICE_ID_NEC_EMMA2RH 0x014b /* EMMA2RH PCI Host */
/*
* 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).
*
*/
#define MAX_SLOT_NUM 10
static unsigned char irq_map[][5] __initdata = {
[3] = {0, MARKEINS_PCI_IRQ_INTB, MARKEINS_PCI_IRQ_INTC,
MARKEINS_PCI_IRQ_INTD, 0,},
[4] = {0, MARKEINS_PCI_IRQ_INTA, 0, 0, 0,},
[5] = {0, 0, 0, 0, 0,},
[6] = {0, MARKEINS_PCI_IRQ_INTC, MARKEINS_PCI_IRQ_INTD,
MARKEINS_PCI_IRQ_INTA, MARKEINS_PCI_IRQ_INTB,},
};
static void nec_usb_controller_fixup(struct pci_dev *dev)
{
if (PCI_SLOT(dev->devfn) == EMMA2RH_USB_SLOT)
/* on board USB controller configuration */
pci_write_config_dword(dev, 0xe4, 1 << 5);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB,
nec_usb_controller_fixup);
/*
* Prevent the PCI layer from seeing the resources allocated to this device
* if it is the host bridge by marking it as such. These resources are of
* no consequence to the PCI layer (they are handled elsewhere).
*/
static void emma2rh_pci_host_fixup(struct pci_dev *dev)
{
int i;
if (PCI_SLOT(dev->devfn) == EMMA2RH_PCI_HOST_SLOT) {
dev->class &= 0xff;
dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
dev->resource[i].start = 0;
dev->resource[i].end = 0;
dev->resource[i].flags = 0;
}
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_EMMA2RH,
emma2rh_pci_host_fixup);
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return irq_map[slot][pin];
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}

View file

@ -0,0 +1,225 @@
/*
* Copyright (C) 2004 ICT CAS
* Author: Li xiaoyu, ICT CAS
* lixy@ict.ac.cn
*
* Copyright (C) 2007 Lemote, Inc. & Institute of Computing Technology
* Author: Fuxin Zhang, zhangfx@lemote.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>
#include <loongson.h>
/* South bridge slot number is set by the pci probe process */
static u8 sb_slot = 5;
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq = 0;
if (slot == sb_slot) {
switch (PCI_FUNC(dev->devfn)) {
case 2:
irq = 10;
break;
case 3:
irq = 11;
break;
case 5:
irq = 9;
break;
}
} else {
irq = LOONGSON_IRQ_BASE + 25 + pin;
}
return irq;
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
static void loongson2e_nec_fixup(struct pci_dev *pdev)
{
unsigned int val;
/* Configures port 1, 2, 3, 4 to be validate*/
pci_read_config_dword(pdev, 0xe0, &val);
pci_write_config_dword(pdev, 0xe0, (val & ~7) | 0x4);
/* System clock is 48-MHz Oscillator. */
pci_write_config_dword(pdev, 0xe4, 1 << 5);
}
static void loongson2e_686b_func0_fixup(struct pci_dev *pdev)
{
unsigned char c;
sb_slot = PCI_SLOT(pdev->devfn);
printk(KERN_INFO "via686b fix: ISA bridge\n");
/* Enable I/O Recovery time */
pci_write_config_byte(pdev, 0x40, 0x08);
/* Enable ISA refresh */
pci_write_config_byte(pdev, 0x41, 0x01);
/* disable ISA line buffer */
pci_write_config_byte(pdev, 0x45, 0x00);
/* Gate INTR, and flush line buffer */
pci_write_config_byte(pdev, 0x46, 0xe0);
/* Disable PCI Delay Transaction, Enable EISA ports 4D0/4D1. */
/* pci_write_config_byte(pdev, 0x47, 0x20); */
/*
* enable PCI Delay Transaction, Enable EISA ports 4D0/4D1.
* enable time-out timer
*/
pci_write_config_byte(pdev, 0x47, 0xe6);
/*
* enable level trigger on pci irqs: 9,10,11,13
* important! without this PCI interrupts won't work
*/
outb(0x2e, 0x4d1);
/* 512 K PCI Decode */
pci_write_config_byte(pdev, 0x48, 0x01);
/* Wait for PGNT before grant to ISA Master/DMA */
pci_write_config_byte(pdev, 0x4a, 0x84);
/*
* Plug'n'Play
*
* Parallel DRQ 3, Floppy DRQ 2 (default)
*/
pci_write_config_byte(pdev, 0x50, 0x0e);
/*
* IRQ Routing for Floppy and Parallel port
*
* IRQ 6 for floppy, IRQ 7 for parallel port
*/
pci_write_config_byte(pdev, 0x51, 0x76);
/* IRQ Routing for serial ports (take IRQ 3 and 4) */
pci_write_config_byte(pdev, 0x52, 0x34);
/* All IRQ's level triggered. */
pci_write_config_byte(pdev, 0x54, 0x00);
/* route PIRQA-D irq */
pci_write_config_byte(pdev, 0x55, 0x90); /* bit 7-4, PIRQA */
pci_write_config_byte(pdev, 0x56, 0xba); /* bit 7-4, PIRQC; */
/* 3-0, PIRQB */
pci_write_config_byte(pdev, 0x57, 0xd0); /* bit 7-4, PIRQD */
/* enable function 5/6, audio/modem */
pci_read_config_byte(pdev, 0x85, &c);
c &= ~(0x3 << 2);
pci_write_config_byte(pdev, 0x85, c);
printk(KERN_INFO"via686b fix: ISA bridge done\n");
}
static void loongson2e_686b_func1_fixup(struct pci_dev *pdev)
{
printk(KERN_INFO"via686b fix: IDE\n");
/* Modify IDE controller setup */
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 48);
pci_write_config_byte(pdev, PCI_COMMAND,
PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER);
pci_write_config_byte(pdev, 0x40, 0x0b);
/* legacy mode */
pci_write_config_byte(pdev, 0x42, 0x09);
#if 1/* play safe, otherwise we may see notebook's usb keyboard lockup */
/* disable read prefetch/write post buffers */
pci_write_config_byte(pdev, 0x41, 0x02);
/* use 3/4 as fifo thresh hold */
pci_write_config_byte(pdev, 0x43, 0x0a);
pci_write_config_byte(pdev, 0x44, 0x00);
pci_write_config_byte(pdev, 0x45, 0x00);
#else
pci_write_config_byte(pdev, 0x41, 0xc2);
pci_write_config_byte(pdev, 0x43, 0x35);
pci_write_config_byte(pdev, 0x44, 0x1c);
pci_write_config_byte(pdev, 0x45, 0x10);
#endif
printk(KERN_INFO"via686b fix: IDE done\n");
}
static void loongson2e_686b_func2_fixup(struct pci_dev *pdev)
{
/* irq routing */
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, 10);
}
static void loongson2e_686b_func3_fixup(struct pci_dev *pdev)
{
/* irq routing */
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, 11);
}
static void loongson2e_686b_func5_fixup(struct pci_dev *pdev)
{
unsigned int val;
unsigned char c;
/* enable IO */
pci_write_config_byte(pdev, PCI_COMMAND,
PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER);
pci_read_config_dword(pdev, 0x4, &val);
pci_write_config_dword(pdev, 0x4, val | 1);
/* route ac97 IRQ */
pci_write_config_byte(pdev, 0x3c, 9);
pci_read_config_byte(pdev, 0x8, &c);
/* link control: enable link & SGD PCM output */
pci_write_config_byte(pdev, 0x41, 0xcc);
/* disable game port, FM, midi, sb, enable write to reg2c-2f */
pci_write_config_byte(pdev, 0x42, 0x20);
/* we are using Avance logic codec */
pci_write_config_word(pdev, 0x2c, 0x1005);
pci_write_config_word(pdev, 0x2e, 0x4710);
pci_read_config_dword(pdev, 0x2c, &val);
pci_write_config_byte(pdev, 0x42, 0x0);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686,
loongson2e_686b_func0_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1,
loongson2e_686b_func1_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_2,
loongson2e_686b_func2_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3,
loongson2e_686b_func3_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5,
loongson2e_686b_func5_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB,
loongson2e_nec_fixup);

View file

@ -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(const 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;
}

View file

@ -0,0 +1,79 @@
/*
*
* 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 <asm/txx9/pci.h>
#include <asm/txx9/jmr3927.h>
int __init jmr3927_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
unsigned char irq = pin;
/* IRQ rotation (PICMG) */
irq--; /* 0-3 */
if (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 (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 */
if (txx9_pci_option & TXX9_PCI_OPT_PICMG)
irq = (irq + 33 - slot) % 4;
else
irq = (irq + 3 + slot) % 4;
}
irq++; /* 1-4 */
switch (irq) {
case 1:
irq = JMR3927_IRQ_IOC_PCIA;
break;
case 2:
irq = JMR3927_IRQ_IOC_PCIB;
break;
case 3:
irq = JMR3927_IRQ_IOC_PCIC;
break;
case 4:
irq = JMR3927_IRQ_IOC_PCID;
break;
}
/* Check OnBoard Ethernet (IDSEL=A24, DevNu=13) */
if (dev->bus->parent == NULL &&
slot == TX3927_PCIC_IDSEL_AD_TO_SLOT(24))
irq = JMR3927_IRQ_ETHER0;
return irq;
}

View file

@ -0,0 +1,29 @@
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
*/
#include <linux/of_irq.h>
#include <linux/of_pci.h>
int (*ltq_pci_plat_arch_init)(struct pci_dev *dev) = NULL;
int (*ltq_pci_plat_dev_init)(struct pci_dev *dev) = NULL;
int pcibios_plat_dev_init(struct pci_dev *dev)
{
if (ltq_pci_plat_arch_init)
return ltq_pci_plat_arch_init(dev);
if (ltq_pci_plat_dev_init)
return ltq_pci_plat_dev_init(dev);
return 0;
}
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return of_irq_parse_and_map_pci(dev, slot, pin);
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 2008 Lemote Technology
* Copyright (C) 2004 ICT CAS
* Author: Li xiaoyu, lixy@ict.ac.cn
*
* Copyright (C) 2007 Lemote, Inc.
* Author: Fuxin Zhang, zhangfx@lemote.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>
#include <loongson.h>
#include <cs5536/cs5536.h>
#include <cs5536/cs5536_pci.h>
/* PCI interrupt pins
*
* These should not be changed, or you should consider loongson2f interrupt
* register and your pci card dispatch
*/
#define PCIA 4
#define PCIB 5
#define PCIC 6
#define PCID 7
/* all the pci device has the PCIA pin, check the datasheet. */
static char irq_tab[][5] __initdata = {
/* INTA INTB INTC INTD */
{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, 0, 0, 0, 0}, /* 16: Unused */
{0, PCIA, 0, 0, 0}, /* 17: RTL8110-0 */
{0, PCIB, 0, 0, 0}, /* 18: RTL8110-1 */
{0, PCIC, 0, 0, 0}, /* 19: SiI3114 */
{0, PCID, 0, 0, 0}, /* 20: 3-ports nec usb */
{0, PCIA, PCIB, PCIC, PCID}, /* 21: PCI-SLOT */
{0, 0, 0, 0, 0}, /* 22: Unused */
{0, 0, 0, 0, 0}, /* 23: Unused */
{0, 0, 0, 0, 0}, /* 24: Unused */
{0, 0, 0, 0, 0}, /* 25: Unused */
{0, 0, 0, 0, 0}, /* 26: Unused */
{0, 0, 0, 0, 0}, /* 27: Unused */
};
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int virq;
if ((PCI_SLOT(dev->devfn) != PCI_IDSEL_CS5536)
&& (PCI_SLOT(dev->devfn) < 32)) {
virq = irq_tab[slot][pin];
printk(KERN_INFO "slot: %d, pin: %d, irq: %d\n", slot, pin,
virq + LOONGSON_IRQ_BASE);
if (virq != 0)
return LOONGSON_IRQ_BASE + virq;
else
return 0;
} else if (PCI_SLOT(dev->devfn) == PCI_IDSEL_CS5536) { /* cs5536 */
switch (PCI_FUNC(dev->devfn)) {
case 2:
pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
CS5536_IDE_INTR);
return CS5536_IDE_INTR; /* for IDE */
case 3:
pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
CS5536_ACC_INTR);
return CS5536_ACC_INTR; /* for AUDIO */
case 4: /* for OHCI */
case 5: /* for EHCI */
case 6: /* for UDC */
case 7: /* for OTG */
pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
CS5536_USB_INTR);
return CS5536_USB_INTR;
}
return dev->irq;
} else {
printk(KERN_INFO " strange pci slot number.\n");
return 0;
}
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
/* CS5536 SPEC. fixup */
static void loongson_cs5536_isa_fixup(struct pci_dev *pdev)
{
/* the uart1 and uart2 interrupt in PIC is enabled as default */
pci_write_config_dword(pdev, PCI_UART1_INT_REG, 1);
pci_write_config_dword(pdev, PCI_UART2_INT_REG, 1);
}
static void loongson_cs5536_ide_fixup(struct pci_dev *pdev)
{
/* setting the mutex pin as IDE function */
pci_write_config_dword(pdev, PCI_IDE_CFG_REG,
CS5536_IDE_FLASH_SIGNATURE);
}
static void loongson_cs5536_acc_fixup(struct pci_dev *pdev)
{
/* enable the AUDIO interrupt in PIC */
pci_write_config_dword(pdev, PCI_ACC_INT_REG, 1);
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xc0);
}
static void loongson_cs5536_ohci_fixup(struct pci_dev *pdev)
{
/* enable the OHCI interrupt in PIC */
/* THE OHCI, EHCI, UDC, OTG are shared with interrupt in PIC */
pci_write_config_dword(pdev, PCI_OHCI_INT_REG, 1);
}
static void loongson_cs5536_ehci_fixup(struct pci_dev *pdev)
{
u32 hi, lo;
/* Serial short detect enable */
_rdmsr(USB_MSR_REG(USB_CONFIG), &hi, &lo);
_wrmsr(USB_MSR_REG(USB_CONFIG), (1 << 1) | (1 << 3), lo);
/* setting the USB2.0 micro frame length */
pci_write_config_dword(pdev, PCI_EHCI_FLADJ_REG, 0x2000);
}
static void loongson_nec_fixup(struct pci_dev *pdev)
{
unsigned int val;
pci_read_config_dword(pdev, 0xe0, &val);
/* Only 2 port be used */
pci_write_config_dword(pdev, 0xe0, (val & ~3) | 0x2);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
loongson_cs5536_isa_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_OHC,
loongson_cs5536_ohci_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_EHC,
loongson_cs5536_ehci_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO,
loongson_cs5536_acc_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE,
loongson_cs5536_ide_fixup);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB,
loongson_nec_fixup);

View file

@ -0,0 +1,66 @@
/*
* fixup-loongson3.c
*
* Copyright (C) 2012 Lemote, Inc.
* Author: Xiang Yu, xiangy@lemote.com
* Chen Huacai, chenhc@lemote.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.
*
*/
#include <linux/pci.h>
#include <boot_param.h>
static void print_fixup_info(const struct pci_dev *pdev)
{
dev_info(&pdev->dev, "Device %x:%x, irq %d\n",
pdev->vendor, pdev->device, pdev->irq);
}
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
print_fixup_info(dev);
return dev->irq;
}
static void pci_fixup_radeon(struct pci_dev *pdev)
{
if (pdev->resource[PCI_ROM_RESOURCE].start)
return;
if (!loongson_sysconf.vgabios_addr)
return;
pdev->resource[PCI_ROM_RESOURCE].start =
loongson_sysconf.vgabios_addr;
pdev->resource[PCI_ROM_RESOURCE].end =
loongson_sysconf.vgabios_addr + 256*1024 - 1;
pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_COPY;
dev_info(&pdev->dev, "BAR %d: assigned %pR for Radeon ROM\n",
PCI_ROM_RESOURCE, &pdev->resource[PCI_ROM_RESOURCE]);
}
DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_radeon);
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}

158
arch/mips/pci/fixup-malta.c Normal file
View file

@ -0,0 +1,158 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/mips-boards/piix4.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] = {
};
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(const 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 malta_piix_func3_base_fixup(struct pci_dev *dev)
{
/* Set a sane PM I/O base address */
pci_write_config_word(dev, PIIX4_FUNC3_PMBA, 0x1000);
/* Enable access to the PM I/O region */
pci_write_config_byte(dev, PIIX4_FUNC3_PMREGMISC,
PIIX4_FUNC3_PMREGMISC_EN);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3,
malta_piix_func3_base_fixup);
static void malta_piix_func0_fixup(struct pci_dev *pdev)
{
unsigned char reg_val;
u32 reg_val32;
u16 reg_val16;
/* PIIX PIRQC[A:D] irq mappings */
static int piixirqmap[PIIX4_FUNC0_PIRQRC_IRQ_ROUTING_MAX] = {
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, PIIX4_FUNC0_PIRQRC+i, &reg_val);
if (reg_val & PIIX4_FUNC0_PIRQRC_IRQ_ROUTING_DISABLE)
pci_irq[PCIA+i] = 0; /* Disabled */
else
pci_irq[PCIA+i] = piixirqmap[reg_val &
PIIX4_FUNC0_PIRQRC_IRQ_ROUTING_MASK];
}
/* 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, PIIX4_FUNC0_TOM, &reg_val);
pci_write_config_byte(pdev, PIIX4_FUNC0_TOM, reg_val |
PIIX4_FUNC0_TOM_TOP_OF_MEMORY_MASK);
}
/* Mux SERIRQ to its pin */
pci_read_config_dword(pdev, PIIX4_FUNC0_GENCFG, &reg_val32);
pci_write_config_dword(pdev, PIIX4_FUNC0_GENCFG,
reg_val32 | PIIX4_FUNC0_GENCFG_SERIRQ);
/* Enable SERIRQ */
pci_read_config_byte(pdev, PIIX4_FUNC0_SERIRQC, &reg_val);
reg_val |= PIIX4_FUNC0_SERIRQC_EN | PIIX4_FUNC0_SERIRQC_CONT;
pci_write_config_byte(pdev, PIIX4_FUNC0_SERIRQC, reg_val);
/* Enable response to special cycles */
pci_read_config_word(pdev, PCI_COMMAND, &reg_val16);
pci_write_config_word(pdev, PCI_COMMAND,
reg_val16 | PCI_COMMAND_SPECIAL);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0,
malta_piix_func0_fixup);
static void 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, PIIX4_FUNC1_IDETIM_PRIMARY_HI,
&reg_val);
pci_write_config_byte(pdev, PIIX4_FUNC1_IDETIM_PRIMARY_HI,
reg_val|PIIX4_FUNC1_IDETIM_PRIMARY_HI_IDE_DECODE_EN);
pci_read_config_byte(pdev, PIIX4_FUNC1_IDETIM_SECONDARY_HI,
&reg_val);
pci_write_config_byte(pdev, PIIX4_FUNC1_IDETIM_SECONDARY_HI,
reg_val|PIIX4_FUNC1_IDETIM_SECONDARY_HI_IDE_DECODE_EN);
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB,
malta_piix_func1_fixup);
/* Enable PCI 2.1 compatibility in PIIX4 */
static void quirk_dlcsetup(struct pci_dev *dev)
{
u8 odlc, ndlc;
(void) pci_read_config_byte(dev, PIIX4_FUNC0_DLC, &odlc);
/* Enable passive releases and delayed transaction */
ndlc = odlc | PIIX4_FUNC0_DLC_USBPR_EN |
PIIX4_FUNC0_DLC_PASSIVE_RELEASE_EN |
PIIX4_FUNC0_DLC_DELAYED_TRANSACTION_EN;
(void) pci_write_config_byte(dev, PIIX4_FUNC0_DLC, ndlc);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0,
quirk_dlcsetup);

View file

@ -0,0 +1,49 @@
/*
* fixup-mpc30x.c, The Victor MP-C303/304 specific PCI fixups.
*
* Copyright (C) 2002,2004 Yoichi Yuasa <yuasa@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
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/vr41xx/mpc30x.h>
static const int internal_func_irqs[] __initconst = {
VRC4173_CASCADE_IRQ,
VRC4173_AC97_IRQ,
VRC4173_USB_IRQ,
};
static const int irq_tab_mpc30x[] __initconst = {
[12] = VRC4173_PCMCIA1_IRQ,
[13] = VRC4173_PCMCIA2_IRQ,
[29] = MQ200_IRQ,
};
int __init pcibios_map_irq(const 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;
}

View file

@ -0,0 +1,216 @@
/*
* PMC-Sierra MSP board specific pci fixups.
*
* Copyright 2001 MontaVista Software Inc.
* Copyright 2005-2007 PMC-Sierra, 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.
*/
#ifdef CONFIG_PCI
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/byteorder.h>
#include <msp_pci.h>
#include <msp_cic_int.h>
/* PCI interrupt pins */
#define IRQ4 MSP_INT_EXT4
#define IRQ5 MSP_INT_EXT5
#define IRQ6 MSP_INT_EXT6
#if defined(CONFIG_PMC_MSP7120_GW)
/* Garibaldi Board IRQ wiring to PCI slots */
static char irq_tab[][5] __initdata = {
/* INTA INTB INTC INTD */
{0, 0, 0, 0, 0 }, /* (AD[0]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[1]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[2]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[3]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[4]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[5]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[6]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[7]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[8]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[9]): Unused */
{0, 0, 0, 0, 0 }, /* 0 (AD[10]): Unused */
{0, 0, 0, 0, 0 }, /* 1 (AD[11]): Unused */
{0, 0, 0, 0, 0 }, /* 2 (AD[12]): Unused */
{0, 0, 0, 0, 0 }, /* 3 (AD[13]): Unused */
{0, 0, 0, 0, 0 }, /* 4 (AD[14]): Unused */
{0, 0, 0, 0, 0 }, /* 5 (AD[15]): Unused */
{0, 0, 0, 0, 0 }, /* 6 (AD[16]): Unused */
{0, 0, 0, 0, 0 }, /* 7 (AD[17]): Unused */
{0, 0, 0, 0, 0 }, /* 8 (AD[18]): Unused */
{0, 0, 0, 0, 0 }, /* 9 (AD[19]): Unused */
{0, 0, 0, 0, 0 }, /* 10 (AD[20]): Unused */
{0, 0, 0, 0, 0 }, /* 11 (AD[21]): Unused */
{0, 0, 0, 0, 0 }, /* 12 (AD[22]): Unused */
{0, 0, 0, 0, 0 }, /* 13 (AD[23]): Unused */
{0, 0, 0, 0, 0 }, /* 14 (AD[24]): Unused */
{0, 0, 0, 0, 0 }, /* 15 (AD[25]): Unused */
{0, 0, 0, 0, 0 }, /* 16 (AD[26]): Unused */
{0, 0, 0, 0, 0 }, /* 17 (AD[27]): Unused */
{0, IRQ4, IRQ4, 0, 0 }, /* 18 (AD[28]): slot 0 */
{0, 0, 0, 0, 0 }, /* 19 (AD[29]): Unused */
{0, IRQ5, IRQ5, 0, 0 }, /* 20 (AD[30]): slot 1 */
{0, IRQ6, IRQ6, 0, 0 } /* 21 (AD[31]): slot 2 */
};
#elif defined(CONFIG_PMC_MSP7120_EVAL)
/* MSP7120 Eval Board IRQ wiring to PCI slots */
static char irq_tab[][5] __initdata = {
/* INTA INTB INTC INTD */
{0, 0, 0, 0, 0 }, /* (AD[0]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[1]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[2]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[3]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[4]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[5]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[6]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[7]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[8]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[9]): Unused */
{0, 0, 0, 0, 0 }, /* 0 (AD[10]): Unused */
{0, 0, 0, 0, 0 }, /* 1 (AD[11]): Unused */
{0, 0, 0, 0, 0 }, /* 2 (AD[12]): Unused */
{0, 0, 0, 0, 0 }, /* 3 (AD[13]): Unused */
{0, 0, 0, 0, 0 }, /* 4 (AD[14]): Unused */
{0, 0, 0, 0, 0 }, /* 5 (AD[15]): Unused */
{0, IRQ6, IRQ6, 0, 0 }, /* 6 (AD[16]): slot 3 (mini) */
{0, IRQ5, IRQ5, 0, 0 }, /* 7 (AD[17]): slot 2 (mini) */
{0, IRQ4, IRQ4, IRQ4, IRQ4}, /* 8 (AD[18]): slot 0 (PCI) */
{0, IRQ5, IRQ5, IRQ5, IRQ5}, /* 9 (AD[19]): slot 1 (PCI) */
{0, 0, 0, 0, 0 }, /* 10 (AD[20]): Unused */
{0, 0, 0, 0, 0 }, /* 11 (AD[21]): Unused */
{0, 0, 0, 0, 0 }, /* 12 (AD[22]): Unused */
{0, 0, 0, 0, 0 }, /* 13 (AD[23]): Unused */
{0, 0, 0, 0, 0 }, /* 14 (AD[24]): Unused */
{0, 0, 0, 0, 0 }, /* 15 (AD[25]): Unused */
{0, 0, 0, 0, 0 }, /* 16 (AD[26]): Unused */
{0, 0, 0, 0, 0 }, /* 17 (AD[27]): Unused */
{0, 0, 0, 0, 0 }, /* 18 (AD[28]): Unused */
{0, 0, 0, 0, 0 }, /* 19 (AD[29]): Unused */
{0, 0, 0, 0, 0 }, /* 20 (AD[30]): Unused */
{0, 0, 0, 0, 0 } /* 21 (AD[31]): Unused */
};
#else
/* Unknown board -- don't assign any IRQs */
static char irq_tab[][5] __initdata = {
/* INTA INTB INTC INTD */
{0, 0, 0, 0, 0 }, /* (AD[0]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[1]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[2]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[3]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[4]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[5]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[6]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[7]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[8]): Unused */
{0, 0, 0, 0, 0 }, /* (AD[9]): Unused */
{0, 0, 0, 0, 0 }, /* 0 (AD[10]): Unused */
{0, 0, 0, 0, 0 }, /* 1 (AD[11]): Unused */
{0, 0, 0, 0, 0 }, /* 2 (AD[12]): Unused */
{0, 0, 0, 0, 0 }, /* 3 (AD[13]): Unused */
{0, 0, 0, 0, 0 }, /* 4 (AD[14]): Unused */
{0, 0, 0, 0, 0 }, /* 5 (AD[15]): Unused */
{0, 0, 0, 0, 0 }, /* 6 (AD[16]): Unused */
{0, 0, 0, 0, 0 }, /* 7 (AD[17]): Unused */
{0, 0, 0, 0, 0 }, /* 8 (AD[18]): Unused */
{0, 0, 0, 0, 0 }, /* 9 (AD[19]): Unused */
{0, 0, 0, 0, 0 }, /* 10 (AD[20]): Unused */
{0, 0, 0, 0, 0 }, /* 11 (AD[21]): Unused */
{0, 0, 0, 0, 0 }, /* 12 (AD[22]): Unused */
{0, 0, 0, 0, 0 }, /* 13 (AD[23]): Unused */
{0, 0, 0, 0, 0 }, /* 14 (AD[24]): Unused */
{0, 0, 0, 0, 0 }, /* 15 (AD[25]): Unused */
{0, 0, 0, 0, 0 }, /* 16 (AD[26]): Unused */
{0, 0, 0, 0, 0 }, /* 17 (AD[27]): Unused */
{0, 0, 0, 0, 0 }, /* 18 (AD[28]): Unused */
{0, 0, 0, 0, 0 }, /* 19 (AD[29]): Unused */
{0, 0, 0, 0, 0 }, /* 20 (AD[30]): Unused */
{0, 0, 0, 0, 0 } /* 21 (AD[31]): Unused */
};
#endif
/*****************************************************************************
*
* FUNCTION: pcibios_plat_dev_init
* _________________________________________________________________________
*
* DESCRIPTION: Perform platform specific device initialization at
* pci_enable_device() time.
* None are needed for the MSP7120 PCI Controller.
*
* INPUTS: dev - structure describing the PCI device
*
* OUTPUTS: none
*
* RETURNS: PCIBIOS_SUCCESSFUL
*
****************************************************************************/
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: pcibios_map_irq
* _________________________________________________________________________
*
* DESCRIPTION: Perform board supplied PCI IRQ mapping routine.
*
* INPUTS: dev - unused
* slot - PCI slot. Identified by which bit of the AD[] bus
* drives the IDSEL line. AD[10] is 0, AD[31] is
* slot 21.
* pin - numbered using the scheme of the PCI_INTERRUPT_PIN
* field of the config header.
*
* OUTPUTS: none
*
* RETURNS: IRQ number
*
****************************************************************************/
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
#if !defined(CONFIG_PMC_MSP7120_GW) && !defined(CONFIG_PMC_MSP7120_EVAL)
printk(KERN_WARNING "PCI: unknown board, no PCI IRQs assigned.\n");
#endif
printk(KERN_WARNING "PCI: irq_tab returned %d for slot=%d pin=%d\n",
irq_tab[slot][pin], slot, pin);
return irq_tab[slot][pin];
}
#endif /* CONFIG_PCI */

View file

@ -0,0 +1,73 @@
/*
*
* 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 <asm/txx9/pci.h>
#include <asm/txx9/rbtx4927.h>
int __init rbtx4927_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
unsigned char irq = pin;
/* IRQ rotation */
irq--; /* 0-3 */
if (slot == TX4927_PCIC_IDSEL_AD_TO_SLOT(23)) {
/* PCI CardSlot (IDSEL=A23) */
/* PCIA => PCIA */
irq = (irq + 0 + slot) % 4;
} else {
/* PCI Backplane */
if (txx9_pci_option & TXX9_PCI_OPT_PICMG)
irq = (irq + 33 - slot) % 4;
else
irq = (irq + 3 + slot) % 4;
}
irq++; /* 1-4 */
switch (irq) {
case 1:
irq = RBTX4927_IRQ_IOC_PCIA;
break;
case 2:
irq = RBTX4927_IRQ_IOC_PCIB;
break;
case 3:
irq = RBTX4927_IRQ_IOC_PCIC;
break;
case 4:
irq = RBTX4927_IRQ_IOC_PCID;
break;
}
return irq;
}

View file

@ -0,0 +1,53 @@
/*
* Toshiba rbtx4938 pci routines
* Copyright (C) 2000-2001 Toshiba Corporation
*
* 2003-2005 (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.
*
* Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
*/
#include <linux/types.h>
#include <asm/txx9/pci.h>
#include <asm/txx9/rbtx4938.h>
int __init rbtx4938_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq = tx4938_pcic1_map_irq(dev, slot);
if (irq >= 0)
return irq;
irq = pin;
/* IRQ rotation */
irq--; /* 0-3 */
if (slot == TX4927_PCIC_IDSEL_AD_TO_SLOT(23)) {
/* PCI CardSlot (IDSEL=A23) */
/* PCIA => PCIA (IDSEL=A23) */
irq = (irq + 0 + slot) % 4;
} else {
/* PCI Backplane */
if (txx9_pci_option & TXX9_PCI_OPT_PICMG)
irq = (irq + 33 - slot) % 4;
else
irq = (irq + 3 + slot) % 4;
}
irq++; /* 1-4 */
switch (irq) {
case 1:
irq = RBTX4938_IRQ_IOC_PCIA;
break;
case 2:
irq = RBTX4938_IRQ_IOC_PCIB;
break;
case 3:
irq = RBTX4938_IRQ_IOC_PCIC;
break;
case 4:
irq = RBTX4938_IRQ_IOC_PCID;
break;
}
return irq;
}

View file

@ -0,0 +1,69 @@
/*
* Copyright 2001 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* stevel@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 <asm/mach-rc32434/rc32434.h>
#include <asm/mach-rc32434/irq.h>
static int irq_map[2][12] = {
{0, 0, 2, 3, 2, 3, 0, 0, 0, 0, 0, 1},
{0, 0, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}
};
int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq = 0;
if (dev->bus->number < 2 && PCI_SLOT(dev->devfn) < 12)
irq = irq_map[dev->bus->number][PCI_SLOT(dev->devfn)];
return irq + GROUP4_IRQ_BASE + 4;
}
static void rc32434_pci_early_fixup(struct pci_dev *dev)
{
if (PCI_SLOT(dev->devfn) == 6 && dev->bus->number == 0) {
/* disable prefetched memory range */
pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0);
pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0x10);
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 4);
}
}
/*
* The fixup applies to both the IDT and VIA devices present on the board
*/
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, rc32434_pci_early_fixup);
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2004, 2006 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/pci.h>
/*
* Set the BCM1250, etc. PCI host bridge's TRDY timeout
* to the finite max.
*/
static void quirk_sb1250_pci(struct pci_dev *dev)
{
pci_write_config_byte(dev, 0x40, 0xff);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIBYTE, PCI_DEVICE_ID_BCM1250_PCI,
quirk_sb1250_pci);
/*
* The BCM1250, etc. PCI/HT bridge reports as a host bridge.
*/
static void 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);
/*
* Set the SP1011 HT/PCI bridge's TRDY timeout to the finite max.
*/
static void quirk_sp1011(struct pci_dev *dev)
{
pci_write_config_byte(dev, 0x64, 0xff);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIPACKETS, PCI_DEVICE_ID_SP1011,
quirk_sp1011);

170
arch/mips/pci/fixup-sni.c Normal file
View file

@ -0,0 +1,170 @@
/*
* 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>
#include <irq.h>
/*
* PCIMT 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 char irq_tab_rm300e[5][5] __initdata = {
/* INTA INTB INTC INTD */
{ 0, 0, 0, 0, 0 }, /* HOST bridge */
{ SCSI, SCSI, SCSI, SCSI, SCSI }, /* SCSI */
{ 0, INTC, INTD, INTA, INTB }, /* Bridge/i960 */
{ 0, INTD, INTA, INTB, INTC }, /* Slot 1 */
{ 0, INTA, INTB, INTC, INTD }, /* Slot 2 */
};
#undef SCSI
#undef ETH
#undef INTA
#undef INTB
#undef INTC
#undef INTD
/*
* PCIT Shortcuts ...
*/
#define SCSI0 PCIT_IRQ_SCSI0
#define SCSI1 PCIT_IRQ_SCSI1
#define ETH PCIT_IRQ_ETHERNET
#define INTA PCIT_IRQ_INTA
#define INTB PCIT_IRQ_INTB
#define INTC PCIT_IRQ_INTC
#define INTD PCIT_IRQ_INTD
static char irq_tab_pcit[13][5] __initdata = {
/* INTA INTB INTC INTD */
{ 0, 0, 0, 0, 0 }, /* HOST bridge */
{ SCSI0, SCSI0, SCSI0, SCSI0, SCSI0 }, /* SCSI */
{ SCSI1, SCSI1, SCSI1, SCSI1, SCSI1 }, /* SCSI */
{ ETH, ETH, ETH, ETH, ETH }, /* Ethernet */
{ 0, INTA, INTB, INTC, INTD }, /* PCI-PCI bridge */
{ 0, 0, 0, 0, 0 }, /* Unused */
{ 0, 0, 0, 0, 0 }, /* Unused */
{ 0, 0, 0, 0, 0 }, /* Unused */
{ 0, INTA, INTB, INTC, INTD }, /* Slot 1 */
{ 0, INTB, INTC, INTD, INTA }, /* Slot 2 */
{ 0, INTC, INTD, INTA, INTB }, /* Slot 3 */
{ 0, INTD, INTA, INTB, INTC }, /* Slot 4 */
{ 0, INTA, INTB, INTC, INTD }, /* Slot 5 */
};
static char irq_tab_pcit_cplus[13][5] __initdata = {
/* INTA INTB INTC INTD */
{ 0, 0, 0, 0, 0 }, /* HOST bridge */
{ 0, INTB, INTC, INTD, INTA }, /* PCI Slot 9 */
{ 0, 0, 0, 0, 0 }, /* PCI-EISA */
{ 0, 0, 0, 0, 0 }, /* Unused */
{ 0, INTA, INTB, INTC, INTD }, /* PCI-PCI bridge */
{ 0, INTB, INTC, INTD, INTA }, /* fixup */
};
static inline int is_rm300_revd(void)
{
unsigned char csmsr = *(volatile unsigned char *)PCIMT_CSMSR;
return (csmsr & 0xa0) == 0x20;
}
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
switch (sni_brd_type) {
case SNI_BRD_PCI_TOWER_CPLUS:
if (slot == 4) {
/*
* SNI messed up interrupt wiring for onboard
* PCI bus 1; we need to fix this up here
*/
while (dev && dev->bus->number != 1)
dev = dev->bus->self;
if (dev && dev->devfn >= PCI_DEVFN(4, 0))
slot = 5;
}
return irq_tab_pcit_cplus[slot][pin];
case SNI_BRD_PCI_TOWER:
return irq_tab_pcit[slot][pin];
case SNI_BRD_PCI_MTOWER:
if (is_rm300_revd())
return irq_tab_rm300d[slot][pin];
/* fall through */
case SNI_BRD_PCI_DESKTOP:
return irq_tab_rm200[slot][pin];
case SNI_BRD_PCI_MTOWER_CPLUS:
return irq_tab_rm300e[slot][pin];
}
return 0;
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}

View file

@ -0,0 +1,51 @@
/*
* fixup-tb0219.c, The TANBAC TB0219 specific PCI fixups.
*
* Copyright (C) 2003 Megasolution Inc. <matsu@megasolution.jp>
* Copyright (C) 2004-2005 Yoichi Yuasa <yuasa@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
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/vr41xx/tb0219.h>
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq = -1;
switch (slot) {
case 12:
irq = TB0219_PCI_SLOT1_IRQ;
break;
case 13:
irq = TB0219_PCI_SLOT2_IRQ;
break;
case 14:
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;
}

View file

@ -0,0 +1,86 @@
/*
* fixup-tb0226.c, The TANBAC TB0226 specific PCI fixups.
*
* Copyright (C) 2002-2005 Yoichi Yuasa <yuasa@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
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/vr41xx/giu.h>
#include <asm/vr41xx/tb0226.h>
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq = -1;
switch (slot) {
case 12:
vr41xx_set_irq_trigger(GD82559_1_PIN,
IRQ_TRIGGER_LEVEL,
IRQ_SIGNAL_THROUGH);
vr41xx_set_irq_level(GD82559_1_PIN, IRQ_LEVEL_LOW);
irq = GD82559_1_IRQ;
break;
case 13:
vr41xx_set_irq_trigger(GD82559_2_PIN,
IRQ_TRIGGER_LEVEL,
IRQ_SIGNAL_THROUGH);
vr41xx_set_irq_level(GD82559_2_PIN, IRQ_LEVEL_LOW);
irq = GD82559_2_IRQ;
break;
case 14:
switch (pin) {
case 1:
vr41xx_set_irq_trigger(UPD720100_INTA_PIN,
IRQ_TRIGGER_LEVEL,
IRQ_SIGNAL_THROUGH);
vr41xx_set_irq_level(UPD720100_INTA_PIN,
IRQ_LEVEL_LOW);
irq = UPD720100_INTA_IRQ;
break;
case 2:
vr41xx_set_irq_trigger(UPD720100_INTB_PIN,
IRQ_TRIGGER_LEVEL,
IRQ_SIGNAL_THROUGH);
vr41xx_set_irq_level(UPD720100_INTB_PIN,
IRQ_LEVEL_LOW);
irq = UPD720100_INTB_IRQ;
break;
case 3:
vr41xx_set_irq_trigger(UPD720100_INTC_PIN,
IRQ_TRIGGER_LEVEL,
IRQ_SIGNAL_THROUGH);
vr41xx_set_irq_level(UPD720100_INTC_PIN,
IRQ_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;
}

View file

@ -0,0 +1,65 @@
/*
* fixup-tb0287.c, The TANBAC TB0287 specific PCI fixups.
*
* Copyright (C) 2005 Yoichi Yuasa <yuasa@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
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <asm/vr41xx/tb0287.h>
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
unsigned char bus;
int irq = -1;
bus = dev->bus->number;
if (bus == 0) {
switch (slot) {
case 16:
irq = TB0287_SM501_IRQ;
break;
case 17:
irq = TB0287_SIL680A_IRQ;
break;
default:
break;
}
} else if (bus == 1) {
switch (PCI_SLOT(dev->devfn)) {
case 0:
irq = TB0287_PCI_SLOT_IRQ;
break;
case 2:
case 3:
irq = TB0287_RTL8110_IRQ;
break;
default:
break;
}
} else if (bus > 1) {
irq = TB0287_PCI_SLOT_IRQ;
}
return irq;
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}

436
arch/mips/pci/msi-octeon.c Normal file
View file

@ -0,0 +1,436 @@
/*
* 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) 2005-2009, 2010 Cavium Networks
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/msi.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-npi-defs.h>
#include <asm/octeon/cvmx-pci-defs.h>
#include <asm/octeon/cvmx-npei-defs.h>
#include <asm/octeon/cvmx-sli-defs.h>
#include <asm/octeon/cvmx-pexp-defs.h>
#include <asm/octeon/pci-octeon.h>
/*
* Each bit in msi_free_irq_bitmask represents a MSI interrupt that is
* in use.
*/
static u64 msi_free_irq_bitmask[4];
/*
* Each bit in msi_multiple_irq_bitmask tells that the device using
* this bit in msi_free_irq_bitmask is also using the next bit. This
* is used so we can disable all of the MSI interrupts when a device
* uses multiple.
*/
static u64 msi_multiple_irq_bitmask[4];
/*
* This lock controls updates to msi_free_irq_bitmask and
* msi_multiple_irq_bitmask.
*/
static DEFINE_SPINLOCK(msi_free_irq_bitmask_lock);
/*
* Number of MSI IRQs used. This variable is set up in
* the module init time.
*/
static int msi_irq_size;
/**
* Called when a driver request MSI interrupts instead of the
* legacy INT A-D. This routine will allocate multiple interrupts
* for MSI devices that support them. A device can override this by
* programming the MSI control bits [6:4] before calling
* pci_enable_msi().
*
* @dev: Device requesting MSI interrupts
* @desc: MSI descriptor
*
* Returns 0 on success.
*/
int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
{
struct msi_msg msg;
u16 control;
int configured_private_bits;
int request_private_bits;
int irq = 0;
int irq_step;
u64 search_mask;
int index;
/*
* Read the MSI config to figure out how many IRQs this device
* wants. Most devices only want 1, which will give
* configured_private_bits and request_private_bits equal 0.
*/
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
/*
* If the number of private bits has been configured then use
* that value instead of the requested number. This gives the
* driver the chance to override the number of interrupts
* before calling pci_enable_msi().
*/
configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4;
if (configured_private_bits == 0) {
/* Nothing is configured, so use the hardware requested size */
request_private_bits = (control & PCI_MSI_FLAGS_QMASK) >> 1;
} else {
/*
* Use the number of configured bits, assuming the
* driver wanted to override the hardware request
* value.
*/
request_private_bits = configured_private_bits;
}
/*
* The PCI 2.3 spec mandates that there are at most 32
* interrupts. If this device asks for more, only give it one.
*/
if (request_private_bits > 5)
request_private_bits = 0;
try_only_one:
/*
* The IRQs have to be aligned on a power of two based on the
* number being requested.
*/
irq_step = 1 << request_private_bits;
/* Mask with one bit for each IRQ */
search_mask = (1 << irq_step) - 1;
/*
* We're going to search msi_free_irq_bitmask_lock for zero
* bits. This represents an MSI interrupt number that isn't in
* use.
*/
spin_lock(&msi_free_irq_bitmask_lock);
for (index = 0; index < msi_irq_size/64; index++) {
for (irq = 0; irq < 64; irq += irq_step) {
if ((msi_free_irq_bitmask[index] & (search_mask << irq)) == 0) {
msi_free_irq_bitmask[index] |= search_mask << irq;
msi_multiple_irq_bitmask[index] |= (search_mask >> 1) << irq;
goto msi_irq_allocated;
}
}
}
msi_irq_allocated:
spin_unlock(&msi_free_irq_bitmask_lock);
/* Make sure the search for available interrupts didn't fail */
if (irq >= 64) {
if (request_private_bits) {
pr_err("arch_setup_msi_irq: Unable to find %d free interrupts, trying just one",
1 << request_private_bits);
request_private_bits = 0;
goto try_only_one;
} else
panic("arch_setup_msi_irq: Unable to find a free MSI interrupt");
}
/* MSI interrupts start at logical IRQ OCTEON_IRQ_MSI_BIT0 */
irq += index*64;
irq += OCTEON_IRQ_MSI_BIT0;
switch (octeon_dma_bar_type) {
case OCTEON_DMA_BAR_TYPE_SMALL:
/* When not using big bar, Bar 0 is based at 128MB */
msg.address_lo =
((128ul << 20) + CVMX_PCI_MSI_RCV) & 0xffffffff;
msg.address_hi = ((128ul << 20) + CVMX_PCI_MSI_RCV) >> 32;
break;
case OCTEON_DMA_BAR_TYPE_BIG:
/* When using big bar, Bar 0 is based at 0 */
msg.address_lo = (0 + CVMX_PCI_MSI_RCV) & 0xffffffff;
msg.address_hi = (0 + CVMX_PCI_MSI_RCV) >> 32;
break;
case OCTEON_DMA_BAR_TYPE_PCIE:
/* When using PCIe, Bar 0 is based at 0 */
/* FIXME CVMX_NPEI_MSI_RCV* other than 0? */
msg.address_lo = (0 + CVMX_NPEI_PCIE_MSI_RCV) & 0xffffffff;
msg.address_hi = (0 + CVMX_NPEI_PCIE_MSI_RCV) >> 32;
break;
case OCTEON_DMA_BAR_TYPE_PCIE2:
/* When using PCIe2, Bar 0 is based at 0 */
msg.address_lo = (0 + CVMX_SLI_PCIE_MSI_RCV) & 0xffffffff;
msg.address_hi = (0 + CVMX_SLI_PCIE_MSI_RCV) >> 32;
break;
default:
panic("arch_setup_msi_irq: Invalid octeon_dma_bar_type");
}
msg.data = irq - OCTEON_IRQ_MSI_BIT0;
/* Update the number of IRQs the device has available to it */
control &= ~PCI_MSI_FLAGS_QSIZE;
control |= request_private_bits << 4;
pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
irq_set_msi_desc(irq, desc);
write_msi_msg(irq, &msg);
return 0;
}
int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
{
struct msi_desc *entry;
int ret;
/*
* MSI-X is not supported.
*/
if (type == PCI_CAP_ID_MSIX)
return -EINVAL;
/*
* If an architecture wants to support multiple MSI, it needs to
* override arch_setup_msi_irqs()
*/
if (type == PCI_CAP_ID_MSI && nvec > 1)
return 1;
list_for_each_entry(entry, &dev->msi_list, list) {
ret = arch_setup_msi_irq(dev, entry);
if (ret < 0)
return ret;
if (ret > 0)
return -ENOSPC;
}
return 0;
}
/**
* Called when a device no longer needs its MSI interrupts. All
* MSI interrupts for the device are freed.
*
* @irq: The devices first irq number. There may be multple in sequence.
*/
void arch_teardown_msi_irq(unsigned int irq)
{
int number_irqs;
u64 bitmask;
int index = 0;
int irq0;
if ((irq < OCTEON_IRQ_MSI_BIT0)
|| (irq > msi_irq_size + OCTEON_IRQ_MSI_BIT0))
panic("arch_teardown_msi_irq: Attempted to teardown illegal "
"MSI interrupt (%d)", irq);
irq -= OCTEON_IRQ_MSI_BIT0;
index = irq / 64;
irq0 = irq % 64;
/*
* Count the number of IRQs we need to free by looking at the
* msi_multiple_irq_bitmask. Each bit set means that the next
* IRQ is also owned by this device.
*/
number_irqs = 0;
while ((irq0 + number_irqs < 64) &&
(msi_multiple_irq_bitmask[index]
& (1ull << (irq0 + number_irqs))))
number_irqs++;
number_irqs++;
/* Mask with one bit for each IRQ */
bitmask = (1 << number_irqs) - 1;
/* Shift the mask to the correct bit location */
bitmask <<= irq0;
if ((msi_free_irq_bitmask[index] & bitmask) != bitmask)
panic("arch_teardown_msi_irq: Attempted to teardown MSI "
"interrupt (%d) not in use", irq);
/* Checks are done, update the in use bitmask */
spin_lock(&msi_free_irq_bitmask_lock);
msi_free_irq_bitmask[index] &= ~bitmask;
msi_multiple_irq_bitmask[index] &= ~bitmask;
spin_unlock(&msi_free_irq_bitmask_lock);
}
static DEFINE_RAW_SPINLOCK(octeon_irq_msi_lock);
static u64 msi_rcv_reg[4];
static u64 mis_ena_reg[4];
static void octeon_irq_msi_enable_pcie(struct irq_data *data)
{
u64 en;
unsigned long flags;
int msi_number = data->irq - OCTEON_IRQ_MSI_BIT0;
int irq_index = msi_number >> 6;
int irq_bit = msi_number & 0x3f;
raw_spin_lock_irqsave(&octeon_irq_msi_lock, flags);
en = cvmx_read_csr(mis_ena_reg[irq_index]);
en |= 1ull << irq_bit;
cvmx_write_csr(mis_ena_reg[irq_index], en);
cvmx_read_csr(mis_ena_reg[irq_index]);
raw_spin_unlock_irqrestore(&octeon_irq_msi_lock, flags);
}
static void octeon_irq_msi_disable_pcie(struct irq_data *data)
{
u64 en;
unsigned long flags;
int msi_number = data->irq - OCTEON_IRQ_MSI_BIT0;
int irq_index = msi_number >> 6;
int irq_bit = msi_number & 0x3f;
raw_spin_lock_irqsave(&octeon_irq_msi_lock, flags);
en = cvmx_read_csr(mis_ena_reg[irq_index]);
en &= ~(1ull << irq_bit);
cvmx_write_csr(mis_ena_reg[irq_index], en);
cvmx_read_csr(mis_ena_reg[irq_index]);
raw_spin_unlock_irqrestore(&octeon_irq_msi_lock, flags);
}
static struct irq_chip octeon_irq_chip_msi_pcie = {
.name = "MSI",
.irq_enable = octeon_irq_msi_enable_pcie,
.irq_disable = octeon_irq_msi_disable_pcie,
};
static void octeon_irq_msi_enable_pci(struct irq_data *data)
{
/*
* Octeon PCI doesn't have the ability to mask/unmask MSI
* interrupts individually. Instead of masking/unmasking them
* in groups of 16, we simple assume MSI devices are well
* behaved. MSI interrupts are always enable and the ACK is
* assumed to be enough
*/
}
static void octeon_irq_msi_disable_pci(struct irq_data *data)
{
/* See comment in enable */
}
static struct irq_chip octeon_irq_chip_msi_pci = {
.name = "MSI",
.irq_enable = octeon_irq_msi_enable_pci,
.irq_disable = octeon_irq_msi_disable_pci,
};
/*
* Called by the interrupt handling code when an MSI interrupt
* occurs.
*/
static irqreturn_t __octeon_msi_do_interrupt(int index, u64 msi_bits)
{
int irq;
int bit;
bit = fls64(msi_bits);
if (bit) {
bit--;
/* Acknowledge it first. */
cvmx_write_csr(msi_rcv_reg[index], 1ull << bit);
irq = bit + OCTEON_IRQ_MSI_BIT0 + 64 * index;
do_IRQ(irq);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
#define OCTEON_MSI_INT_HANDLER_X(x) \
static irqreturn_t octeon_msi_interrupt##x(int cpl, void *dev_id) \
{ \
u64 msi_bits = cvmx_read_csr(msi_rcv_reg[(x)]); \
return __octeon_msi_do_interrupt((x), msi_bits); \
}
/*
* Create octeon_msi_interrupt{0-3} function body
*/
OCTEON_MSI_INT_HANDLER_X(0);
OCTEON_MSI_INT_HANDLER_X(1);
OCTEON_MSI_INT_HANDLER_X(2);
OCTEON_MSI_INT_HANDLER_X(3);
/*
* Initializes the MSI interrupt handling code
*/
int __init octeon_msi_initialize(void)
{
int irq;
struct irq_chip *msi;
if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_PCIE) {
msi_rcv_reg[0] = CVMX_PEXP_NPEI_MSI_RCV0;
msi_rcv_reg[1] = CVMX_PEXP_NPEI_MSI_RCV1;
msi_rcv_reg[2] = CVMX_PEXP_NPEI_MSI_RCV2;
msi_rcv_reg[3] = CVMX_PEXP_NPEI_MSI_RCV3;
mis_ena_reg[0] = CVMX_PEXP_NPEI_MSI_ENB0;
mis_ena_reg[1] = CVMX_PEXP_NPEI_MSI_ENB1;
mis_ena_reg[2] = CVMX_PEXP_NPEI_MSI_ENB2;
mis_ena_reg[3] = CVMX_PEXP_NPEI_MSI_ENB3;
msi = &octeon_irq_chip_msi_pcie;
} else {
msi_rcv_reg[0] = CVMX_NPI_NPI_MSI_RCV;
#define INVALID_GENERATE_ADE 0x8700000000000000ULL;
msi_rcv_reg[1] = INVALID_GENERATE_ADE;
msi_rcv_reg[2] = INVALID_GENERATE_ADE;
msi_rcv_reg[3] = INVALID_GENERATE_ADE;
mis_ena_reg[0] = INVALID_GENERATE_ADE;
mis_ena_reg[1] = INVALID_GENERATE_ADE;
mis_ena_reg[2] = INVALID_GENERATE_ADE;
mis_ena_reg[3] = INVALID_GENERATE_ADE;
msi = &octeon_irq_chip_msi_pci;
}
for (irq = OCTEON_IRQ_MSI_BIT0; irq <= OCTEON_IRQ_MSI_LAST; irq++)
irq_set_chip_and_handler(irq, msi, handle_simple_irq);
if (octeon_has_feature(OCTEON_FEATURE_PCIE)) {
if (request_irq(OCTEON_IRQ_PCI_MSI0, octeon_msi_interrupt0,
0, "MSI[0:63]", octeon_msi_interrupt0))
panic("request_irq(OCTEON_IRQ_PCI_MSI0) failed");
if (request_irq(OCTEON_IRQ_PCI_MSI1, octeon_msi_interrupt1,
0, "MSI[64:127]", octeon_msi_interrupt1))
panic("request_irq(OCTEON_IRQ_PCI_MSI1) failed");
if (request_irq(OCTEON_IRQ_PCI_MSI2, octeon_msi_interrupt2,
0, "MSI[127:191]", octeon_msi_interrupt2))
panic("request_irq(OCTEON_IRQ_PCI_MSI2) failed");
if (request_irq(OCTEON_IRQ_PCI_MSI3, octeon_msi_interrupt3,
0, "MSI[192:255]", octeon_msi_interrupt3))
panic("request_irq(OCTEON_IRQ_PCI_MSI3) failed");
msi_irq_size = 256;
} else if (octeon_is_pci_host()) {
if (request_irq(OCTEON_IRQ_PCI_MSI0, octeon_msi_interrupt0,
0, "MSI[0:15]", octeon_msi_interrupt0))
panic("request_irq(OCTEON_IRQ_PCI_MSI0) failed");
if (request_irq(OCTEON_IRQ_PCI_MSI1, octeon_msi_interrupt0,
0, "MSI[16:31]", octeon_msi_interrupt0))
panic("request_irq(OCTEON_IRQ_PCI_MSI1) failed");
if (request_irq(OCTEON_IRQ_PCI_MSI2, octeon_msi_interrupt0,
0, "MSI[32:47]", octeon_msi_interrupt0))
panic("request_irq(OCTEON_IRQ_PCI_MSI2) failed");
if (request_irq(OCTEON_IRQ_PCI_MSI3, octeon_msi_interrupt0,
0, "MSI[48:63]", octeon_msi_interrupt0))
panic("request_irq(OCTEON_IRQ_PCI_MSI3) failed");
msi_irq_size = 64;
}
return 0;
}
subsys_initcall(octeon_msi_initialize);

570
arch/mips/pci/msi-xlp.c Normal file
View file

@ -0,0 +1,570 @@
/*
* Copyright (c) 2003-2012 Broadcom Corporation
* All Rights Reserved
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the Broadcom
* license below:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY BROADCOM ``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 BROADCOM OR CONTRIBUTORS 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.
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/msi.h>
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/console.h>
#include <asm/io.h>
#include <asm/netlogic/interrupt.h>
#include <asm/netlogic/haldefs.h>
#include <asm/netlogic/common.h>
#include <asm/netlogic/mips-extns.h>
#include <asm/netlogic/xlp-hal/iomap.h>
#include <asm/netlogic/xlp-hal/xlp.h>
#include <asm/netlogic/xlp-hal/pic.h>
#include <asm/netlogic/xlp-hal/pcibus.h>
#include <asm/netlogic/xlp-hal/bridge.h>
#define XLP_MSIVEC_PER_LINK 32
#define XLP_MSIXVEC_TOTAL (cpu_is_xlp9xx() ? 128 : 32)
#define XLP_MSIXVEC_PER_LINK (cpu_is_xlp9xx() ? 32 : 8)
/* 128 MSI irqs per node, mapped starting at NLM_MSI_VEC_BASE */
static inline int nlm_link_msiirq(int link, int msivec)
{
return NLM_MSI_VEC_BASE + link * XLP_MSIVEC_PER_LINK + msivec;
}
/* get the link MSI vector from irq number */
static inline int nlm_irq_msivec(int irq)
{
return (irq - NLM_MSI_VEC_BASE) % XLP_MSIVEC_PER_LINK;
}
/* get the link from the irq number */
static inline int nlm_irq_msilink(int irq)
{
int total_msivec = XLP_MSIVEC_PER_LINK * PCIE_NLINKS;
return ((irq - NLM_MSI_VEC_BASE) % total_msivec) /
XLP_MSIVEC_PER_LINK;
}
/*
* For XLP 8xx/4xx/3xx/2xx, only 32 MSI-X vectors are possible because
* there are only 32 PIC interrupts for MSI. We split them statically
* and use 8 MSI-X vectors per link - this keeps the allocation and
* lookup simple.
* On XLP 9xx, there are 32 vectors per link, and the interrupts are
* not routed thru PIC, so we can use all 128 MSI-X vectors.
*/
static inline int nlm_link_msixirq(int link, int bit)
{
return NLM_MSIX_VEC_BASE + link * XLP_MSIXVEC_PER_LINK + bit;
}
/* get the link MSI vector from irq number */
static inline int nlm_irq_msixvec(int irq)
{
return (irq - NLM_MSIX_VEC_BASE) % XLP_MSIXVEC_TOTAL;
}
/* get the link from MSIX vec */
static inline int nlm_irq_msixlink(int msixvec)
{
return msixvec / XLP_MSIXVEC_PER_LINK;
}
/*
* Per link MSI and MSI-X information, set as IRQ handler data for
* MSI and MSI-X interrupts.
*/
struct xlp_msi_data {
struct nlm_soc_info *node;
uint64_t lnkbase;
uint32_t msi_enabled_mask;
uint32_t msi_alloc_mask;
uint32_t msix_alloc_mask;
spinlock_t msi_lock;
};
/*
* MSI Chip definitions
*
* On XLP, there is a PIC interrupt associated with each PCIe link on the
* chip (which appears as a PCI bridge to us). This gives us 32 MSI irqa
* per link and 128 overall.
*
* When a device connected to the link raises a MSI interrupt, we get a
* link interrupt and we then have to look at PCIE_MSI_STATUS register at
* the bridge to map it to the IRQ
*/
static void xlp_msi_enable(struct irq_data *d)
{
struct xlp_msi_data *md = irq_data_get_irq_handler_data(d);
unsigned long flags;
int vec;
vec = nlm_irq_msivec(d->irq);
spin_lock_irqsave(&md->msi_lock, flags);
md->msi_enabled_mask |= 1u << vec;
if (cpu_is_xlp9xx())
nlm_write_reg(md->lnkbase, PCIE_9XX_MSI_EN,
md->msi_enabled_mask);
else
nlm_write_reg(md->lnkbase, PCIE_MSI_EN, md->msi_enabled_mask);
spin_unlock_irqrestore(&md->msi_lock, flags);
}
static void xlp_msi_disable(struct irq_data *d)
{
struct xlp_msi_data *md = irq_data_get_irq_handler_data(d);
unsigned long flags;
int vec;
vec = nlm_irq_msivec(d->irq);
spin_lock_irqsave(&md->msi_lock, flags);
md->msi_enabled_mask &= ~(1u << vec);
if (cpu_is_xlp9xx())
nlm_write_reg(md->lnkbase, PCIE_9XX_MSI_EN,
md->msi_enabled_mask);
else
nlm_write_reg(md->lnkbase, PCIE_MSI_EN, md->msi_enabled_mask);
spin_unlock_irqrestore(&md->msi_lock, flags);
}
static void xlp_msi_mask_ack(struct irq_data *d)
{
struct xlp_msi_data *md = irq_data_get_irq_handler_data(d);
int link, vec;
link = nlm_irq_msilink(d->irq);
vec = nlm_irq_msivec(d->irq);
xlp_msi_disable(d);
/* Ack MSI on bridge */
if (cpu_is_xlp9xx())
nlm_write_reg(md->lnkbase, PCIE_9XX_MSI_STATUS, 1u << vec);
else
nlm_write_reg(md->lnkbase, PCIE_MSI_STATUS, 1u << vec);
/* Ack at eirr and PIC */
ack_c0_eirr(PIC_PCIE_LINK_MSI_IRQ(link));
if (cpu_is_xlp9xx())
nlm_pic_ack(md->node->picbase,
PIC_9XX_IRT_PCIE_LINK_INDEX(link));
else
nlm_pic_ack(md->node->picbase, PIC_IRT_PCIE_LINK_INDEX(link));
}
static struct irq_chip xlp_msi_chip = {
.name = "XLP-MSI",
.irq_enable = xlp_msi_enable,
.irq_disable = xlp_msi_disable,
.irq_mask_ack = xlp_msi_mask_ack,
.irq_unmask = xlp_msi_enable,
};
/*
* XLP8XX/4XX/3XX/2XX:
* The MSI-X interrupt handling is different from MSI, there are 32 MSI-X
* interrupts generated by the PIC and each of these correspond to a MSI-X
* vector (0-31) that can be assigned.
*
* We divide the MSI-X vectors to 8 per link and do a per-link allocation
*
* XLP9XX:
* 32 MSI-X vectors are available per link, and the interrupts are not routed
* thru the PIC. PIC ack not needed.
*
* Enable and disable done using standard MSI functions.
*/
static void xlp_msix_mask_ack(struct irq_data *d)
{
struct xlp_msi_data *md;
int link, msixvec;
uint32_t status_reg, bit;
msixvec = nlm_irq_msixvec(d->irq);
link = nlm_irq_msixlink(msixvec);
mask_msi_irq(d);
md = irq_data_get_irq_handler_data(d);
/* Ack MSI on bridge */
if (cpu_is_xlp9xx()) {
status_reg = PCIE_9XX_MSIX_STATUSX(link);
bit = msixvec % XLP_MSIXVEC_PER_LINK;
} else {
status_reg = PCIE_MSIX_STATUS;
bit = msixvec;
}
nlm_write_reg(md->lnkbase, status_reg, 1u << bit);
/* Ack at eirr and PIC */
ack_c0_eirr(PIC_PCIE_MSIX_IRQ(link));
if (!cpu_is_xlp9xx())
nlm_pic_ack(md->node->picbase,
PIC_IRT_PCIE_MSIX_INDEX(msixvec));
}
static struct irq_chip xlp_msix_chip = {
.name = "XLP-MSIX",
.irq_enable = unmask_msi_irq,
.irq_disable = mask_msi_irq,
.irq_mask_ack = xlp_msix_mask_ack,
.irq_unmask = unmask_msi_irq,
};
void arch_teardown_msi_irq(unsigned int irq)
{
}
/*
* Setup a PCIe link for MSI. By default, the links are in
* legacy interrupt mode. We will switch them to MSI mode
* at the first MSI request.
*/
static void xlp_config_link_msi(uint64_t lnkbase, int lirq, uint64_t msiaddr)
{
u32 val;
if (cpu_is_xlp9xx()) {
val = nlm_read_reg(lnkbase, PCIE_9XX_INT_EN0);
if ((val & 0x200) == 0) {
val |= 0x200; /* MSI Interrupt enable */
nlm_write_reg(lnkbase, PCIE_9XX_INT_EN0, val);
}
} else {
val = nlm_read_reg(lnkbase, PCIE_INT_EN0);
if ((val & 0x200) == 0) {
val |= 0x200;
nlm_write_reg(lnkbase, PCIE_INT_EN0, val);
}
}
val = nlm_read_reg(lnkbase, 0x1); /* CMD */
if ((val & 0x0400) == 0) {
val |= 0x0400;
nlm_write_reg(lnkbase, 0x1, val);
}
/* Update IRQ in the PCI irq reg */
val = nlm_read_pci_reg(lnkbase, 0xf);
val &= ~0x1fu;
val |= (1 << 8) | lirq;
nlm_write_pci_reg(lnkbase, 0xf, val);
/* MSI addr */
nlm_write_reg(lnkbase, PCIE_BRIDGE_MSI_ADDRH, msiaddr >> 32);
nlm_write_reg(lnkbase, PCIE_BRIDGE_MSI_ADDRL, msiaddr & 0xffffffff);
/* MSI cap for bridge */
val = nlm_read_reg(lnkbase, PCIE_BRIDGE_MSI_CAP);
if ((val & (1 << 16)) == 0) {
val |= 0xb << 16; /* mmc32, msi enable */
nlm_write_reg(lnkbase, PCIE_BRIDGE_MSI_CAP, val);
}
}
/*
* Allocate a MSI vector on a link
*/
static int xlp_setup_msi(uint64_t lnkbase, int node, int link,
struct msi_desc *desc)
{
struct xlp_msi_data *md;
struct msi_msg msg;
unsigned long flags;
int msivec, irt, lirq, xirq, ret;
uint64_t msiaddr;
/* Get MSI data for the link */
lirq = PIC_PCIE_LINK_MSI_IRQ(link);
xirq = nlm_irq_to_xirq(node, nlm_link_msiirq(link, 0));
md = irq_get_handler_data(xirq);
msiaddr = MSI_LINK_ADDR(node, link);
spin_lock_irqsave(&md->msi_lock, flags);
if (md->msi_alloc_mask == 0) {
xlp_config_link_msi(lnkbase, lirq, msiaddr);
/* switch the link IRQ to MSI range */
if (cpu_is_xlp9xx())
irt = PIC_9XX_IRT_PCIE_LINK_INDEX(link);
else
irt = PIC_IRT_PCIE_LINK_INDEX(link);
nlm_setup_pic_irq(node, lirq, lirq, irt);
nlm_pic_init_irt(nlm_get_node(node)->picbase, irt, lirq,
node * nlm_threads_per_node(), 1 /*en */);
}
/* allocate a MSI vec, and tell the bridge about it */
msivec = fls(md->msi_alloc_mask);
if (msivec == XLP_MSIVEC_PER_LINK) {
spin_unlock_irqrestore(&md->msi_lock, flags);
return -ENOMEM;
}
md->msi_alloc_mask |= (1u << msivec);
spin_unlock_irqrestore(&md->msi_lock, flags);
msg.address_hi = msiaddr >> 32;
msg.address_lo = msiaddr & 0xffffffff;
msg.data = 0xc00 | msivec;
xirq = xirq + msivec; /* msi mapped to global irq space */
ret = irq_set_msi_desc(xirq, desc);
if (ret < 0)
return ret;
write_msi_msg(xirq, &msg);
return 0;
}
/*
* Switch a link to MSI-X mode
*/
static void xlp_config_link_msix(uint64_t lnkbase, int lirq, uint64_t msixaddr)
{
u32 val;
val = nlm_read_reg(lnkbase, 0x2C);
if ((val & 0x80000000U) == 0) {
val |= 0x80000000U;
nlm_write_reg(lnkbase, 0x2C, val);
}
if (cpu_is_xlp9xx()) {
val = nlm_read_reg(lnkbase, PCIE_9XX_INT_EN0);
if ((val & 0x200) == 0) {
val |= 0x200; /* MSI Interrupt enable */
nlm_write_reg(lnkbase, PCIE_9XX_INT_EN0, val);
}
} else {
val = nlm_read_reg(lnkbase, PCIE_INT_EN0);
if ((val & 0x200) == 0) {
val |= 0x200; /* MSI Interrupt enable */
nlm_write_reg(lnkbase, PCIE_INT_EN0, val);
}
}
val = nlm_read_reg(lnkbase, 0x1); /* CMD */
if ((val & 0x0400) == 0) {
val |= 0x0400;
nlm_write_reg(lnkbase, 0x1, val);
}
/* Update IRQ in the PCI irq reg */
val = nlm_read_pci_reg(lnkbase, 0xf);
val &= ~0x1fu;
val |= (1 << 8) | lirq;
nlm_write_pci_reg(lnkbase, 0xf, val);
if (cpu_is_xlp9xx()) {
/* MSI-X addresses */
nlm_write_reg(lnkbase, PCIE_9XX_BRIDGE_MSIX_ADDR_BASE,
msixaddr >> 8);
nlm_write_reg(lnkbase, PCIE_9XX_BRIDGE_MSIX_ADDR_LIMIT,
(msixaddr + MSI_ADDR_SZ) >> 8);
} else {
/* MSI-X addresses */
nlm_write_reg(lnkbase, PCIE_BRIDGE_MSIX_ADDR_BASE,
msixaddr >> 8);
nlm_write_reg(lnkbase, PCIE_BRIDGE_MSIX_ADDR_LIMIT,
(msixaddr + MSI_ADDR_SZ) >> 8);
}
}
/*
* Allocate a MSI-X vector
*/
static int xlp_setup_msix(uint64_t lnkbase, int node, int link,
struct msi_desc *desc)
{
struct xlp_msi_data *md;
struct msi_msg msg;
unsigned long flags;
int t, msixvec, lirq, xirq, ret;
uint64_t msixaddr;
/* Get MSI data for the link */
lirq = PIC_PCIE_MSIX_IRQ(link);
xirq = nlm_irq_to_xirq(node, nlm_link_msixirq(link, 0));
md = irq_get_handler_data(xirq);
msixaddr = MSIX_LINK_ADDR(node, link);
spin_lock_irqsave(&md->msi_lock, flags);
/* switch the PCIe link to MSI-X mode at the first alloc */
if (md->msix_alloc_mask == 0)
xlp_config_link_msix(lnkbase, lirq, msixaddr);
/* allocate a MSI-X vec, and tell the bridge about it */
t = fls(md->msix_alloc_mask);
if (t == XLP_MSIXVEC_PER_LINK) {
spin_unlock_irqrestore(&md->msi_lock, flags);
return -ENOMEM;
}
md->msix_alloc_mask |= (1u << t);
spin_unlock_irqrestore(&md->msi_lock, flags);
xirq += t;
msixvec = nlm_irq_msixvec(xirq);
msg.address_hi = msixaddr >> 32;
msg.address_lo = msixaddr & 0xffffffff;
msg.data = 0xc00 | msixvec;
ret = irq_set_msi_desc(xirq, desc);
if (ret < 0)
return ret;
write_msi_msg(xirq, &msg);
return 0;
}
int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
{
struct pci_dev *lnkdev;
uint64_t lnkbase;
int node, link, slot;
lnkdev = xlp_get_pcie_link(dev);
if (lnkdev == NULL) {
dev_err(&dev->dev, "Could not find bridge\n");
return 1;
}
slot = PCI_SLOT(lnkdev->devfn);
link = PCI_FUNC(lnkdev->devfn);
node = slot / 8;
lnkbase = nlm_get_pcie_base(node, link);
if (desc->msi_attrib.is_msix)
return xlp_setup_msix(lnkbase, node, link, desc);
else
return xlp_setup_msi(lnkbase, node, link, desc);
}
void __init xlp_init_node_msi_irqs(int node, int link)
{
struct nlm_soc_info *nodep;
struct xlp_msi_data *md;
int irq, i, irt, msixvec, val;
pr_info("[%d %d] Init node PCI IRT\n", node, link);
nodep = nlm_get_node(node);
/* Alloc an MSI block for the link */
md = kzalloc(sizeof(*md), GFP_KERNEL);
spin_lock_init(&md->msi_lock);
md->msi_enabled_mask = 0;
md->msi_alloc_mask = 0;
md->msix_alloc_mask = 0;
md->node = nodep;
md->lnkbase = nlm_get_pcie_base(node, link);
/* extended space for MSI interrupts */
irq = nlm_irq_to_xirq(node, nlm_link_msiirq(link, 0));
for (i = irq; i < irq + XLP_MSIVEC_PER_LINK; i++) {
irq_set_chip_and_handler(i, &xlp_msi_chip, handle_level_irq);
irq_set_handler_data(i, md);
}
for (i = 0; i < XLP_MSIXVEC_PER_LINK ; i++) {
if (cpu_is_xlp9xx()) {
val = ((node * nlm_threads_per_node()) << 7 |
PIC_PCIE_MSIX_IRQ(link) << 1 | 0 << 0);
nlm_write_pcie_reg(md->lnkbase, PCIE_9XX_MSIX_VECX(i +
(link * XLP_MSIXVEC_PER_LINK)), val);
} else {
/* Initialize MSI-X irts to generate one interrupt
* per link
*/
msixvec = link * XLP_MSIXVEC_PER_LINK + i;
irt = PIC_IRT_PCIE_MSIX_INDEX(msixvec);
nlm_pic_init_irt(nodep->picbase, irt,
PIC_PCIE_MSIX_IRQ(link),
node * nlm_threads_per_node(), 1);
}
/* Initialize MSI-X extended irq space for the link */
irq = nlm_irq_to_xirq(node, nlm_link_msixirq(link, i));
irq_set_chip_and_handler(irq, &xlp_msix_chip, handle_level_irq);
irq_set_handler_data(irq, md);
}
}
void nlm_dispatch_msi(int node, int lirq)
{
struct xlp_msi_data *md;
int link, i, irqbase;
u32 status;
link = lirq - PIC_PCIE_LINK_MSI_IRQ_BASE;
irqbase = nlm_irq_to_xirq(node, nlm_link_msiirq(link, 0));
md = irq_get_handler_data(irqbase);
if (cpu_is_xlp9xx())
status = nlm_read_reg(md->lnkbase, PCIE_9XX_MSI_STATUS) &
md->msi_enabled_mask;
else
status = nlm_read_reg(md->lnkbase, PCIE_MSI_STATUS) &
md->msi_enabled_mask;
while (status) {
i = __ffs(status);
do_IRQ(irqbase + i);
status &= status - 1;
}
}
void nlm_dispatch_msix(int node, int lirq)
{
struct xlp_msi_data *md;
int link, i, irqbase;
u32 status;
link = lirq - PIC_PCIE_MSIX_IRQ_BASE;
irqbase = nlm_irq_to_xirq(node, nlm_link_msixirq(link, 0));
md = irq_get_handler_data(irqbase);
if (cpu_is_xlp9xx())
status = nlm_read_reg(md->lnkbase, PCIE_9XX_MSIX_STATUSX(link));
else
status = nlm_read_reg(md->lnkbase, PCIE_MSIX_STATUS);
/* narrow it down to the MSI-x vectors for our link */
if (!cpu_is_xlp9xx())
status = (status >> (link * XLP_MSIXVEC_PER_LINK)) &
((1 << XLP_MSIXVEC_PER_LINK) - 1);
while (status) {
i = __ffs(status);
do_IRQ(irqbase + i);
status &= status - 1;
}
}

527
arch/mips/pci/ops-bcm63xx.c Normal file
View file

@ -0,0 +1,527 @@
/*
* 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) 2008 Maxime Bizon <mbizon@freebox.fr>
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/io.h>
#include "pci-bcm63xx.h"
/*
* swizzle 32bits data to return only the needed part
*/
static int postprocess_read(u32 data, int where, unsigned int size)
{
u32 ret;
ret = 0;
switch (size) {
case 1:
ret = (data >> ((where & 3) << 3)) & 0xff;
break;
case 2:
ret = (data >> ((where & 3) << 3)) & 0xffff;
break;
case 4:
ret = data;
break;
}
return ret;
}
static int preprocess_write(u32 orig_data, u32 val, int where,
unsigned int size)
{
u32 ret;
ret = 0;
switch (size) {
case 1:
ret = (orig_data & ~(0xff << ((where & 3) << 3))) |
(val << ((where & 3) << 3));
break;
case 2:
ret = (orig_data & ~(0xffff << ((where & 3) << 3))) |
(val << ((where & 3) << 3));
break;
case 4:
ret = val;
break;
}
return ret;
}
/*
* setup hardware for a configuration cycle with given parameters
*/
static int bcm63xx_setup_cfg_access(int type, unsigned int busn,
unsigned int devfn, int where)
{
unsigned int slot, func, reg;
u32 val;
slot = PCI_SLOT(devfn);
func = PCI_FUNC(devfn);
reg = where >> 2;
/* sanity check */
if (slot > (MPI_L2PCFG_DEVNUM_MASK >> MPI_L2PCFG_DEVNUM_SHIFT))
return 1;
if (func > (MPI_L2PCFG_FUNC_MASK >> MPI_L2PCFG_FUNC_SHIFT))
return 1;
if (reg > (MPI_L2PCFG_REG_MASK >> MPI_L2PCFG_REG_SHIFT))
return 1;
/* ok, setup config access */
val = (reg << MPI_L2PCFG_REG_SHIFT);
val |= (func << MPI_L2PCFG_FUNC_SHIFT);
val |= (slot << MPI_L2PCFG_DEVNUM_SHIFT);
val |= MPI_L2PCFG_CFG_USEREG_MASK;
val |= MPI_L2PCFG_CFG_SEL_MASK;
/* type 0 cycle for local bus, type 1 cycle for anything else */
if (type != 0) {
/* FIXME: how to specify bus ??? */
val |= (1 << MPI_L2PCFG_CFG_TYPE_SHIFT);
}
bcm_mpi_writel(val, MPI_L2PCFG_REG);
return 0;
}
static int bcm63xx_do_cfg_read(int type, unsigned int busn,
unsigned int devfn, int where, int size,
u32 *val)
{
u32 data;
/* two phase cycle, first we write address, then read data at
* another location, caller already has a spinlock so no need
* to add one here */
if (bcm63xx_setup_cfg_access(type, busn, devfn, where))
return PCIBIOS_DEVICE_NOT_FOUND;
iob();
data = le32_to_cpu(__raw_readl(pci_iospace_start));
/* restore IO space normal behaviour */
bcm_mpi_writel(0, MPI_L2PCFG_REG);
*val = postprocess_read(data, where, size);
return PCIBIOS_SUCCESSFUL;
}
static int bcm63xx_do_cfg_write(int type, unsigned int busn,
unsigned int devfn, int where, int size,
u32 val)
{
u32 data;
/* two phase cycle, first we write address, then write data to
* another location, caller already has a spinlock so no need
* to add one here */
if (bcm63xx_setup_cfg_access(type, busn, devfn, where))
return PCIBIOS_DEVICE_NOT_FOUND;
iob();
data = le32_to_cpu(__raw_readl(pci_iospace_start));
data = preprocess_write(data, val, where, size);
__raw_writel(cpu_to_le32(data), pci_iospace_start);
wmb();
/* no way to know the access is done, we have to wait */
udelay(500);
/* restore IO space normal behaviour */
bcm_mpi_writel(0, MPI_L2PCFG_REG);
return PCIBIOS_SUCCESSFUL;
}
static int bcm63xx_pci_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
int type;
type = bus->parent ? 1 : 0;
if (type == 0 && PCI_SLOT(devfn) == CARDBUS_PCI_IDSEL)
return PCIBIOS_DEVICE_NOT_FOUND;
return bcm63xx_do_cfg_read(type, bus->number, devfn,
where, size, val);
}
static int bcm63xx_pci_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
int type;
type = bus->parent ? 1 : 0;
if (type == 0 && PCI_SLOT(devfn) == CARDBUS_PCI_IDSEL)
return PCIBIOS_DEVICE_NOT_FOUND;
return bcm63xx_do_cfg_write(type, bus->number, devfn,
where, size, val);
}
struct pci_ops bcm63xx_pci_ops = {
.read = bcm63xx_pci_read,
.write = bcm63xx_pci_write
};
#ifdef CONFIG_CARDBUS
/*
* emulate configuration read access on a cardbus bridge
*/
#define FAKE_CB_BRIDGE_SLOT 0x1e
static int fake_cb_bridge_bus_number = -1;
static struct {
u16 pci_command;
u8 cb_latency;
u8 subordinate_busn;
u8 cardbus_busn;
u8 pci_busn;
int bus_assigned;
u16 bridge_control;
u32 mem_base0;
u32 mem_limit0;
u32 mem_base1;
u32 mem_limit1;
u32 io_base0;
u32 io_limit0;
u32 io_base1;
u32 io_limit1;
} fake_cb_bridge_regs;
static int fake_cb_bridge_read(int where, int size, u32 *val)
{
unsigned int reg;
u32 data;
data = 0;
reg = where >> 2;
switch (reg) {
case (PCI_VENDOR_ID >> 2):
case (PCI_CB_SUBSYSTEM_VENDOR_ID >> 2):
/* create dummy vendor/device id from our cpu id */
data = (bcm63xx_get_cpu_id() << 16) | PCI_VENDOR_ID_BROADCOM;
break;
case (PCI_COMMAND >> 2):
data = (PCI_STATUS_DEVSEL_SLOW << 16);
data |= fake_cb_bridge_regs.pci_command;
break;
case (PCI_CLASS_REVISION >> 2):
data = (PCI_CLASS_BRIDGE_CARDBUS << 16);
break;
case (PCI_CACHE_LINE_SIZE >> 2):
data = (PCI_HEADER_TYPE_CARDBUS << 16);
break;
case (PCI_INTERRUPT_LINE >> 2):
/* bridge control */
data = (fake_cb_bridge_regs.bridge_control << 16);
/* pin:intA line:0xff */
data |= (0x1 << 8) | 0xff;
break;
case (PCI_CB_PRIMARY_BUS >> 2):
data = (fake_cb_bridge_regs.cb_latency << 24);
data |= (fake_cb_bridge_regs.subordinate_busn << 16);
data |= (fake_cb_bridge_regs.cardbus_busn << 8);
data |= fake_cb_bridge_regs.pci_busn;
break;
case (PCI_CB_MEMORY_BASE_0 >> 2):
data = fake_cb_bridge_regs.mem_base0;
break;
case (PCI_CB_MEMORY_LIMIT_0 >> 2):
data = fake_cb_bridge_regs.mem_limit0;
break;
case (PCI_CB_MEMORY_BASE_1 >> 2):
data = fake_cb_bridge_regs.mem_base1;
break;
case (PCI_CB_MEMORY_LIMIT_1 >> 2):
data = fake_cb_bridge_regs.mem_limit1;
break;
case (PCI_CB_IO_BASE_0 >> 2):
/* | 1 for 32bits io support */
data = fake_cb_bridge_regs.io_base0 | 0x1;
break;
case (PCI_CB_IO_LIMIT_0 >> 2):
data = fake_cb_bridge_regs.io_limit0;
break;
case (PCI_CB_IO_BASE_1 >> 2):
/* | 1 for 32bits io support */
data = fake_cb_bridge_regs.io_base1 | 0x1;
break;
case (PCI_CB_IO_LIMIT_1 >> 2):
data = fake_cb_bridge_regs.io_limit1;
break;
}
*val = postprocess_read(data, where, size);
return PCIBIOS_SUCCESSFUL;
}
/*
* emulate configuration write access on a cardbus bridge
*/
static int fake_cb_bridge_write(int where, int size, u32 val)
{
unsigned int reg;
u32 data, tmp;
int ret;
ret = fake_cb_bridge_read((where & ~0x3), 4, &data);
if (ret != PCIBIOS_SUCCESSFUL)
return ret;
data = preprocess_write(data, val, where, size);
reg = where >> 2;
switch (reg) {
case (PCI_COMMAND >> 2):
fake_cb_bridge_regs.pci_command = (data & 0xffff);
break;
case (PCI_CB_PRIMARY_BUS >> 2):
fake_cb_bridge_regs.cb_latency = (data >> 24) & 0xff;
fake_cb_bridge_regs.subordinate_busn = (data >> 16) & 0xff;
fake_cb_bridge_regs.cardbus_busn = (data >> 8) & 0xff;
fake_cb_bridge_regs.pci_busn = data & 0xff;
if (fake_cb_bridge_regs.cardbus_busn)
fake_cb_bridge_regs.bus_assigned = 1;
break;
case (PCI_INTERRUPT_LINE >> 2):
tmp = (data >> 16) & 0xffff;
/* disable memory prefetch support */
tmp &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
tmp &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
fake_cb_bridge_regs.bridge_control = tmp;
break;
case (PCI_CB_MEMORY_BASE_0 >> 2):
fake_cb_bridge_regs.mem_base0 = data;
break;
case (PCI_CB_MEMORY_LIMIT_0 >> 2):
fake_cb_bridge_regs.mem_limit0 = data;
break;
case (PCI_CB_MEMORY_BASE_1 >> 2):
fake_cb_bridge_regs.mem_base1 = data;
break;
case (PCI_CB_MEMORY_LIMIT_1 >> 2):
fake_cb_bridge_regs.mem_limit1 = data;
break;
case (PCI_CB_IO_BASE_0 >> 2):
fake_cb_bridge_regs.io_base0 = data;
break;
case (PCI_CB_IO_LIMIT_0 >> 2):
fake_cb_bridge_regs.io_limit0 = data;
break;
case (PCI_CB_IO_BASE_1 >> 2):
fake_cb_bridge_regs.io_base1 = data;
break;
case (PCI_CB_IO_LIMIT_1 >> 2):
fake_cb_bridge_regs.io_limit1 = data;
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int bcm63xx_cb_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
/* snoop access to slot 0x1e on root bus, we fake a cardbus
* bridge at this location */
if (!bus->parent && PCI_SLOT(devfn) == FAKE_CB_BRIDGE_SLOT) {
fake_cb_bridge_bus_number = bus->number;
return fake_cb_bridge_read(where, size, val);
}
/* a configuration cycle for the device behind the cardbus
* bridge is actually done as a type 0 cycle on the primary
* bus. This means that only one device can be on the cardbus
* bus */
if (fake_cb_bridge_regs.bus_assigned &&
bus->number == fake_cb_bridge_regs.cardbus_busn &&
PCI_SLOT(devfn) == 0)
return bcm63xx_do_cfg_read(0, 0,
PCI_DEVFN(CARDBUS_PCI_IDSEL, 0),
where, size, val);
return PCIBIOS_DEVICE_NOT_FOUND;
}
static int bcm63xx_cb_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
if (!bus->parent && PCI_SLOT(devfn) == FAKE_CB_BRIDGE_SLOT) {
fake_cb_bridge_bus_number = bus->number;
return fake_cb_bridge_write(where, size, val);
}
if (fake_cb_bridge_regs.bus_assigned &&
bus->number == fake_cb_bridge_regs.cardbus_busn &&
PCI_SLOT(devfn) == 0)
return bcm63xx_do_cfg_write(0, 0,
PCI_DEVFN(CARDBUS_PCI_IDSEL, 0),
where, size, val);
return PCIBIOS_DEVICE_NOT_FOUND;
}
struct pci_ops bcm63xx_cb_ops = {
.read = bcm63xx_cb_read,
.write = bcm63xx_cb_write,
};
/*
* only one IO window, so it cannot be shared by PCI and cardbus, use
* fixup to choose and detect unhandled configuration
*/
static void bcm63xx_fixup(struct pci_dev *dev)
{
static int io_window = -1;
int i, found, new_io_window;
u32 val;
/* look for any io resource */
found = 0;
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
found = 1;
break;
}
}
if (!found)
return;
/* skip our fake bus with only cardbus bridge on it */
if (dev->bus->number == fake_cb_bridge_bus_number)
return;
/* find on which bus the device is */
if (fake_cb_bridge_regs.bus_assigned &&
dev->bus->number == fake_cb_bridge_regs.cardbus_busn &&
PCI_SLOT(dev->devfn) == 0)
new_io_window = 1;
else
new_io_window = 0;
if (new_io_window == io_window)
return;
if (io_window != -1) {
printk(KERN_ERR "bcm63xx: both PCI and cardbus devices "
"need IO, which hardware cannot do\n");
return;
}
printk(KERN_INFO "bcm63xx: PCI IO window assigned to %s\n",
(new_io_window == 0) ? "PCI" : "cardbus");
val = bcm_mpi_readl(MPI_L2PIOREMAP_REG);
if (io_window)
val |= MPI_L2PREMAP_IS_CARDBUS_MASK;
else
val &= ~MPI_L2PREMAP_IS_CARDBUS_MASK;
bcm_mpi_writel(val, MPI_L2PIOREMAP_REG);
io_window = new_io_window;
}
DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, bcm63xx_fixup);
#endif
static int bcm63xx_pcie_can_access(struct pci_bus *bus, int devfn)
{
switch (bus->number) {
case PCIE_BUS_BRIDGE:
return (PCI_SLOT(devfn) == 0);
case PCIE_BUS_DEVICE:
if (PCI_SLOT(devfn) == 0)
return bcm_pcie_readl(PCIE_DLSTATUS_REG)
& DLSTATUS_PHYLINKUP;
default:
return false;
}
}
static int bcm63xx_pcie_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u32 data;
u32 reg = where & ~3;
if (!bcm63xx_pcie_can_access(bus, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
if (bus->number == PCIE_BUS_DEVICE)
reg += PCIE_DEVICE_OFFSET;
data = bcm_pcie_readl(reg);
*val = postprocess_read(data, where, size);
return PCIBIOS_SUCCESSFUL;
}
static int bcm63xx_pcie_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
u32 data;
u32 reg = where & ~3;
if (!bcm63xx_pcie_can_access(bus, devfn))
return PCIBIOS_DEVICE_NOT_FOUND;
if (bus->number == PCIE_BUS_DEVICE)
reg += PCIE_DEVICE_OFFSET;
data = bcm_pcie_readl(reg);
data = preprocess_write(data, val, where, size);
bcm_pcie_writel(data, reg);
return PCIBIOS_SUCCESSFUL;
}
struct pci_ops bcm63xx_pcie_ops = {
.read = bcm63xx_pcie_read,
.write = bcm63xx_pcie_write
};

View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc.
* All rights reserved.
* Authors: Carsten Langgaard <carstenl@mips.com>
* Maciej W. Rozycki <macro@mips.com>
*
* 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/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <asm/mips-boards/bonito64.h>
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
#define CFG_SPACE_REG(offset) (void *)CKSEG1ADDR(_pcictrl_bonito_pcicfg + (offset))
#define ID_SEL_BEGIN 10
#define MAX_DEV_NUM (31 - ID_SEL_BEGIN)
static int bonito64_pcibios_config_access(unsigned char access_type,
struct pci_bus *bus,
unsigned int devfn, int where,
u32 * data)
{
u32 busnum = bus->number;
u32 addr, type;
u32 dummy;
void *addrp;
int device = PCI_SLOT(devfn);
int function = PCI_FUNC(devfn);
int reg = where & ~3;
if (busnum == 0) {
/* Type 0 configuration for onboard PCI bus */
if (device > MAX_DEV_NUM)
return -1;
addr = (1 << (device + ID_SEL_BEGIN)) | (function << 8) | reg;
type = 0;
} else {
/* Type 1 configuration for offboard PCI bus */
addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
type = 0x10000;
}
/* Clear aborts */
BONITO_PCICMD |= BONITO_PCICMD_MABORT_CLR | BONITO_PCICMD_MTABORT_CLR;
BONITO_PCIMAP_CFG = (addr >> 16) | type;
/* Flush Bonito register block */
dummy = BONITO_PCIMAP_CFG;
mmiowb();
addrp = CFG_SPACE_REG(addr & 0xffff);
if (access_type == PCI_ACCESS_WRITE) {
writel(cpu_to_le32(*data), addrp);
/* Wait till done */
while (BONITO_PCIMSTAT & 0xF);
} else {
*data = le32_to_cpu(readl(addrp));
}
/* 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
};

322
arch/mips/pci/ops-bridge.c Normal file
View file

@ -0,0 +1,322 @@
/*
* 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) 1999, 2000, 04, 06 Ralf Baechle (ralf@linux-mips.org)
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
*/
#include <linux/pci.h>
#include <asm/paccess.h>
#include <asm/pci/bridge.h>
#include <asm/sn/arch.h>
#include <asm/sn/intr.h>
#include <asm/sn/sn0/hub.h>
/*
* Most of the IOC3 PCI config register aren't present
* we emulate what is needed for a normal PCI enumeration
*/
static u32 emulate_ioc3_cfg(int where, int size)
{
if (size == 1 && where == 0x3d)
return 0x01;
else if (size == 2 && where == 0x3c)
return 0x0100;
else if (size == 4 && where == 0x3c)
return 0x00000100;
return 0;
}
/*
* 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 fucking fucked beyond belief ... 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 fucking fucked beyond belief ... Don't even give the
* generic PCI code a chance to look at the wrong register.
*/
if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) {
*value = emulate_ioc3_cfg(where, size);
return PCIBIOS_SUCCESSFUL;
}
/*
* IOC3 is fucking fucked beyond belief ... 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 fucking fucked beyond belief ... 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 fucking fucked beyond belief ... Don't even give the
* generic PCI code a chance to look at the wrong register.
*/
if ((where >= 0x14 && where < 0x40) || (where >= 0x48)) {
*value = emulate_ioc3_cfg(where, size);
return PCIBIOS_SUCCESSFUL;
}
/*
* IOC3 is fucking fucked beyond belief ... 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 fucking fucked beyond belief ... 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 fucking fucked beyond belief ... 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 fucking fucked beyond belief ... 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 fucking fucked beyond belief ... 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 fucking fucked beyond belief ... 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 fucking fucked beyond belief ... 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);
}
struct pci_ops bridge_pci_ops = {
.read = pci_read_config,
.write = pci_write_config,
};

182
arch/mips/pci/ops-emma2rh.c Normal file
View file

@ -0,0 +1,182 @@
/*
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/pci/ops-vr41xx.c
*
* Copyright 2001 MontaVista Software Inc.
*
* 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/pci.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/addrspace.h>
#include <asm/debug.h>
#include <asm/emma/emma2rh.h>
#define RTABORT (0x1<<9)
#define RMABORT (0x1<<10)
#define EMMA2RH_PCI_SLOT_NUM 9 /* 0000:09.0 is final PCI device */
/*
* access config space
*/
static int check_args(struct pci_bus *bus, u32 devfn, u32 * bus_num)
{
/* check if the bus is top-level */
if (bus->parent != NULL) {
*bus_num = bus->number;
db_assert(bus_num != NULL);
} else
*bus_num = 0;
if (*bus_num == 0) {
/* Type 0 */
if (PCI_SLOT(devfn) >= 10)
return PCIBIOS_DEVICE_NOT_FOUND;
} else {
/* Type 1 */
if ((*bus_num >= 64) || (PCI_SLOT(devfn) >= 16))
return PCIBIOS_DEVICE_NOT_FOUND;
}
return 0;
}
static inline int set_pci_configuration_address(unsigned char bus_num,
unsigned int devfn, int where)
{
u32 config_win0;
emma2rh_out32(EMMA2RH_PCI_INT, ~RMABORT);
if (bus_num == 0)
/*
* Type 0 configuration
*/
config_win0 = (1 << (22 + PCI_SLOT(devfn))) | (5 << 9);
else
/*
* Type 1 configuration
*/
config_win0 = (bus_num << 26) | (PCI_SLOT(devfn) << 22) |
(1 << 15) | (5 << 9);
emma2rh_out32(EMMA2RH_PCI_IWIN0_CTR, config_win0);
return 0;
}
static int pci_config_read(struct pci_bus *bus, unsigned int devfn, int where,
int size, uint32_t * val)
{
u32 bus_num;
u32 base = KSEG1ADDR(EMMA2RH_PCI_CONFIG_BASE);
u32 backup_win0;
u32 data;
*val = 0xffffffffU;
if (check_args(bus, devfn, &bus_num) == PCIBIOS_DEVICE_NOT_FOUND)
return PCIBIOS_DEVICE_NOT_FOUND;
backup_win0 = emma2rh_in32(EMMA2RH_PCI_IWIN0_CTR);
if (set_pci_configuration_address(bus_num, devfn, where) < 0)
return PCIBIOS_DEVICE_NOT_FOUND;
data =
*(volatile u32 *)(base + (PCI_FUNC(devfn) << 8) +
(where & 0xfffffffc));
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:
emma2rh_out32(EMMA2RH_PCI_IWIN0_CTR, backup_win0);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
emma2rh_out32(EMMA2RH_PCI_IWIN0_CTR, backup_win0);
if (emma2rh_in32(EMMA2RH_PCI_INT) & RMABORT)
return PCIBIOS_DEVICE_NOT_FOUND;
return PCIBIOS_SUCCESSFUL;
}
static int pci_config_write(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 val)
{
u32 bus_num;
u32 base = KSEG1ADDR(EMMA2RH_PCI_CONFIG_BASE);
u32 backup_win0;
u32 data;
int shift;
if (check_args(bus, devfn, &bus_num) == PCIBIOS_DEVICE_NOT_FOUND)
return PCIBIOS_DEVICE_NOT_FOUND;
backup_win0 = emma2rh_in32(EMMA2RH_PCI_IWIN0_CTR);
if (set_pci_configuration_address(bus_num, devfn, where) < 0)
return PCIBIOS_DEVICE_NOT_FOUND;
/* read modify write */
data =
*(volatile u32 *)(base + (PCI_FUNC(devfn) << 8) +
(where & 0xfffffffc));
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:
emma2rh_out32(EMMA2RH_PCI_IWIN0_CTR, backup_win0);
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
*(volatile u32 *)(base + (PCI_FUNC(devfn) << 8) +
(where & 0xfffffffc)) = data;
emma2rh_out32(EMMA2RH_PCI_IWIN0_CTR, backup_win0);
if (emma2rh_in32(EMMA2RH_PCI_INT) & RMABORT)
return PCIBIOS_DEVICE_NOT_FOUND;
return PCIBIOS_SUCCESSFUL;
}
struct pci_ops emma2rh_pci_ops = {
.read = pci_config_read,
.write = pci_config_write,
};

View file

@ -0,0 +1,152 @@
/*
* Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc.
* All rights reserved.
* Authors: Carsten Langgaard <carstenl@mips.com>
* Maciej W. Rozycki <macro@mips.com>
*
* 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 gt64xxx_pci0_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) && (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 gt64xxx_pci0_pcibios_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 * val)
{
u32 data = 0;
if (gt64xxx_pci0_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 gt64xxx_pci0_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 (gt64xxx_pci0_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 (gt64xxx_pci0_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn,
where, &data))
return PCIBIOS_DEVICE_NOT_FOUND;
return PCIBIOS_SUCCESSFUL;
}
struct pci_ops gt64xxx_pci0_ops = {
.read = gt64xxx_pci0_pcibios_read,
.write = gt64xxx_pci0_pcibios_write
};

115
arch/mips/pci/ops-lantiq.c Normal file
View file

@ -0,0 +1,115 @@
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <asm/addrspace.h>
#include <linux/vmalloc.h>
#include <lantiq_soc.h>
#include "pci-lantiq.h"
#define LTQ_PCI_CFG_BUSNUM_SHF 16
#define LTQ_PCI_CFG_DEVNUM_SHF 11
#define LTQ_PCI_CFG_FUNNUM_SHF 8
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
static int ltq_pci_config_access(unsigned char access_type, struct pci_bus *bus,
unsigned int devfn, unsigned int where, u32 *data)
{
unsigned long cfg_base;
unsigned long flags;
u32 temp;
/* we support slot from 0 to 15 dev_fn & 0x68 (AD29) is the
SoC itself */
if ((bus->number != 0) || ((devfn & 0xf8) > 0x78)
|| ((devfn & 0xf8) == 0) || ((devfn & 0xf8) == 0x68))
return 1;
spin_lock_irqsave(&ebu_lock, flags);
cfg_base = (unsigned long) ltq_pci_mapped_cfg;
cfg_base |= (bus->number << LTQ_PCI_CFG_BUSNUM_SHF) | (devfn <<
LTQ_PCI_CFG_FUNNUM_SHF) | (where & ~0x3);
/* Perform access */
if (access_type == PCI_ACCESS_WRITE) {
ltq_w32(swab32(*data), ((u32 *)cfg_base));
} else {
*data = ltq_r32(((u32 *)(cfg_base)));
*data = swab32(*data);
}
wmb();
/* clean possible Master abort */
cfg_base = (unsigned long) ltq_pci_mapped_cfg;
cfg_base |= (0x0 << LTQ_PCI_CFG_FUNNUM_SHF) + 4;
temp = ltq_r32(((u32 *)(cfg_base)));
temp = swab32(temp);
cfg_base = (unsigned long) ltq_pci_mapped_cfg;
cfg_base |= (0x68 << LTQ_PCI_CFG_FUNNUM_SHF) + 4;
ltq_w32(temp, ((u32 *)cfg_base));
spin_unlock_irqrestore(&ebu_lock, flags);
if (((*data) == 0xffffffff) && (access_type == PCI_ACCESS_READ))
return 1;
return 0;
}
int ltq_pci_read_config_dword(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u32 data = 0;
if (ltq_pci_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;
}
int ltq_pci_write_config_dword(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
u32 data = 0;
if (size == 4) {
data = val;
} else {
if (ltq_pci_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 (ltq_pci_config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data))
return PCIBIOS_DEVICE_NOT_FOUND;
return PCIBIOS_SUCCESSFUL;
}

View file

@ -0,0 +1,216 @@
/*
* Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc.
* All rights reserved.
* Authors: Carsten Langgaard <carstenl@mips.com>
* Maciej W. Rozycki <macro@mips.com>
*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin <wuzhangjin@gmail.com>
*
* 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.
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <loongson.h>
#ifdef CONFIG_CS5536
#include <cs5536/cs5536_pci.h>
#include <cs5536/cs5536.h>
#endif
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
#define CFG_SPACE_REG(offset) \
(void *)CKSEG1ADDR(LOONGSON_PCICFG_BASE | (offset))
#define ID_SEL_BEGIN 11
#define MAX_DEV_NUM (31 - ID_SEL_BEGIN)
static int loongson_pcibios_config_access(unsigned char access_type,
struct pci_bus *bus,
unsigned int devfn, int where,
u32 *data)
{
u32 busnum = bus->number;
u32 addr, type;
u32 dummy;
void *addrp;
int device = PCI_SLOT(devfn);
int function = PCI_FUNC(devfn);
int reg = where & ~3;
if (busnum == 0) {
/* board-specific part,currently,only fuloong2f,yeeloong2f
* use CS5536, fuloong2e use via686b, gdium has no
* south bridge
*/
#ifdef CONFIG_CS5536
/* cs5536_pci_conf_read4/write4() will call _rdmsr/_wrmsr() to
* access the regsters PCI_MSR_ADDR, PCI_MSR_DATA_LO,
* PCI_MSR_DATA_HI, which is bigger than PCI_MSR_CTRL, so, it
* will not go this branch, but the others. so, no calling dead
* loop here.
*/
if ((PCI_IDSEL_CS5536 == device) && (reg < PCI_MSR_CTRL)) {
switch (access_type) {
case PCI_ACCESS_READ:
*data = cs5536_pci_conf_read4(function, reg);
break;
case PCI_ACCESS_WRITE:
cs5536_pci_conf_write4(function, reg, *data);
break;
}
return 0;
}
#endif
/* Type 0 configuration for onboard PCI bus */
if (device > MAX_DEV_NUM)
return -1;
addr = (1 << (device + ID_SEL_BEGIN)) | (function << 8) | reg;
type = 0;
} else {
/* Type 1 configuration for offboard PCI bus */
addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
type = 0x10000;
}
/* Clear aborts */
LOONGSON_PCICMD |= LOONGSON_PCICMD_MABORT_CLR | \
LOONGSON_PCICMD_MTABORT_CLR;
LOONGSON_PCIMAP_CFG = (addr >> 16) | type;
/* Flush Bonito register block */
dummy = LOONGSON_PCIMAP_CFG;
mmiowb();
addrp = CFG_SPACE_REG(addr & 0xffff);
if (access_type == PCI_ACCESS_WRITE)
writel(cpu_to_le32(*data), addrp);
else
*data = le32_to_cpu(readl(addrp));
/* Detect Master/Target abort */
if (LOONGSON_PCICMD & (LOONGSON_PCICMD_MABORT_CLR |
LOONGSON_PCICMD_MTABORT_CLR)) {
/* Error occurred */
/* Clear bits */
LOONGSON_PCICMD |= (LOONGSON_PCICMD_MABORT_CLR |
LOONGSON_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 loongson_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 (loongson_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 loongson_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 (loongson_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 (loongson_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where,
&data))
return -1;
return PCIBIOS_SUCCESSFUL;
}
struct pci_ops loongson_pci_ops = {
.read = loongson_pcibios_read,
.write = loongson_pcibios_write
};
#ifdef CONFIG_CS5536
DEFINE_RAW_SPINLOCK(msr_lock);
void _rdmsr(u32 msr, u32 *hi, u32 *lo)
{
struct pci_bus bus = {
.number = PCI_BUS_CS5536
};
u32 devfn = PCI_DEVFN(PCI_IDSEL_CS5536, 0);
unsigned long flags;
raw_spin_lock_irqsave(&msr_lock, flags);
loongson_pcibios_write(&bus, devfn, PCI_MSR_ADDR, 4, msr);
loongson_pcibios_read(&bus, devfn, PCI_MSR_DATA_LO, 4, lo);
loongson_pcibios_read(&bus, devfn, PCI_MSR_DATA_HI, 4, hi);
raw_spin_unlock_irqrestore(&msr_lock, flags);
}
EXPORT_SYMBOL(_rdmsr);
void _wrmsr(u32 msr, u32 hi, u32 lo)
{
struct pci_bus bus = {
.number = PCI_BUS_CS5536
};
u32 devfn = PCI_DEVFN(PCI_IDSEL_CS5536, 0);
unsigned long flags;
raw_spin_lock_irqsave(&msr_lock, flags);
loongson_pcibios_write(&bus, devfn, PCI_MSR_ADDR, 4, msr);
loongson_pcibios_write(&bus, devfn, PCI_MSR_DATA_LO, 4, lo);
loongson_pcibios_write(&bus, devfn, PCI_MSR_DATA_HI, 4, hi);
raw_spin_unlock_irqrestore(&msr_lock, flags);
}
EXPORT_SYMBOL(_wrmsr);
#endif

View file

@ -0,0 +1,101 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <asm/mips-boards/bonito64.h>
#include <loongson.h>
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
#define HT1LO_PCICFG_BASE 0x1a000000
#define HT1LO_PCICFG_BASE_TP1 0x1b000000
static int loongson3_pci_config_access(unsigned char access_type,
struct pci_bus *bus, unsigned int devfn,
int where, u32 *data)
{
unsigned char busnum = bus->number;
u_int64_t addr, type;
void *addrp;
int device = PCI_SLOT(devfn);
int function = PCI_FUNC(devfn);
int reg = where & ~3;
addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
if (busnum == 0) {
if (device > 31)
return PCIBIOS_DEVICE_NOT_FOUND;
addrp = (void *)(TO_UNCAC(HT1LO_PCICFG_BASE) | (addr & 0xffff));
type = 0;
} else {
addrp = (void *)(TO_UNCAC(HT1LO_PCICFG_BASE_TP1) | (addr));
type = 0x10000;
}
if (access_type == PCI_ACCESS_WRITE)
writel(*data, addrp);
else {
*data = readl(addrp);
if (*data == 0xffffffff) {
*data = -1;
return PCIBIOS_DEVICE_NOT_FOUND;
}
}
return PCIBIOS_SUCCESSFUL;
}
static int loongson3_pci_pcibios_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u32 data = 0;
int ret = loongson3_pci_config_access(PCI_ACCESS_READ,
bus, devfn, where, &data);
if (ret != PCIBIOS_SUCCESSFUL)
return ret;
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 loongson3_pci_pcibios_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
u32 data = 0;
int ret;
if (size == 4)
data = val;
else {
ret = loongson3_pci_config_access(PCI_ACCESS_READ,
bus, devfn, where, &data);
if (ret != PCIBIOS_SUCCESSFUL)
return ret;
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));
}
ret = loongson3_pci_config_access(PCI_ACCESS_WRITE,
bus, devfn, where, &data);
return ret;
}
struct pci_ops loongson_pci_ops = {
.read = loongson3_pci_pcibios_read,
.write = loongson3_pci_pcibios_write
};

101
arch/mips/pci/ops-mace.c Normal file
View file

@ -0,0 +1,101 @@
/*
* 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/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
*/
static inline int mkaddr(struct pci_bus *bus, unsigned int devfn,
unsigned int reg)
{
return ((bus->number & 0xff) << 16) |
((devfn & 0xff) << 8) |
(reg & 0xfc);
}
static int
mace_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 *val)
{
u32 control = mace->pci.control;
/* disable master aborts interrupts during config read */
mace->pci.control = control & ~MACEPCI_CONTROL_MAR_INT;
mace->pci.config_addr = mkaddr(bus, 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;
}
/* ack possible master abort */
mace->pci.error &= ~MACEPCI_ERROR_MASTER_ABORT;
mace->pci.control = control;
/*
* someone forgot to set the ultra bit for the onboard
* scsi chips; we fake it here
*/
if (bus->number == 0 && reg == 0x40 && size == 4 &&
(devfn == (1 << 3) || devfn == (2 << 3)))
*val |= 0x1000;
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)
{
mace->pci.config_addr = mkaddr(bus, 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,
};

147
arch/mips/pci/ops-msc.c Normal file
View file

@ -0,0 +1,147 @@
/*
* 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/types.h>
#include <linux/pci.h>
#include <linux/kernel.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;
u32 intr;
/* Clear status register bits. */
MSC_WRITE(MSC01_PCI_INTSTAT,
(MSC01_PCI_INTCFG_MA_BIT | MSC01_PCI_INTCFG_TA_BIT));
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)));
/* 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_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
};

145
arch/mips/pci/ops-nile4.c Normal file
View file

@ -0,0 +1,145 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <asm/bootinfo.h>
#include <asm/lasat/lasat.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 DEFINE_SPINLOCK(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,
};

985
arch/mips/pci/ops-pmcmsp.c Normal file
View file

@ -0,0 +1,985 @@
/*
* PMC-Sierra MSP board specific pci_ops
*
* Copyright 2001 MontaVista Software Inc.
* Copyright 2005-2007 PMC-Sierra, Inc
*
* Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
*
* Much of the code is derived from the original DDB5074 port by
* Geert Uytterhoeven <geert@linux-m68k.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.
*
*/
#define PCI_COUNTERS 1
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#if defined(CONFIG_PROC_FS) && defined(PCI_COUNTERS)
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#endif /* CONFIG_PROC_FS && PCI_COUNTERS */
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/byteorder.h>
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
#include <asm/mipsmtregs.h>
#endif
#include <msp_prom.h>
#include <msp_cic_int.h>
#include <msp_pci.h>
#include <msp_regs.h>
#include <msp_regops.h>
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
#if defined(CONFIG_PROC_FS) && defined(PCI_COUNTERS)
static char proc_init;
extern struct proc_dir_entry *proc_bus_pci_dir;
unsigned int pci_int_count[32];
static void pci_proc_init(void);
/*****************************************************************************
*
* FUNCTION: show_msp_pci_counts
* _________________________________________________________________________
*
* DESCRIPTION: Prints the count of how many times each PCI
* interrupt has asserted. Can be invoked by the
* /proc filesystem.
*
* INPUTS: m - synthetic file construction data
* v - iterator
*
* RETURNS: 0 or error
*
****************************************************************************/
static int show_msp_pci_counts(struct seq_file *m, void *v)
{
int i;
unsigned int intcount, total = 0;
for (i = 0; i < 32; ++i) {
intcount = pci_int_count[i];
if (intcount != 0) {
seq_printf(m, "[%d] = %u\n", i, intcount);
total += intcount;
}
}
seq_printf(m, "total = %u\n", total);
return 0;
}
static int msp_pci_rd_cnt_open(struct inode *inode, struct file *file)
{
return single_open(file, show_msp_pci_counts, NULL);
}
static const struct file_operations msp_pci_rd_cnt_fops = {
.open = msp_pci_rd_cnt_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*****************************************************************************
*
* FUNCTION: gen_pci_cfg_wr_show
* _________________________________________________________________________
*
* DESCRIPTION: Generates a configuration write cycle for debug purposes.
* The IDSEL line asserted and location and data written are
* immaterial. Just want to be able to prove that a
* configuration write can be correctly generated on the
* PCI bus. Intent is that this function by invocable from
* the /proc filesystem.
*
* INPUTS: m - synthetic file construction data
* v - iterator
*
* RETURNS: 0 or error
*
****************************************************************************/
static int gen_pci_cfg_wr_show(struct seq_file *m, void *v)
{
unsigned char where = 0; /* Write to static Device/Vendor ID */
unsigned char bus_num = 0; /* Bus 0 */
unsigned char dev_fn = 0xF; /* Arbitrary device number */
u32 wr_data = 0xFF00AA00; /* Arbitrary data */
struct msp_pci_regs *preg = (void *)PCI_BASE_REG;
unsigned long value;
int intr;
seq_puts(m, "PMC MSP PCI: Beginning\n");
if (proc_init == 0) {
pci_proc_init();
proc_init = ~0;
}
seq_puts(m, "PMC MSP PCI: Before Cfg Wr\n");
/*
* Generate PCI Configuration Write Cycle
*/
/* Clear cause register bits */
preg->if_status = ~(BPCI_IFSTATUS_BC0F | BPCI_IFSTATUS_BC1F);
/* Setup address that is to appear on PCI bus */
preg->config_addr = BPCI_CFGADDR_ENABLE |
(bus_num << BPCI_CFGADDR_BUSNUM_SHF) |
(dev_fn << BPCI_CFGADDR_FUNCTNUM_SHF) |
(where & 0xFC);
value = cpu_to_le32(wr_data);
/* Launch the PCI configuration write cycle */
*PCI_CONFIG_SPACE_REG = value;
/*
* Check if the PCI configuration cycle (rd or wr) succeeded, by
* checking the status bits for errors like master or target abort.
*/
intr = preg->if_status;
seq_puts(m, "PMC MSP PCI: After Cfg Wr\n");
return 0;
}
static int gen_pci_cfg_wr_open(struct inode *inode, struct file *file)
{
return single_open(file, gen_pci_cfg_wr_show, NULL);
}
static const struct file_operations gen_pci_cfg_wr_fops = {
.open = gen_pci_cfg_wr_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*****************************************************************************
*
* FUNCTION: pci_proc_init
* _________________________________________________________________________
*
* DESCRIPTION: Create entries in the /proc filesystem for debug access.
*
* INPUTS: none
*
* OUTPUTS: none
*
* RETURNS: none
*
****************************************************************************/
static void pci_proc_init(void)
{
proc_create("pmc_msp_pci_rd_cnt", 0, NULL, &msp_pci_rd_cnt_fops);
proc_create("pmc_msp_pci_cfg_wr", 0, NULL, &gen_pci_cfg_wr_fops);
}
#endif /* CONFIG_PROC_FS && PCI_COUNTERS */
static DEFINE_SPINLOCK(bpci_lock);
/*****************************************************************************
*
* STRUCT: pci_io_resource
* _________________________________________________________________________
*
* DESCRIPTION: Defines the address range that pciauto() will use to
* assign to the I/O BARs of PCI devices.
*
* Use the start and end addresses of the MSP7120 PCI Host
* Controller I/O space, in the form that they appear on the
* PCI bus AFTER MSP7120 has performed address translation.
*
* For I/O accesses, MSP7120 ignores OATRAN and maps I/O
* accesses into the bottom 0xFFF region of address space,
* so that is the range to put into the pci_io_resource
* struct.
*
* In MSP4200, the start address was 0x04 instead of the
* expected 0x00. Will just assume there was a good reason
* for this!
*
* NOTES: Linux, by default, will assign I/O space to the lowest
* region of address space. Since MSP7120 and Linux,
* by default, have no offset in between how they map, the
* io_offset element of pci_controller struct should be set
* to zero.
* ELEMENTS:
* name - String used for a meaningful name.
*
* start - Start address of MSP7120's I/O space, as MSP7120 presents
* the address on the PCI bus.
*
* end - End address of MSP7120's I/O space, as MSP7120 presents
* the address on the PCI bus.
*
* flags - Attributes indicating the type of resource. In this case,
* indicate I/O space.
*
****************************************************************************/
static struct resource pci_io_resource = {
.name = "pci IO space",
.start = 0x04,
.end = 0x0FFF,
.flags = IORESOURCE_IO /* I/O space */
};
/*****************************************************************************
*
* STRUCT: pci_mem_resource
* _________________________________________________________________________
*
* DESCRIPTION: Defines the address range that pciauto() will use to
* assign to the memory BARs of PCI devices.
*
* The .start and .end values are dependent upon how address
* translation is performed by the OATRAN regiser.
*
* The values to use for .start and .end are the values
* in the form they appear on the PCI bus AFTER MSP7120 has
* performed OATRAN address translation.
*
* ELEMENTS:
* name - String used for a meaningful name.
*
* start - Start address of MSP7120's memory space, as MSP7120 presents
* the address on the PCI bus.
*
* end - End address of MSP7120's memory space, as MSP7120 presents
* the address on the PCI bus.
*
* flags - Attributes indicating the type of resource. In this case,
* indicate memory space.
*
****************************************************************************/
static struct resource pci_mem_resource = {
.name = "pci memory space",
.start = MSP_PCI_SPACE_BASE,
.end = MSP_PCI_SPACE_END,
.flags = IORESOURCE_MEM /* memory space */
};
/*****************************************************************************
*
* FUNCTION: bpci_interrupt
* _________________________________________________________________________
*
* DESCRIPTION: PCI status interrupt handler. Updates the count of how
* many times each status bit has been set, then clears
* the status bits. If the appropriate macros are defined,
* these counts can be viewed via the /proc filesystem.
*
* INPUTS: irq - unused
* dev_id - unused
* pt_regs - unused
*
* OUTPUTS: none
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
*
****************************************************************************/
static irqreturn_t bpci_interrupt(int irq, void *dev_id)
{
struct msp_pci_regs *preg = (void *)PCI_BASE_REG;
unsigned int stat = preg->if_status;
#if defined(CONFIG_PROC_FS) && defined(PCI_COUNTERS)
int i;
for (i = 0; i < 32; ++i) {
if ((1 << i) & stat)
++pci_int_count[i];
}
#endif /* PROC_FS && PCI_COUNTERS */
/* printk("PCI ISR: Status=%08X\n", stat); */
/* write to clear all asserted interrupts */
preg->if_status = stat;
return IRQ_HANDLED;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_config_access
* _________________________________________________________________________
*
* DESCRIPTION: Performs a PCI configuration access (rd or wr), then
* checks that the access succeeded by querying MSP7120's
* PCI status bits.
*
* INPUTS:
* access_type - kind of PCI configuration cycle to perform
* (read or write). Legal values are
* PCI_ACCESS_WRITE and PCI_ACCESS_READ.
*
* bus - pointer to the bus number of the device to
* be targeted for the configuration cycle.
* The only element of the pci_bus structure
* used is bus->number. This argument determines
* if the configuration access will be Type 0 or
* Type 1. Since MSP7120 assumes itself to be the
* PCI Host, any non-zero bus->number generates
* a Type 1 access.
*
* devfn - this is an 8-bit field. The lower three bits
* specify the function number of the device to
* be targeted for the configuration cycle, with
* all three-bit combinations being legal. The
* upper five bits specify the device number,
* with legal values being 10 to 31.
*
* where - address within the Configuration Header
* space to access.
*
* data - for write accesses, contains the data to
* write.
*
* OUTPUTS:
* data - for read accesses, contains the value read.
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* -1 - access failure
*
****************************************************************************/
int msp_pcibios_config_access(unsigned char access_type,
struct pci_bus *bus,
unsigned int devfn,
unsigned char where,
u32 *data)
{
struct msp_pci_regs *preg = (void *)PCI_BASE_REG;
unsigned char bus_num = bus->number;
unsigned char dev_fn = (unsigned char)devfn;
unsigned long flags;
unsigned long intr;
unsigned long value;
static char pciirqflag;
int ret;
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
unsigned int vpe_status;
#endif
#if defined(CONFIG_PROC_FS) && defined(PCI_COUNTERS)
if (proc_init == 0) {
pci_proc_init();
proc_init = ~0;
}
#endif /* CONFIG_PROC_FS && PCI_COUNTERS */
/*
* Just the first time this function invokes, allocate
* an interrupt line for PCI host status interrupts. The
* allocation assigns an interrupt handler to the interrupt.
*/
if (pciirqflag == 0) {
ret = request_irq(MSP_INT_PCI,/* Hardcoded internal MSP7120 wiring */
bpci_interrupt,
IRQF_SHARED,
"PMC MSP PCI Host",
preg);
if (ret != 0)
return ret;
pciirqflag = ~0;
}
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
local_irq_save(flags);
vpe_status = dvpe();
#else
spin_lock_irqsave(&bpci_lock, flags);
#endif
/*
* Clear PCI cause register bits.
*
* In Polo, the PCI Host had a dedicated DMA called the
* Block Copy (not to be confused with the general purpose Block
* Copy Engine block). There appear to have been special interrupts
* for this Block Copy, called Block Copy 0 Fault (BC0F) and
* Block Copy 1 Fault (BC1F). MSP4200 and MSP7120 don't have this
* dedicated Block Copy block, so these two interrupts are now
* marked reserved. In case the Block Copy is resurrected in a
* future design, maintain the code that treats these two interrupts
* specially.
*
* Write to clear all interrupts in the PCI status register, aside
* from BC0F and BC1F.
*/
preg->if_status = ~(BPCI_IFSTATUS_BC0F | BPCI_IFSTATUS_BC1F);
/* Setup address that is to appear on PCI bus */
preg->config_addr = BPCI_CFGADDR_ENABLE |
(bus_num << BPCI_CFGADDR_BUSNUM_SHF) |
(dev_fn << BPCI_CFGADDR_FUNCTNUM_SHF) |
(where & 0xFC);
/* IF access is a PCI configuration write */
if (access_type == PCI_ACCESS_WRITE) {
value = cpu_to_le32(*data);
*PCI_CONFIG_SPACE_REG = value;
} else {
/* ELSE access is a PCI configuration read */
value = le32_to_cpu(*PCI_CONFIG_SPACE_REG);
*data = value;
}
/*
* Check if the PCI configuration cycle (rd or wr) succeeded, by
* checking the status bits for errors like master or target abort.
*/
intr = preg->if_status;
/* Clear config access */
preg->config_addr = 0;
/* IF error occurred */
if (intr & ~(BPCI_IFSTATUS_BC0F | BPCI_IFSTATUS_BC1F)) {
/* Clear status bits */
preg->if_status = ~(BPCI_IFSTATUS_BC0F | BPCI_IFSTATUS_BC1F);
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
evpe(vpe_status);
local_irq_restore(flags);
#else
spin_unlock_irqrestore(&bpci_lock, flags);
#endif
return -1;
}
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
evpe(vpe_status);
local_irq_restore(flags);
#else
spin_unlock_irqrestore(&bpci_lock, flags);
#endif
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_read_config_byte
* _________________________________________________________________________
*
* DESCRIPTION: Read a byte from PCI configuration address spac
* Since the hardware can't address 8 bit chunks
* directly, read a 32-bit chunk, then mask off extraneous
* bits.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the read is destined for.
* devfn - device/function combination that the read is
* destined for.
* where - register within the Configuration Header space
* to access.
*
* OUTPUTS val - read data
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* -1 - read access failure
*
****************************************************************************/
static int
msp_pcibios_read_config_byte(struct pci_bus *bus,
unsigned int devfn,
int where,
u32 *val)
{
u32 data = 0;
/*
* If the config access did not complete normally (e.g., underwent
* master abort) do the PCI compliant thing, which is to supply an
* all ones value.
*/
if (msp_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
where, &data)) {
*val = 0xFFFFFFFF;
return -1;
}
*val = (data >> ((where & 3) << 3)) & 0x0ff;
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_read_config_word
* _________________________________________________________________________
*
* DESCRIPTION: Read a word (16 bits) from PCI configuration address space.
* Since the hardware can't address 16 bit chunks
* directly, read a 32-bit chunk, then mask off extraneous
* bits.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the read is destined for.
* devfn - device/function combination that the read is
* destined for.
* where - register within the Configuration Header space
* to access.
*
* OUTPUTS val - read data
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* PCIBIOS_BAD_REGISTER_NUMBER - bad register address
* -1 - read access failure
*
****************************************************************************/
static int
msp_pcibios_read_config_word(struct pci_bus *bus,
unsigned int devfn,
int where,
u32 *val)
{
u32 data = 0;
/* if (where & 1) */ /* Commented out non-compliant code.
* Should allow word access to configuration
* registers, with only exception being when
* the word access would wrap around into
* the next dword.
*/
if ((where & 3) == 3) {
*val = 0xFFFFFFFF;
return PCIBIOS_BAD_REGISTER_NUMBER;
}
/*
* If the config access did not complete normally (e.g., underwent
* master abort) do the PCI compliant thing, which is to supply an
* all ones value.
*/
if (msp_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
where, &data)) {
*val = 0xFFFFFFFF;
return -1;
}
*val = (data >> ((where & 3) << 3)) & 0x0ffff;
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_read_config_dword
* _________________________________________________________________________
*
* DESCRIPTION: Read a double word (32 bits) from PCI configuration
* address space.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the read is destined for.
* devfn - device/function combination that the read is
* destined for.
* where - register within the Configuration Header space
* to access.
*
* OUTPUTS val - read data
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* PCIBIOS_BAD_REGISTER_NUMBER - bad register address
* -1 - read access failure
*
****************************************************************************/
static int
msp_pcibios_read_config_dword(struct pci_bus *bus,
unsigned int devfn,
int where,
u32 *val)
{
u32 data = 0;
/* Address must be dword aligned. */
if (where & 3) {
*val = 0xFFFFFFFF;
return PCIBIOS_BAD_REGISTER_NUMBER;
}
/*
* If the config access did not complete normally (e.g., underwent
* master abort) do the PCI compliant thing, which is to supply an
* all ones value.
*/
if (msp_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
where, &data)) {
*val = 0xFFFFFFFF;
return -1;
}
*val = data;
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_write_config_byte
* _________________________________________________________________________
*
* DESCRIPTION: Write a byte to PCI configuration address space.
* Since the hardware can't address 8 bit chunks
* directly, a read-modify-write is performed.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the write is destined for.
* devfn - device/function combination that the write is
* destined for.
* where - register within the Configuration Header space
* to access.
* val - value to write
*
* OUTPUTS none
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* -1 - write access failure
*
****************************************************************************/
static int
msp_pcibios_write_config_byte(struct pci_bus *bus,
unsigned int devfn,
int where,
u8 val)
{
u32 data = 0;
/* read config space */
if (msp_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
where, &data))
return -1;
/* modify the byte within the dword */
data = (data & ~(0xff << ((where & 3) << 3))) |
(val << ((where & 3) << 3));
/* write back the full dword */
if (msp_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn,
where, &data))
return -1;
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_write_config_word
* _________________________________________________________________________
*
* DESCRIPTION: Write a word (16-bits) to PCI configuration address space.
* Since the hardware can't address 16 bit chunks
* directly, a read-modify-write is performed.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the write is destined for.
* devfn - device/function combination that the write is
* destined for.
* where - register within the Configuration Header space
* to access.
* val - value to write
*
* OUTPUTS none
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* PCIBIOS_BAD_REGISTER_NUMBER - bad register address
* -1 - write access failure
*
****************************************************************************/
static int
msp_pcibios_write_config_word(struct pci_bus *bus,
unsigned int devfn,
int where,
u16 val)
{
u32 data = 0;
/* Fixed non-compliance: if (where & 1) */
if ((where & 3) == 3)
return PCIBIOS_BAD_REGISTER_NUMBER;
/* read config space */
if (msp_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
where, &data))
return -1;
/* modify the word within the dword */
data = (data & ~(0xffff << ((where & 3) << 3))) |
(val << ((where & 3) << 3));
/* write back the full dword */
if (msp_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn,
where, &data))
return -1;
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_write_config_dword
* _________________________________________________________________________
*
* DESCRIPTION: Write a double word (32-bits) to PCI configuration address
* space.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the write is destined for.
* devfn - device/function combination that the write is
* destined for.
* where - register within the Configuration Header space
* to access.
* val - value to write
*
* OUTPUTS none
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* PCIBIOS_BAD_REGISTER_NUMBER - bad register address
* -1 - write access failure
*
****************************************************************************/
static int
msp_pcibios_write_config_dword(struct pci_bus *bus,
unsigned int devfn,
int where,
u32 val)
{
/* check that address is dword aligned */
if (where & 3)
return PCIBIOS_BAD_REGISTER_NUMBER;
/* perform write */
if (msp_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn,
where, &val))
return -1;
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_read_config
* _________________________________________________________________________
*
* DESCRIPTION: Interface the PCI configuration read request with
* the appropriate function, based on how many bytes
* the read request is.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the write is destined for.
* devfn - device/function combination that the write is
* destined for.
* where - register within the Configuration Header space
* to access.
* size - in units of bytes, should be 1, 2, or 4.
*
* OUTPUTS val - value read, with any extraneous bytes masked
* to zero.
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* -1 - failure
*
****************************************************************************/
int
msp_pcibios_read_config(struct pci_bus *bus,
unsigned int devfn,
int where,
int size,
u32 *val)
{
if (size == 1) {
if (msp_pcibios_read_config_byte(bus, devfn, where, val)) {
return -1;
}
} else if (size == 2) {
if (msp_pcibios_read_config_word(bus, devfn, where, val)) {
return -1;
}
} else if (size == 4) {
if (msp_pcibios_read_config_dword(bus, devfn, where, val)) {
return -1;
}
} else {
*val = 0xFFFFFFFF;
return -1;
}
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* FUNCTION: msp_pcibios_write_config
* _________________________________________________________________________
*
* DESCRIPTION: Interface the PCI configuration write request with
* the appropriate function, based on how many bytes
* the read request is.
*
* INPUTS bus - structure containing attributes for the PCI bus
* that the write is destined for.
* devfn - device/function combination that the write is
* destined for.
* where - register within the Configuration Header space
* to access.
* size - in units of bytes, should be 1, 2, or 4.
* val - value to write
*
* OUTPUTS: none
*
* RETURNS: PCIBIOS_SUCCESSFUL - success
* -1 - failure
*
****************************************************************************/
int
msp_pcibios_write_config(struct pci_bus *bus,
unsigned int devfn,
int where,
int size,
u32 val)
{
if (size == 1) {
if (msp_pcibios_write_config_byte(bus, devfn,
where, (u8)(0xFF & val))) {
return -1;
}
} else if (size == 2) {
if (msp_pcibios_write_config_word(bus, devfn,
where, (u16)(0xFFFF & val))) {
return -1;
}
} else if (size == 4) {
if (msp_pcibios_write_config_dword(bus, devfn, where, val)) {
return -1;
}
} else {
return -1;
}
return PCIBIOS_SUCCESSFUL;
}
/*****************************************************************************
*
* STRUCTURE: msp_pci_ops
* _________________________________________________________________________
*
* DESCRIPTION: structure to abstract the hardware specific PCI
* configuration accesses.
*
* ELEMENTS:
* read - function for Linux to generate PCI Configuration reads.
* write - function for Linux to generate PCI Configuration writes.
*
****************************************************************************/
struct pci_ops msp_pci_ops = {
.read = msp_pcibios_read_config,
.write = msp_pcibios_write_config
};
/*****************************************************************************
*
* STRUCTURE: msp_pci_controller
* _________________________________________________________________________
*
* Describes the attributes of the MSP7120 PCI Host Controller
*
* ELEMENTS:
* pci_ops - abstracts the hardware specific PCI configuration
* accesses.
*
* mem_resource - address range pciauto() uses to assign to PCI device
* memory BARs.
*
* mem_offset - offset between how MSP7120 outbound PCI memory
* transaction addresses appear on the PCI bus and how Linux
* wants to configure memory BARs of the PCI devices.
* MSP7120 does nothing funky, so just set to zero.
*
* io_resource - address range pciauto() uses to assign to PCI device
* I/O BARs.
*
* io_offset - offset between how MSP7120 outbound PCI I/O
* transaction addresses appear on the PCI bus and how
* Linux defaults to configure I/O BARs of the PCI devices.
* MSP7120 maps outbound I/O accesses into the bottom
* bottom 4K of PCI address space (and ignores OATRAN).
* Since the Linux default is to configure I/O BARs to the
* bottom 4K, no special offset is needed. Just set to zero.
*
****************************************************************************/
static struct pci_controller msp_pci_controller = {
.pci_ops = &msp_pci_ops,
.mem_resource = &pci_mem_resource,
.mem_offset = 0,
.io_map_base = MSP_PCI_IOSPACE_BASE,
.io_resource = &pci_io_resource,
.io_offset = 0
};
/*****************************************************************************
*
* FUNCTION: msp_pci_init
* _________________________________________________________________________
*
* DESCRIPTION: Initialize the PCI Host Controller and register it with
* Linux so Linux can seize control of the PCI bus.
*
****************************************************************************/
void __init msp_pci_init(void)
{
struct msp_pci_regs *preg = (void *)PCI_BASE_REG;
u32 id;
/* Extract Device ID */
id = read_reg32(PCI_JTAG_DEVID_REG, 0xFFFF) >> 12;
/* Check if JTAG ID identifies MSP7120 */
if (!MSP_HAS_PCI(id)) {
printk(KERN_WARNING "PCI: No PCI; id reads as %x\n", id);
goto no_pci;
}
/*
* Enable flushing of the PCI-SDRAM queue upon a read
* of the SDRAM's Memory Configuration Register.
*/
*(unsigned long *)QFLUSH_REG_1 = 3;
/* Configure PCI Host Controller. */
preg->if_status = ~0; /* Clear cause register bits */
preg->config_addr = 0; /* Clear config access */
preg->oatran = MSP_PCI_OATRAN; /* PCI outbound addr translation */
preg->if_mask = 0xF8BF87C0; /* Enable all PCI status interrupts */
/* configure so inb(), outb(), and family are functional */
set_io_port_base(MSP_PCI_IOSPACE_BASE);
/* Tell Linux the details of the MSP7120 PCI Host Controller */
register_pci_controller(&msp_pci_controller);
return;
no_pci:
/* Disable PCI channel */
printk(KERN_WARNING "PCI: no host PCI bus detected\n");
}

206
arch/mips/pci/ops-rc32434.c Normal file
View file

@ -0,0 +1,206 @@
/*
* BRIEF MODULE DESCRIPTION
* pci_ops for IDT EB434 board
*
* Copyright 2004 IDT Inc. (rischelp@idt.com)
* Copyright 2006 Felix Fietkau <nbd@openwrt.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/delay.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <asm/cpu.h>
#include <asm/mach-rc32434/rc32434.h>
#include <asm/mach-rc32434/pci.h>
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
#define PCI_CFG_SET(bus, slot, func, off) \
(rc32434_pci->pcicfga = (0x80000000 | \
((bus) << 16) | ((slot)<<11) | \
((func)<<8) | (off)))
static inline int config_access(unsigned char access_type,
struct pci_bus *bus, unsigned int devfn,
unsigned char where, u32 *data)
{
unsigned int slot = PCI_SLOT(devfn);
u8 func = PCI_FUNC(devfn);
/* Setup address */
PCI_CFG_SET(bus->number, slot, func, where);
rc32434_sync();
if (access_type == PCI_ACCESS_WRITE)
rc32434_pci->pcicfgd = *data;
else
*data = rc32434_pci->pcicfgd;
rc32434_sync();
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 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);
*val = (data >> ((where & 3) << 3)) & 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);
*val = (data >> ((where & 3) << 3)) & 0xffff;
return ret;
}
static int read_config_dword(struct pci_bus *bus, unsigned int devfn,
int where, u32 *val)
{
int ret;
int delay = 1;
/*
* Don't scan too far, else there will be errors with plugged in
* daughterboard (rb564).
*/
if (bus->number == 0 && PCI_SLOT(devfn) > 21)
return 0;
retry:
ret = config_access(PCI_ACCESS_READ, bus, devfn, where, val);
/*
* Certain devices react delayed at device scan time, this
* gives them time to settle
*/
if (where == PCI_VENDOR_ID) {
if (ret == 0xffffffff || ret == 0x00000000 ||
ret == 0x0000ffff || ret == 0xffff0000) {
if (delay > 4)
return 0;
delay *= 2;
msleep(delay);
goto retry;
}
}
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 pci_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
switch (size) {
case 1:
return read_config_byte(bus, devfn, where, (u8 *) val);
case 2:
return read_config_word(bus, devfn, where, (u16 *) val);
default:
return read_config_dword(bus, devfn, where, val);
}
}
static int pci_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 rc32434_pci_ops = {
.read = pci_config_read,
.write = pci_config_write,
};

164
arch/mips/pci/ops-sni.c Normal file
View file

@ -0,0 +1,164 @@
/*
* 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 = inb(PCIMT_CONFIG_DATA + (reg & 3));
break;
case 2:
*val = inw(PCIMT_CONFIG_DATA + (reg & 2));
break;
case 4:
*val = inl(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:
outb(val, PCIMT_CONFIG_DATA + (reg & 3));
break;
case 2:
outw(val, PCIMT_CONFIG_DATA + (reg & 2));
break;
case 4:
outl(val, PCIMT_CONFIG_DATA);
break;
}
return 0;
}
struct pci_ops sni_pcimt_ops = {
.read = pcimt_read,
.write = pcimt_write,
};
static int pcit_set_config_address(unsigned int busno, unsigned int devfn, int reg)
{
if ((devfn > 255) || (reg > 255) || (busno > 255))
return PCIBIOS_BAD_REGISTER_NUMBER;
outl((1 << 31) | ((busno & 0xff) << 16) | ((devfn & 0xff) << 8) | (reg & 0xfc), 0xcf8);
return PCIBIOS_SUCCESSFUL;
}
static int pcit_read(struct pci_bus *bus, unsigned int devfn, int reg,
int size, u32 * val)
{
int res;
/*
* on bus 0 we need to check, whether there is a device answering
* for the devfn by doing a config write and checking the result. If
* we don't do it, we will get a data bus error
*/
if (bus->number == 0) {
pcit_set_config_address(0, 0, 0x68);
outl(inl(0xcfc) | 0xc0000000, 0xcfc);
if ((res = pcit_set_config_address(0, devfn, 0)))
return res;
outl(0xffffffff, 0xcfc);
pcit_set_config_address(0, 0, 0x68);
if (inl(0xcfc) & 0x100000)
return PCIBIOS_DEVICE_NOT_FOUND;
}
if ((res = pcit_set_config_address(bus->number, devfn, reg)))
return res;
switch (size) {
case 1:
*val = inb(PCIMT_CONFIG_DATA + (reg & 3));
break;
case 2:
*val = inw(PCIMT_CONFIG_DATA + (reg & 2));
break;
case 4:
*val = inl(PCIMT_CONFIG_DATA);
break;
}
return 0;
}
static int pcit_write(struct pci_bus *bus, unsigned int devfn, int reg,
int size, u32 val)
{
int res;
if ((res = pcit_set_config_address(bus->number, devfn, reg)))
return res;
switch (size) {
case 1:
outb(val, PCIMT_CONFIG_DATA + (reg & 3));
break;
case 2:
outw(val, PCIMT_CONFIG_DATA + (reg & 2));
break;
case 4:
outl(val, PCIMT_CONFIG_DATA);
break;
}
return 0;
}
struct pci_ops sni_pcit_ops = {
.read = pcit_read,
.write = pcit_write,
};

231
arch/mips/pci/ops-tx3927.c Normal file
View file

@ -0,0 +1,231 @@
/*
* 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 TX3927.
*
* Much of the code is derived from the original DDB5074 port by
* Geert Uytterhoeven <geert@linux-m68k.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 <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/addrspace.h>
#include <asm/txx9irq.h>
#include <asm/txx9/pci.h>
#include <asm/txx9/tx3927.h>
static int mkaddr(struct pci_bus *bus, unsigned char devfn, unsigned char where)
{
if (bus->parent == NULL &&
devfn >= PCI_DEVFN(TX3927_PCIC_MAX_DEVNU, 0))
return -1;
tx3927_pcicptr->ica =
((bus->number & 0xff) << 0x10) |
((devfn & 0xff) << 0x08) |
(where & 0xfc) | (bus->parent ? 1 : 0);
/* 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 0;
}
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;
/* flush write buffer */
iob();
return PCIBIOS_DEVICE_NOT_FOUND;
}
return PCIBIOS_SUCCESSFUL;
}
static int tx3927_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 * val)
{
if (mkaddr(bus, devfn, where)) {
*val = 0xffffffff;
return PCIBIOS_DEVICE_NOT_FOUND;
}
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 tx3927_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
if (mkaddr(bus, devfn, where))
return PCIBIOS_DEVICE_NOT_FOUND;
switch (size) {
case 1:
*(volatile u8 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 3)) = val;
break;
case 2:
*(volatile u16 *) ((unsigned long) & tx3927_pcicptr->icd | (where & 2)) =
cpu_to_le16(val);
break;
case 4:
tx3927_pcicptr->icd = cpu_to_le32(val);
}
return check_abort();
}
static struct pci_ops tx3927_pci_ops = {
.read = tx3927_pci_read_config,
.write = tx3927_pci_write_config,
};
void __init tx3927_pcic_setup(struct pci_controller *channel,
unsigned long sdram_size, int extarb)
{
unsigned long flags;
unsigned long io_base =
channel->io_resource->start + mips_io_port_base - IO_BASE;
unsigned long io_size =
channel->io_resource->end - channel->io_resource->start;
unsigned long io_pciaddr =
channel->io_resource->start - channel->io_offset;
unsigned long mem_base =
channel->mem_resource->start;
unsigned long mem_size =
channel->mem_resource->end - channel->mem_resource->start;
unsigned long mem_pciaddr =
channel->mem_resource->start - channel->mem_offset;
printk(KERN_INFO "TX3927 PCIC -- DID:%04x VID:%04x RID:%02x Arbiter:%s",
tx3927_pcicptr->did, tx3927_pcicptr->vid,
tx3927_pcicptr->rid,
extarb ? "External" : "Internal");
channel->pci_ops = &tx3927_pci_ops;
local_irq_save(flags);
/* Disable External PCI Config. Access */
tx3927_pcicptr->lbc = TX3927_PCIC_LBC_EPCAD;
#ifdef __BIG_ENDIAN
tx3927_pcicptr->lbc |= TX3927_PCIC_LBC_IBSE |
TX3927_PCIC_LBC_TIBSE |
TX3927_PCIC_LBC_TMFBSE | TX3927_PCIC_LBC_MSDSE;
#endif
/* LB->PCI mappings */
tx3927_pcicptr->iomas = ~(io_size - 1);
tx3927_pcicptr->ilbioma = io_base;
tx3927_pcicptr->ipbioma = io_pciaddr;
tx3927_pcicptr->mmas = ~(mem_size - 1);
tx3927_pcicptr->ilbmma = mem_base;
tx3927_pcicptr->ipbmma = mem_pciaddr;
/* PCI->LB mappings */
tx3927_pcicptr->iobas = 0xffffffff;
tx3927_pcicptr->ioba = 0;
tx3927_pcicptr->tlbioma = 0;
tx3927_pcicptr->mbas = ~(sdram_size - 1);
tx3927_pcicptr->mba = 0;
tx3927_pcicptr->tlbmma = 0;
/* Enable Direct mapping Address Space Decoder */
tx3927_pcicptr->lbc |= TX3927_PCIC_LBC_ILMDE | TX3927_PCIC_LBC_ILIDE;
/* Clear All Local Bus Status */
tx3927_pcicptr->lbstat = TX3927_PCIC_LBIM_ALL;
/* Enable All Local Bus Interrupts */
tx3927_pcicptr->lbim = TX3927_PCIC_LBIM_ALL;
/* Clear All PCI Status Error */
tx3927_pcicptr->pcistat = TX3927_PCIC_PCISTATIM_ALL;
/* Enable All PCI Status Error Interrupts */
tx3927_pcicptr->pcistatim = TX3927_PCIC_PCISTATIM_ALL;
/* PCIC Int => IRC IRQ10 */
tx3927_pcicptr->il = TX3927_IR_PCI;
/* Target Control (per errata) */
tx3927_pcicptr->tc = TX3927_PCIC_TC_OF8E | TX3927_PCIC_TC_IF8E;
/* Enable Bus Arbiter */
if (!extarb)
tx3927_pcicptr->pbapmc = TX3927_PCIC_PBAPMC_PBAEN;
tx3927_pcicptr->pcicmd = PCI_COMMAND_MASTER |
PCI_COMMAND_MEMORY |
PCI_COMMAND_IO |
PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
local_irq_restore(flags);
}
static irqreturn_t tx3927_pcierr_interrupt(int irq, void *dev_id)
{
struct pt_regs *regs = get_irq_regs();
if (txx9_pci_err_action != TXX9_PCI_ERR_IGNORE) {
printk(KERN_WARNING "PCI error interrupt at 0x%08lx.\n",
regs->cp0_epc);
printk(KERN_WARNING "pcistat:%02x, lbstat:%04lx\n",
tx3927_pcicptr->pcistat, tx3927_pcicptr->lbstat);
}
if (txx9_pci_err_action != TXX9_PCI_ERR_PANIC) {
/* clear all pci errors */
tx3927_pcicptr->pcistat |= TX3927_PCIC_PCISTATIM_ALL;
tx3927_pcicptr->istat = TX3927_PCIC_IIM_ALL;
tx3927_pcicptr->tstat = TX3927_PCIC_TIM_ALL;
tx3927_pcicptr->lbstat = TX3927_PCIC_LBIM_ALL;
return IRQ_HANDLED;
}
console_verbose();
panic("PCI error.");
}
void __init tx3927_setup_pcierr_irq(void)
{
if (request_irq(TXX9_IRQ_BASE + TX3927_IR_PCI,
tx3927_pcierr_interrupt,
0, "PCI error",
(void *)TX3927_PCIC_REG))
printk(KERN_WARNING "Failed to request irq for PCIERR\n");
}

528
arch/mips/pci/ops-tx4927.c Normal file
View file

@ -0,0 +1,528 @@
/*
* Define the pci_ops for the PCIC on Toshiba TX4927, TX4938, etc.
*
* Based on linux/arch/mips/pci/ops-tx4938.c,
* linux/arch/mips/pci/fixup-rbtx4938.c,
* linux/arch/mips/txx9/rbtx4938/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
* 2003-2005 (c) MontaVista Software, Inc.
* Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
* (C) Copyright TOSHIBA CORPORATION 2000-2001, 2004-2007
*
* 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/interrupt.h>
#include <linux/irq.h>
#include <asm/txx9/pci.h>
#include <asm/txx9/tx4927pcic.h>
static struct {
struct pci_controller *channel;
struct tx4927_pcic_reg __iomem *pcicptr;
} pcicptrs[2]; /* TX4938 has 2 pcic */
static void __init set_tx4927_pcicptr(struct pci_controller *channel,
struct tx4927_pcic_reg __iomem *pcicptr)
{
int i;
for (i = 0; i < ARRAY_SIZE(pcicptrs); i++) {
if (pcicptrs[i].channel == channel) {
pcicptrs[i].pcicptr = pcicptr;
return;
}
}
for (i = 0; i < ARRAY_SIZE(pcicptrs); i++) {
if (!pcicptrs[i].channel) {
pcicptrs[i].channel = channel;
pcicptrs[i].pcicptr = pcicptr;
return;
}
}
BUG();
}
struct tx4927_pcic_reg __iomem *get_tx4927_pcicptr(
struct pci_controller *channel)
{
int i;
for (i = 0; i < ARRAY_SIZE(pcicptrs); i++) {
if (pcicptrs[i].channel == channel)
return pcicptrs[i].pcicptr;
}
return NULL;
}
static int mkaddr(struct pci_bus *bus, unsigned int devfn, int where,
struct tx4927_pcic_reg __iomem *pcicptr)
{
if (bus->parent == NULL &&
devfn >= PCI_DEVFN(TX4927_PCIC_MAX_DEVNU, 0))
return -1;
__raw_writel(((bus->number & 0xff) << 0x10)
| ((devfn & 0xff) << 0x08) | (where & 0xfc)
| (bus->parent ? 1 : 0),
&pcicptr->g2pcfgadrs);
/* clear M_ABORT and Disable M_ABORT Int. */
__raw_writel((__raw_readl(&pcicptr->pcistatus) & 0x0000ffff)
| (PCI_STATUS_REC_MASTER_ABORT << 16),
&pcicptr->pcistatus);
return 0;
}
static int check_abort(struct tx4927_pcic_reg __iomem *pcicptr)
{
int code = PCIBIOS_SUCCESSFUL;
/* wait write cycle completion before checking error status */
while (__raw_readl(&pcicptr->pcicstatus) & TX4927_PCIC_PCICSTATUS_IWB)
;
if (__raw_readl(&pcicptr->pcistatus)
& (PCI_STATUS_REC_MASTER_ABORT << 16)) {
__raw_writel((__raw_readl(&pcicptr->pcistatus) & 0x0000ffff)
| (PCI_STATUS_REC_MASTER_ABORT << 16),
&pcicptr->pcistatus);
/* flush write buffer */
iob();
code = PCIBIOS_DEVICE_NOT_FOUND;
}
return code;
}
static u8 icd_readb(int offset, struct tx4927_pcic_reg __iomem *pcicptr)
{
#ifdef __BIG_ENDIAN
offset ^= 3;
#endif
return __raw_readb((void __iomem *)&pcicptr->g2pcfgdata + offset);
}
static u16 icd_readw(int offset, struct tx4927_pcic_reg __iomem *pcicptr)
{
#ifdef __BIG_ENDIAN
offset ^= 2;
#endif
return __raw_readw((void __iomem *)&pcicptr->g2pcfgdata + offset);
}
static u32 icd_readl(struct tx4927_pcic_reg __iomem *pcicptr)
{
return __raw_readl(&pcicptr->g2pcfgdata);
}
static void icd_writeb(u8 val, int offset,
struct tx4927_pcic_reg __iomem *pcicptr)
{
#ifdef __BIG_ENDIAN
offset ^= 3;
#endif
__raw_writeb(val, (void __iomem *)&pcicptr->g2pcfgdata + offset);
}
static void icd_writew(u16 val, int offset,
struct tx4927_pcic_reg __iomem *pcicptr)
{
#ifdef __BIG_ENDIAN
offset ^= 2;
#endif
__raw_writew(val, (void __iomem *)&pcicptr->g2pcfgdata + offset);
}
static void icd_writel(u32 val, struct tx4927_pcic_reg __iomem *pcicptr)
{
__raw_writel(val, &pcicptr->g2pcfgdata);
}
static struct tx4927_pcic_reg __iomem *pci_bus_to_pcicptr(struct pci_bus *bus)
{
struct pci_controller *channel = bus->sysdata;
return get_tx4927_pcicptr(channel);
}
static int tx4927_pci_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
struct tx4927_pcic_reg __iomem *pcicptr = pci_bus_to_pcicptr(bus);
if (mkaddr(bus, devfn, where, pcicptr)) {
*val = 0xffffffff;
return -1;
}
switch (size) {
case 1:
*val = icd_readb(where & 3, pcicptr);
break;
case 2:
*val = icd_readw(where & 3, pcicptr);
break;
default:
*val = icd_readl(pcicptr);
}
return check_abort(pcicptr);
}
static int tx4927_pci_config_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct tx4927_pcic_reg __iomem *pcicptr = pci_bus_to_pcicptr(bus);
if (mkaddr(bus, devfn, where, pcicptr))
return -1;
switch (size) {
case 1:
icd_writeb(val, where & 3, pcicptr);
break;
case 2:
icd_writew(val, where & 3, pcicptr);
break;
default:
icd_writel(val, pcicptr);
}
return check_abort(pcicptr);
}
static struct pci_ops tx4927_pci_ops = {
.read = tx4927_pci_config_read,
.write = tx4927_pci_config_write,
};
static struct {
u8 trdyto;
u8 retryto;
u16 gbwc;
} tx4927_pci_opts = {
.trdyto = 0,
.retryto = 0,
.gbwc = 0xfe0, /* 4064 GBUSCLK for CCFG.GTOT=0b11 */
};
char *tx4927_pcibios_setup(char *str)
{
if (!strncmp(str, "trdyto=", 7)) {
u8 val = 0;
if (kstrtou8(str + 7, 0, &val) == 0)
tx4927_pci_opts.trdyto = val;
return NULL;
}
if (!strncmp(str, "retryto=", 8)) {
u8 val = 0;
if (kstrtou8(str + 8, 0, &val) == 0)
tx4927_pci_opts.retryto = val;
return NULL;
}
if (!strncmp(str, "gbwc=", 5)) {
u16 val;
if (kstrtou16(str + 5, 0, &val) == 0)
tx4927_pci_opts.gbwc = val;
return NULL;
}
return str;
}
void __init tx4927_pcic_setup(struct tx4927_pcic_reg __iomem *pcicptr,
struct pci_controller *channel, int extarb)
{
int i;
unsigned long flags;
set_tx4927_pcicptr(channel, pcicptr);
if (!channel->pci_ops)
printk(KERN_INFO
"PCIC -- DID:%04x VID:%04x RID:%02x Arbiter:%s\n",
__raw_readl(&pcicptr->pciid) >> 16,
__raw_readl(&pcicptr->pciid) & 0xffff,
__raw_readl(&pcicptr->pciccrev) & 0xff,
extarb ? "External" : "Internal");
channel->pci_ops = &tx4927_pci_ops;
local_irq_save(flags);
/* Disable All Initiator Space */
__raw_writel(__raw_readl(&pcicptr->pciccfg)
& ~(TX4927_PCIC_PCICCFG_G2PMEN(0)
| TX4927_PCIC_PCICCFG_G2PMEN(1)
| TX4927_PCIC_PCICCFG_G2PMEN(2)
| TX4927_PCIC_PCICCFG_G2PIOEN),
&pcicptr->pciccfg);
/* GB->PCI mappings */
__raw_writel((channel->io_resource->end - channel->io_resource->start)
>> 4,
&pcicptr->g2piomask);
____raw_writeq((channel->io_resource->start +
channel->io_map_base - IO_BASE) |
#ifdef __BIG_ENDIAN
TX4927_PCIC_G2PIOGBASE_ECHG
#else
TX4927_PCIC_G2PIOGBASE_BSDIS
#endif
, &pcicptr->g2piogbase);
____raw_writeq(channel->io_resource->start - channel->io_offset,
&pcicptr->g2piopbase);
for (i = 0; i < 3; i++) {
__raw_writel(0, &pcicptr->g2pmmask[i]);
____raw_writeq(0, &pcicptr->g2pmgbase[i]);
____raw_writeq(0, &pcicptr->g2pmpbase[i]);
}
if (channel->mem_resource->end) {
__raw_writel((channel->mem_resource->end
- channel->mem_resource->start) >> 4,
&pcicptr->g2pmmask[0]);
____raw_writeq(channel->mem_resource->start |
#ifdef __BIG_ENDIAN
TX4927_PCIC_G2PMnGBASE_ECHG
#else
TX4927_PCIC_G2PMnGBASE_BSDIS
#endif
, &pcicptr->g2pmgbase[0]);
____raw_writeq(channel->mem_resource->start -
channel->mem_offset,
&pcicptr->g2pmpbase[0]);
}
/* PCI->GB mappings (I/O 256B) */
__raw_writel(0, &pcicptr->p2giopbase); /* 256B */
____raw_writeq(0, &pcicptr->p2giogbase);
/* PCI->GB mappings (MEM 512MB (64MB on R1.x)) */
__raw_writel(0, &pcicptr->p2gm0plbase);
__raw_writel(0, &pcicptr->p2gm0pubase);
____raw_writeq(TX4927_PCIC_P2GMnGBASE_TMEMEN |
#ifdef __BIG_ENDIAN
TX4927_PCIC_P2GMnGBASE_TECHG
#else
TX4927_PCIC_P2GMnGBASE_TBSDIS
#endif
, &pcicptr->p2gmgbase[0]);
/* PCI->GB mappings (MEM 16MB) */
__raw_writel(0xffffffff, &pcicptr->p2gm1plbase);
__raw_writel(0xffffffff, &pcicptr->p2gm1pubase);
____raw_writeq(0, &pcicptr->p2gmgbase[1]);
/* PCI->GB mappings (MEM 1MB) */
__raw_writel(0xffffffff, &pcicptr->p2gm2pbase); /* 1MB */
____raw_writeq(0, &pcicptr->p2gmgbase[2]);
/* Clear all (including IRBER) except for GBWC */
__raw_writel((tx4927_pci_opts.gbwc << 16)
& TX4927_PCIC_PCICCFG_GBWC_MASK,
&pcicptr->pciccfg);
/* Enable Initiator Memory Space */
if (channel->mem_resource->end)
__raw_writel(__raw_readl(&pcicptr->pciccfg)
| TX4927_PCIC_PCICCFG_G2PMEN(0),
&pcicptr->pciccfg);
/* Enable Initiator I/O Space */
if (channel->io_resource->end)
__raw_writel(__raw_readl(&pcicptr->pciccfg)
| TX4927_PCIC_PCICCFG_G2PIOEN,
&pcicptr->pciccfg);
/* Enable Initiator Config */
__raw_writel(__raw_readl(&pcicptr->pciccfg)
| TX4927_PCIC_PCICCFG_ICAEN | TX4927_PCIC_PCICCFG_TCAR,
&pcicptr->pciccfg);
/* Do not use MEMMUL, MEMINF: YMFPCI card causes M_ABORT. */
__raw_writel(0, &pcicptr->pcicfg1);
__raw_writel((__raw_readl(&pcicptr->g2ptocnt) & ~0xffff)
| (tx4927_pci_opts.trdyto & 0xff)
| ((tx4927_pci_opts.retryto & 0xff) << 8),
&pcicptr->g2ptocnt);
/* Clear All Local Bus Status */
__raw_writel(TX4927_PCIC_PCICSTATUS_ALL, &pcicptr->pcicstatus);
/* Enable All Local Bus Interrupts */
__raw_writel(TX4927_PCIC_PCICSTATUS_ALL, &pcicptr->pcicmask);
/* Clear All Initiator Status */
__raw_writel(TX4927_PCIC_G2PSTATUS_ALL, &pcicptr->g2pstatus);
/* Enable All Initiator Interrupts */
__raw_writel(TX4927_PCIC_G2PSTATUS_ALL, &pcicptr->g2pmask);
/* Clear All PCI Status Error */
__raw_writel((__raw_readl(&pcicptr->pcistatus) & 0x0000ffff)
| (TX4927_PCIC_PCISTATUS_ALL << 16),
&pcicptr->pcistatus);
/* Enable All PCI Status Error Interrupts */
__raw_writel(TX4927_PCIC_PCISTATUS_ALL, &pcicptr->pcimask);
if (!extarb) {
/* Reset Bus Arbiter */
__raw_writel(TX4927_PCIC_PBACFG_RPBA, &pcicptr->pbacfg);
__raw_writel(0, &pcicptr->pbabm);
/* Enable Bus Arbiter */
__raw_writel(TX4927_PCIC_PBACFG_PBAEN, &pcicptr->pbacfg);
}
__raw_writel(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY
| PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
&pcicptr->pcistatus);
local_irq_restore(flags);
printk(KERN_DEBUG
"PCI: COMMAND=%04x,PCIMASK=%04x,"
"TRDYTO=%02x,RETRYTO=%02x,GBWC=%03x\n",
__raw_readl(&pcicptr->pcistatus) & 0xffff,
__raw_readl(&pcicptr->pcimask) & 0xffff,
__raw_readl(&pcicptr->g2ptocnt) & 0xff,
(__raw_readl(&pcicptr->g2ptocnt) & 0xff00) >> 8,
(__raw_readl(&pcicptr->pciccfg) >> 16) & 0xfff);
}
static void tx4927_report_pcic_status1(struct tx4927_pcic_reg __iomem *pcicptr)
{
__u16 pcistatus = (__u16)(__raw_readl(&pcicptr->pcistatus) >> 16);
__u32 g2pstatus = __raw_readl(&pcicptr->g2pstatus);
__u32 pcicstatus = __raw_readl(&pcicptr->pcicstatus);
static struct {
__u32 flag;
const char *str;
} pcistat_tbl[] = {
{ PCI_STATUS_DETECTED_PARITY, "DetectedParityError" },
{ PCI_STATUS_SIG_SYSTEM_ERROR, "SignaledSystemError" },
{ PCI_STATUS_REC_MASTER_ABORT, "ReceivedMasterAbort" },
{ PCI_STATUS_REC_TARGET_ABORT, "ReceivedTargetAbort" },
{ PCI_STATUS_SIG_TARGET_ABORT, "SignaledTargetAbort" },
{ PCI_STATUS_PARITY, "MasterParityError" },
}, g2pstat_tbl[] = {
{ TX4927_PCIC_G2PSTATUS_TTOE, "TIOE" },
{ TX4927_PCIC_G2PSTATUS_RTOE, "RTOE" },
}, pcicstat_tbl[] = {
{ TX4927_PCIC_PCICSTATUS_PME, "PME" },
{ TX4927_PCIC_PCICSTATUS_TLB, "TLB" },
{ TX4927_PCIC_PCICSTATUS_NIB, "NIB" },
{ TX4927_PCIC_PCICSTATUS_ZIB, "ZIB" },
{ TX4927_PCIC_PCICSTATUS_PERR, "PERR" },
{ TX4927_PCIC_PCICSTATUS_SERR, "SERR" },
{ TX4927_PCIC_PCICSTATUS_GBE, "GBE" },
{ TX4927_PCIC_PCICSTATUS_IWB, "IWB" },
};
int i, cont;
printk(KERN_ERR "");
if (pcistatus & TX4927_PCIC_PCISTATUS_ALL) {
printk(KERN_CONT "pcistat:%04x(", pcistatus);
for (i = 0, cont = 0; i < ARRAY_SIZE(pcistat_tbl); i++)
if (pcistatus & pcistat_tbl[i].flag)
printk(KERN_CONT "%s%s",
cont++ ? " " : "", pcistat_tbl[i].str);
printk(KERN_CONT ") ");
}
if (g2pstatus & TX4927_PCIC_G2PSTATUS_ALL) {
printk(KERN_CONT "g2pstatus:%08x(", g2pstatus);
for (i = 0, cont = 0; i < ARRAY_SIZE(g2pstat_tbl); i++)
if (g2pstatus & g2pstat_tbl[i].flag)
printk(KERN_CONT "%s%s",
cont++ ? " " : "", g2pstat_tbl[i].str);
printk(KERN_CONT ") ");
}
if (pcicstatus & TX4927_PCIC_PCICSTATUS_ALL) {
printk(KERN_CONT "pcicstatus:%08x(", pcicstatus);
for (i = 0, cont = 0; i < ARRAY_SIZE(pcicstat_tbl); i++)
if (pcicstatus & pcicstat_tbl[i].flag)
printk(KERN_CONT "%s%s",
cont++ ? " " : "", pcicstat_tbl[i].str);
printk(KERN_CONT ")");
}
printk(KERN_CONT "\n");
}
void tx4927_report_pcic_status(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(pcicptrs); i++) {
if (pcicptrs[i].pcicptr)
tx4927_report_pcic_status1(pcicptrs[i].pcicptr);
}
}
static void tx4927_dump_pcic_settings1(struct tx4927_pcic_reg __iomem *pcicptr)
{
int i;
__u32 __iomem *preg = (__u32 __iomem *)pcicptr;
printk(KERN_INFO "tx4927 pcic (0x%p) settings:", pcicptr);
for (i = 0; i < sizeof(struct tx4927_pcic_reg); i += 4, preg++) {
if (i % 32 == 0) {
printk(KERN_CONT "\n");
printk(KERN_INFO "%04x:", i);
}
/* skip registers with side-effects */
if (i == offsetof(struct tx4927_pcic_reg, g2pintack)
|| i == offsetof(struct tx4927_pcic_reg, g2pspc)
|| i == offsetof(struct tx4927_pcic_reg, g2pcfgadrs)
|| i == offsetof(struct tx4927_pcic_reg, g2pcfgdata)) {
printk(KERN_CONT " XXXXXXXX");
continue;
}
printk(KERN_CONT " %08x", __raw_readl(preg));
}
printk(KERN_CONT "\n");
}
void tx4927_dump_pcic_settings(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(pcicptrs); i++) {
if (pcicptrs[i].pcicptr)
tx4927_dump_pcic_settings1(pcicptrs[i].pcicptr);
}
}
irqreturn_t tx4927_pcierr_interrupt(int irq, void *dev_id)
{
struct pt_regs *regs = get_irq_regs();
struct tx4927_pcic_reg __iomem *pcicptr =
(struct tx4927_pcic_reg __iomem *)(unsigned long)dev_id;
if (txx9_pci_err_action != TXX9_PCI_ERR_IGNORE) {
printk(KERN_WARNING "PCIERR interrupt at 0x%0*lx\n",
(int)(2 * sizeof(unsigned long)), regs->cp0_epc);
tx4927_report_pcic_status1(pcicptr);
}
if (txx9_pci_err_action != TXX9_PCI_ERR_PANIC) {
/* clear all pci errors */
__raw_writel((__raw_readl(&pcicptr->pcistatus) & 0x0000ffff)
| (TX4927_PCIC_PCISTATUS_ALL << 16),
&pcicptr->pcistatus);
__raw_writel(TX4927_PCIC_G2PSTATUS_ALL, &pcicptr->g2pstatus);
__raw_writel(TX4927_PCIC_PBASTATUS_ALL, &pcicptr->pbastatus);
__raw_writel(TX4927_PCIC_PCICSTATUS_ALL, &pcicptr->pcicstatus);
return IRQ_HANDLED;
}
console_verbose();
tx4927_dump_pcic_settings1(pcicptr);
panic("PCI error.");
}
#ifdef CONFIG_TOSHIBA_FPCIB0
static void tx4927_quirk_slc90e66_bridge(struct pci_dev *dev)
{
struct tx4927_pcic_reg __iomem *pcicptr = pci_bus_to_pcicptr(dev->bus);
if (!pcicptr)
return;
if (__raw_readl(&pcicptr->pbacfg) & TX4927_PCIC_PBACFG_PBAEN) {
/* Reset Bus Arbiter */
__raw_writel(TX4927_PCIC_PBACFG_RPBA, &pcicptr->pbacfg);
/*
* swap reqBP and reqXP (raise priority of SLC90E66).
* SLC90E66(PCI-ISA bridge) is connected to REQ2 on
* PCI Backplane board.
*/
__raw_writel(0x72543610, &pcicptr->pbareqport);
__raw_writel(0, &pcicptr->pbabm);
/* Use Fixed ParkMaster (required by SLC90E66) */
__raw_writel(TX4927_PCIC_PBACFG_FIXPA, &pcicptr->pbacfg);
/* Enable Bus Arbiter */
__raw_writel(TX4927_PCIC_PBACFG_FIXPA |
TX4927_PCIC_PBACFG_PBAEN,
&pcicptr->pbacfg);
printk(KERN_INFO "PCI: Use Fixed Park Master (REQPORT %08x)\n",
__raw_readl(&pcicptr->pbareqport));
}
}
#define PCI_DEVICE_ID_EFAR_SLC90E66_0 0x9460
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_0,
tx4927_quirk_slc90e66_bridge);
#endif

126
arch/mips/pci/ops-vr41xx.c Normal file
View file

@ -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 <source@mvista.com>
* Copyright (C) 2004-2005 Yoichi Yuasa <yuasa@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. <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 (void __iomem *)KSEG1ADDR(0x0f000c14)
#define PCICONFAREG (void __iomem *)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,
};

536
arch/mips/pci/pci-alchemy.c Normal file
View file

@ -0,0 +1,536 @@
/*
* Alchemy PCI host mode support.
*
* Copyright 2001-2003, 2007-2008 MontaVista Software Inc.
* Author: MontaVista Software, Inc. <source@mvista.com>
*
* Support for all devices (greater than 16) added by David Gathright.
*/
#include <linux/clk.h>
#include <linux/export.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/syscore_ops.h>
#include <linux/vmalloc.h>
#include <asm/dma-coherence.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/tlbmisc.h>
#ifdef CONFIG_PCI_DEBUG
#define DBG(x...) printk(KERN_DEBUG x)
#else
#define DBG(x...) do {} while (0)
#endif
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
struct alchemy_pci_context {
struct pci_controller alchemy_pci_ctrl; /* leave as first member! */
void __iomem *regs; /* ctrl base */
/* tools for wired entry for config space access */
unsigned long last_elo0;
unsigned long last_elo1;
int wired_entry;
struct vm_struct *pci_cfg_vm;
unsigned long pm[12];
int (*board_map_irq)(const struct pci_dev *d, u8 slot, u8 pin);
int (*board_pci_idsel)(unsigned int devsel, int assert);
};
/* for syscore_ops. There's only one PCI controller on Alchemy chips, so this
* should suffice for now.
*/
static struct alchemy_pci_context *__alchemy_pci_ctx;
/* IO/MEM resources for PCI. Keep the memres in sync with __fixup_bigphys_addr
* in arch/mips/alchemy/common/setup.c
*/
static struct resource alchemy_pci_def_memres = {
.start = ALCHEMY_PCI_MEMWIN_START,
.end = ALCHEMY_PCI_MEMWIN_END,
.name = "PCI memory space",
.flags = IORESOURCE_MEM
};
static struct resource alchemy_pci_def_iores = {
.start = ALCHEMY_PCI_IOWIN_START,
.end = ALCHEMY_PCI_IOWIN_END,
.name = "PCI IO space",
.flags = IORESOURCE_IO
};
static 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);
write_c0_pagemask(pagemask);
write_c0_entryhi(entryhi);
write_c0_entrylo0(entrylo0);
write_c0_entrylo1(entrylo1);
tlb_write_indexed();
write_c0_entryhi(old_ctx);
write_c0_pagemask(old_pagemask);
}
static void alchemy_pci_wired_entry(struct alchemy_pci_context *ctx)
{
ctx->wired_entry = read_c0_wired();
add_wired_entry(0, 0, (unsigned long)ctx->pci_cfg_vm->addr, PM_4K);
ctx->last_elo0 = ctx->last_elo1 = ~0;
}
static int config_access(unsigned char access_type, struct pci_bus *bus,
unsigned int dev_fn, unsigned char where, u32 *data)
{
struct alchemy_pci_context *ctx = bus->sysdata;
unsigned int device = PCI_SLOT(dev_fn);
unsigned int function = PCI_FUNC(dev_fn);
unsigned long offset, status, cfg_base, flags, entryLo0, entryLo1, r;
int error = PCIBIOS_SUCCESSFUL;
if (device > 19) {
*data = 0xffffffff;
return -1;
}
local_irq_save(flags);
r = __raw_readl(ctx->regs + PCI_REG_STATCMD) & 0x0000ffff;
r |= PCI_STATCMD_STATUS(0x2000);
__raw_writel(r, ctx->regs + PCI_REG_STATCMD);
wmb();
/* Allow board vendors to implement their own off-chip IDSEL.
* If it doesn't succeed, may as well bail out at this point.
*/
if (ctx->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;
/* To improve performance, if the current device is the same as
* the last device accessed, we don't touch the TLB.
*/
entryLo0 = (6 << 26) | (cfg_base >> 6) | (2 << 3) | 7;
entryLo1 = (6 << 26) | (cfg_base >> 6) | (0x1000 >> 6) | (2 << 3) | 7;
if ((entryLo0 != ctx->last_elo0) || (entryLo1 != ctx->last_elo1)) {
mod_wired_entry(ctx->wired_entry, entryLo0, entryLo1,
(unsigned long)ctx->pci_cfg_vm->addr, PM_4K);
ctx->last_elo0 = entryLo0;
ctx->last_elo1 = entryLo1;
}
if (access_type == PCI_ACCESS_WRITE)
__raw_writel(*data, ctx->pci_cfg_vm->addr + offset);
else
*data = __raw_readl(ctx->pci_cfg_vm->addr + offset);
wmb();
DBG("alchemy-pci: cfg access %d bus %u dev %u at %x dat %x conf %lx\n",
access_type, bus->number, device, where, *data, offset);
/* check for errors, master abort */
status = __raw_readl(ctx->regs + PCI_REG_STATCMD);
if (status & (1 << 29)) {
*data = 0xffffffff;
error = -1;
DBG("alchemy-pci: master abort on cfg access %d bus %d dev %d\n",
access_type, bus->number, device);
} else if ((status >> 28) & 0xf) {
DBG("alchemy-pci: PCI ERR detected: dev %d, status %lx\n",
device, (status >> 28) & 0xf);
/* clear errors */
__raw_writel(status & 0xf000ffff, ctx->regs + PCI_REG_STATCMD);
*data = 0xffffffff;
error = -1;
}
/* Take away the IDSEL. */
(void)ctx->board_pci_idsel(device, 0);
local_irq_restore(flags);
return error;
}
static int read_config_byte(struct pci_bus *bus, unsigned int devfn,
int where, u8 *val)
{
u32 data;
int 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 = 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)
{
return config_access(PCI_ACCESS_READ, bus, devfn, where, val);
}
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)
{
return config_access(PCI_ACCESS_WRITE, bus, devfn, where, &val);
}
static int alchemy_pci_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 alchemy_pci_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);
}
}
static struct pci_ops alchemy_pci_ops = {
.read = alchemy_pci_read,
.write = alchemy_pci_write,
};
static int alchemy_pci_def_idsel(unsigned int devsel, int assert)
{
return 1; /* success */
}
/* save PCI controller register contents. */
static int alchemy_pci_suspend(void)
{
struct alchemy_pci_context *ctx = __alchemy_pci_ctx;
if (!ctx)
return 0;
ctx->pm[0] = __raw_readl(ctx->regs + PCI_REG_CMEM);
ctx->pm[1] = __raw_readl(ctx->regs + PCI_REG_CONFIG) & 0x0009ffff;
ctx->pm[2] = __raw_readl(ctx->regs + PCI_REG_B2BMASK_CCH);
ctx->pm[3] = __raw_readl(ctx->regs + PCI_REG_B2BBASE0_VID);
ctx->pm[4] = __raw_readl(ctx->regs + PCI_REG_B2BBASE1_SID);
ctx->pm[5] = __raw_readl(ctx->regs + PCI_REG_MWMASK_DEV);
ctx->pm[6] = __raw_readl(ctx->regs + PCI_REG_MWBASE_REV_CCL);
ctx->pm[7] = __raw_readl(ctx->regs + PCI_REG_ID);
ctx->pm[8] = __raw_readl(ctx->regs + PCI_REG_CLASSREV);
ctx->pm[9] = __raw_readl(ctx->regs + PCI_REG_PARAM);
ctx->pm[10] = __raw_readl(ctx->regs + PCI_REG_MBAR);
ctx->pm[11] = __raw_readl(ctx->regs + PCI_REG_TIMEOUT);
return 0;
}
static void alchemy_pci_resume(void)
{
struct alchemy_pci_context *ctx = __alchemy_pci_ctx;
if (!ctx)
return;
__raw_writel(ctx->pm[0], ctx->regs + PCI_REG_CMEM);
__raw_writel(ctx->pm[2], ctx->regs + PCI_REG_B2BMASK_CCH);
__raw_writel(ctx->pm[3], ctx->regs + PCI_REG_B2BBASE0_VID);
__raw_writel(ctx->pm[4], ctx->regs + PCI_REG_B2BBASE1_SID);
__raw_writel(ctx->pm[5], ctx->regs + PCI_REG_MWMASK_DEV);
__raw_writel(ctx->pm[6], ctx->regs + PCI_REG_MWBASE_REV_CCL);
__raw_writel(ctx->pm[7], ctx->regs + PCI_REG_ID);
__raw_writel(ctx->pm[8], ctx->regs + PCI_REG_CLASSREV);
__raw_writel(ctx->pm[9], ctx->regs + PCI_REG_PARAM);
__raw_writel(ctx->pm[10], ctx->regs + PCI_REG_MBAR);
__raw_writel(ctx->pm[11], ctx->regs + PCI_REG_TIMEOUT);
wmb();
__raw_writel(ctx->pm[1], ctx->regs + PCI_REG_CONFIG);
wmb();
/* YAMON on all db1xxx boards wipes the TLB and writes zero to C0_wired
* on resume, making it necessary to recreate it as soon as possible.
*/
ctx->wired_entry = 8191; /* impossibly high value */
alchemy_pci_wired_entry(ctx); /* install it */
}
static struct syscore_ops alchemy_pci_pmops = {
.suspend = alchemy_pci_suspend,
.resume = alchemy_pci_resume,
};
static int alchemy_pci_probe(struct platform_device *pdev)
{
struct alchemy_pci_platdata *pd = pdev->dev.platform_data;
struct alchemy_pci_context *ctx;
void __iomem *virt_io;
unsigned long val;
struct resource *r;
struct clk *c;
int ret;
/* need at least PCI IRQ mapping table */
if (!pd) {
dev_err(&pdev->dev, "need platform data for PCI setup\n");
ret = -ENODEV;
goto out;
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
dev_err(&pdev->dev, "no memory for pcictl context\n");
ret = -ENOMEM;
goto out;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "no pcictl ctrl regs resource\n");
ret = -ENODEV;
goto out1;
}
if (!request_mem_region(r->start, resource_size(r), pdev->name)) {
dev_err(&pdev->dev, "cannot claim pci regs\n");
ret = -ENODEV;
goto out1;
}
c = clk_get(&pdev->dev, "pci_clko");
if (IS_ERR(c)) {
dev_err(&pdev->dev, "unable to find PCI clock\n");
ret = PTR_ERR(c);
goto out2;
}
ret = clk_prepare_enable(c);
if (ret) {
dev_err(&pdev->dev, "cannot enable PCI clock\n");
goto out6;
}
ctx->regs = ioremap_nocache(r->start, resource_size(r));
if (!ctx->regs) {
dev_err(&pdev->dev, "cannot map pci regs\n");
ret = -ENODEV;
goto out5;
}
/* map parts of the PCI IO area */
/* REVISIT: if this changes with a newer variant (doubt it) make this
* a platform resource.
*/
virt_io = ioremap(AU1500_PCI_IO_PHYS_ADDR, 0x00100000);
if (!virt_io) {
dev_err(&pdev->dev, "cannot remap pci io space\n");
ret = -ENODEV;
goto out3;
}
ctx->alchemy_pci_ctrl.io_map_base = (unsigned long)virt_io;
/* Au1500 revisions older than AD have borked coherent PCI */
if ((alchemy_get_cputype() == ALCHEMY_CPU_AU1500) &&
(read_c0_prid() < 0x01030202) && !coherentio) {
val = __raw_readl(ctx->regs + PCI_REG_CONFIG);
val |= PCI_CONFIG_NC;
__raw_writel(val, ctx->regs + PCI_REG_CONFIG);
wmb();
dev_info(&pdev->dev, "non-coherent PCI on Au1500 AA/AB/AC\n");
}
if (pd->board_map_irq)
ctx->board_map_irq = pd->board_map_irq;
if (pd->board_pci_idsel)
ctx->board_pci_idsel = pd->board_pci_idsel;
else
ctx->board_pci_idsel = alchemy_pci_def_idsel;
/* fill in relevant pci_controller members */
ctx->alchemy_pci_ctrl.pci_ops = &alchemy_pci_ops;
ctx->alchemy_pci_ctrl.mem_resource = &alchemy_pci_def_memres;
ctx->alchemy_pci_ctrl.io_resource = &alchemy_pci_def_iores;
/* we can't ioremap the entire pci config space because it's too large,
* nor can we dynamically ioremap it because some drivers use the
* PCI config routines from within atomic contex and that becomes a
* problem in get_vm_area(). Instead we use one wired TLB entry to
* handle all config accesses for all busses.
*/
ctx->pci_cfg_vm = get_vm_area(0x2000, VM_IOREMAP);
if (!ctx->pci_cfg_vm) {
dev_err(&pdev->dev, "unable to get vm area\n");
ret = -ENOMEM;
goto out4;
}
ctx->wired_entry = 8191; /* impossibly high value */
alchemy_pci_wired_entry(ctx); /* install it */
set_io_port_base((unsigned long)ctx->alchemy_pci_ctrl.io_map_base);
/* board may want to modify bits in the config register, do it now */
val = __raw_readl(ctx->regs + PCI_REG_CONFIG);
val &= ~pd->pci_cfg_clr;
val |= pd->pci_cfg_set;
val &= ~PCI_CONFIG_PD; /* clear disable bit */
__raw_writel(val, ctx->regs + PCI_REG_CONFIG);
wmb();
__alchemy_pci_ctx = ctx;
platform_set_drvdata(pdev, ctx);
register_syscore_ops(&alchemy_pci_pmops);
register_pci_controller(&ctx->alchemy_pci_ctrl);
dev_info(&pdev->dev, "PCI controller at %ld MHz\n",
clk_get_rate(c) / 1000000);
return 0;
out4:
iounmap(virt_io);
out3:
iounmap(ctx->regs);
out5:
clk_disable_unprepare(c);
out6:
clk_put(c);
out2:
release_mem_region(r->start, resource_size(r));
out1:
kfree(ctx);
out:
return ret;
}
static struct platform_driver alchemy_pcictl_driver = {
.probe = alchemy_pci_probe,
.driver = {
.name = "alchemy-pci",
.owner = THIS_MODULE,
},
};
static int __init alchemy_pci_init(void)
{
/* Au1500/Au1550 have PCI */
switch (alchemy_get_cputype()) {
case ALCHEMY_CPU_AU1500:
case ALCHEMY_CPU_AU1550:
return platform_driver_register(&alchemy_pcictl_driver);
}
return 0;
}
arch_initcall(alchemy_pci_init);
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
struct alchemy_pci_context *ctx = dev->sysdata;
if (ctx && ctx->board_map_irq)
return ctx->board_map_irq(dev, slot, pin);
return -1;
}
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}

428
arch/mips/pci/pci-ar71xx.c Normal file
View file

@ -0,0 +1,428 @@
/*
* Atheros AR71xx PCI host controller driver
*
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/resource.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/bitops.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include <asm/mach-ath79/ath79.h>
#define AR71XX_PCI_REG_CRP_AD_CBE 0x00
#define AR71XX_PCI_REG_CRP_WRDATA 0x04
#define AR71XX_PCI_REG_CRP_RDDATA 0x08
#define AR71XX_PCI_REG_CFG_AD 0x0c
#define AR71XX_PCI_REG_CFG_CBE 0x10
#define AR71XX_PCI_REG_CFG_WRDATA 0x14
#define AR71XX_PCI_REG_CFG_RDDATA 0x18
#define AR71XX_PCI_REG_PCI_ERR 0x1c
#define AR71XX_PCI_REG_PCI_ERR_ADDR 0x20
#define AR71XX_PCI_REG_AHB_ERR 0x24
#define AR71XX_PCI_REG_AHB_ERR_ADDR 0x28
#define AR71XX_PCI_CRP_CMD_WRITE 0x00010000
#define AR71XX_PCI_CRP_CMD_READ 0x00000000
#define AR71XX_PCI_CFG_CMD_READ 0x0000000a
#define AR71XX_PCI_CFG_CMD_WRITE 0x0000000b
#define AR71XX_PCI_INT_CORE BIT(4)
#define AR71XX_PCI_INT_DEV2 BIT(2)
#define AR71XX_PCI_INT_DEV1 BIT(1)
#define AR71XX_PCI_INT_DEV0 BIT(0)
#define AR71XX_PCI_IRQ_COUNT 5
struct ar71xx_pci_controller {
void __iomem *cfg_base;
spinlock_t lock;
int irq;
int irq_base;
struct pci_controller pci_ctrl;
struct resource io_res;
struct resource mem_res;
};
/* Byte lane enable bits */
static const u8 ar71xx_pci_ble_table[4][4] = {
{0x0, 0xf, 0xf, 0xf},
{0xe, 0xd, 0xb, 0x7},
{0xc, 0xf, 0x3, 0xf},
{0xf, 0xf, 0xf, 0xf},
};
static const u32 ar71xx_pci_read_mask[8] = {
0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0
};
static inline u32 ar71xx_pci_get_ble(int where, int size, int local)
{
u32 t;
t = ar71xx_pci_ble_table[size & 3][where & 3];
BUG_ON(t == 0xf);
t <<= (local) ? 20 : 4;
return t;
}
static inline u32 ar71xx_pci_bus_addr(struct pci_bus *bus, unsigned int devfn,
int where)
{
u32 ret;
if (!bus->number) {
/* type 0 */
ret = (1 << PCI_SLOT(devfn)) | (PCI_FUNC(devfn) << 8) |
(where & ~3);
} else {
/* type 1 */
ret = (bus->number << 16) | (PCI_SLOT(devfn) << 11) |
(PCI_FUNC(devfn) << 8) | (where & ~3) | 1;
}
return ret;
}
static inline struct ar71xx_pci_controller *
pci_bus_to_ar71xx_controller(struct pci_bus *bus)
{
struct pci_controller *hose;
hose = (struct pci_controller *) bus->sysdata;
return container_of(hose, struct ar71xx_pci_controller, pci_ctrl);
}
static int ar71xx_pci_check_error(struct ar71xx_pci_controller *apc, int quiet)
{
void __iomem *base = apc->cfg_base;
u32 pci_err;
u32 ahb_err;
pci_err = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR) & 3;
if (pci_err) {
if (!quiet) {
u32 addr;
addr = __raw_readl(base + AR71XX_PCI_REG_PCI_ERR_ADDR);
pr_crit("ar71xx: %s bus error %d at addr 0x%x\n",
"PCI", pci_err, addr);
}
/* clear PCI error status */
__raw_writel(pci_err, base + AR71XX_PCI_REG_PCI_ERR);
}
ahb_err = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR) & 1;
if (ahb_err) {
if (!quiet) {
u32 addr;
addr = __raw_readl(base + AR71XX_PCI_REG_AHB_ERR_ADDR);
pr_crit("ar71xx: %s bus error %d at addr 0x%x\n",
"AHB", ahb_err, addr);
}
/* clear AHB error status */
__raw_writel(ahb_err, base + AR71XX_PCI_REG_AHB_ERR);
}
return !!(ahb_err | pci_err);
}
static inline void ar71xx_pci_local_write(struct ar71xx_pci_controller *apc,
int where, int size, u32 value)
{
void __iomem *base = apc->cfg_base;
u32 ad_cbe;
value = value << (8 * (where & 3));
ad_cbe = AR71XX_PCI_CRP_CMD_WRITE | (where & ~3);
ad_cbe |= ar71xx_pci_get_ble(where, size, 1);
__raw_writel(ad_cbe, base + AR71XX_PCI_REG_CRP_AD_CBE);
__raw_writel(value, base + AR71XX_PCI_REG_CRP_WRDATA);
}
static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus,
unsigned int devfn,
int where, int size, u32 cmd)
{
struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus);
void __iomem *base = apc->cfg_base;
u32 addr;
addr = ar71xx_pci_bus_addr(bus, devfn, where);
__raw_writel(addr, base + AR71XX_PCI_REG_CFG_AD);
__raw_writel(cmd | ar71xx_pci_get_ble(where, size, 0),
base + AR71XX_PCI_REG_CFG_CBE);
return ar71xx_pci_check_error(apc, 1);
}
static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *value)
{
struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus);
void __iomem *base = apc->cfg_base;
unsigned long flags;
u32 data;
int err;
int ret;
ret = PCIBIOS_SUCCESSFUL;
data = ~0;
spin_lock_irqsave(&apc->lock, flags);
err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
AR71XX_PCI_CFG_CMD_READ);
if (err)
ret = PCIBIOS_DEVICE_NOT_FOUND;
else
data = __raw_readl(base + AR71XX_PCI_REG_CFG_RDDATA);
spin_unlock_irqrestore(&apc->lock, flags);
*value = (data >> (8 * (where & 3))) & ar71xx_pci_read_mask[size & 7];
return ret;
}
static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 value)
{
struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus);
void __iomem *base = apc->cfg_base;
unsigned long flags;
int err;
int ret;
value = value << (8 * (where & 3));
ret = PCIBIOS_SUCCESSFUL;
spin_lock_irqsave(&apc->lock, flags);
err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size,
AR71XX_PCI_CFG_CMD_WRITE);
if (err)
ret = PCIBIOS_DEVICE_NOT_FOUND;
else
__raw_writel(value, base + AR71XX_PCI_REG_CFG_WRDATA);
spin_unlock_irqrestore(&apc->lock, flags);
return ret;
}
static struct pci_ops ar71xx_pci_ops = {
.read = ar71xx_pci_read_config,
.write = ar71xx_pci_write_config,
};
static void ar71xx_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct ar71xx_pci_controller *apc;
void __iomem *base = ath79_reset_base;
u32 pending;
apc = irq_get_handler_data(irq);
pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) &
__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
if (pending & AR71XX_PCI_INT_DEV0)
generic_handle_irq(apc->irq_base + 0);
else if (pending & AR71XX_PCI_INT_DEV1)
generic_handle_irq(apc->irq_base + 1);
else if (pending & AR71XX_PCI_INT_DEV2)
generic_handle_irq(apc->irq_base + 2);
else if (pending & AR71XX_PCI_INT_CORE)
generic_handle_irq(apc->irq_base + 4);
else
spurious_interrupt();
}
static void ar71xx_pci_irq_unmask(struct irq_data *d)
{
struct ar71xx_pci_controller *apc;
unsigned int irq;
void __iomem *base = ath79_reset_base;
u32 t;
apc = irq_data_get_irq_chip_data(d);
irq = d->irq - apc->irq_base;
t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
}
static void ar71xx_pci_irq_mask(struct irq_data *d)
{
struct ar71xx_pci_controller *apc;
unsigned int irq;
void __iomem *base = ath79_reset_base;
u32 t;
apc = irq_data_get_irq_chip_data(d);
irq = d->irq - apc->irq_base;
t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
}
static struct irq_chip ar71xx_pci_irq_chip = {
.name = "AR71XX PCI",
.irq_mask = ar71xx_pci_irq_mask,
.irq_unmask = ar71xx_pci_irq_unmask,
.irq_mask_ack = ar71xx_pci_irq_mask,
};
static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc)
{
void __iomem *base = ath79_reset_base;
int i;
__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS);
BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR71XX_PCI_IRQ_COUNT);
apc->irq_base = ATH79_PCI_IRQ_BASE;
for (i = apc->irq_base;
i < apc->irq_base + AR71XX_PCI_IRQ_COUNT; i++) {
irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip,
handle_level_irq);
irq_set_chip_data(i, apc);
}
irq_set_handler_data(apc->irq, apc);
irq_set_chained_handler(apc->irq, ar71xx_pci_irq_handler);
}
static void ar71xx_pci_reset(void)
{
void __iomem *ddr_base = ath79_ddr_base;
ath79_device_reset_set(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE);
mdelay(100);
ath79_device_reset_clear(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE);
mdelay(100);
__raw_writel(AR71XX_PCI_WIN0_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN0);
__raw_writel(AR71XX_PCI_WIN1_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN1);
__raw_writel(AR71XX_PCI_WIN2_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN2);
__raw_writel(AR71XX_PCI_WIN3_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN3);
__raw_writel(AR71XX_PCI_WIN4_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN4);
__raw_writel(AR71XX_PCI_WIN5_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN5);
__raw_writel(AR71XX_PCI_WIN6_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN6);
__raw_writel(AR71XX_PCI_WIN7_OFFS, ddr_base + AR71XX_DDR_REG_PCI_WIN7);
mdelay(100);
}
static int ar71xx_pci_probe(struct platform_device *pdev)
{
struct ar71xx_pci_controller *apc;
struct resource *res;
u32 t;
apc = devm_kzalloc(&pdev->dev, sizeof(struct ar71xx_pci_controller),
GFP_KERNEL);
if (!apc)
return -ENOMEM;
spin_lock_init(&apc->lock);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base");
apc->cfg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(apc->cfg_base))
return PTR_ERR(apc->cfg_base);
apc->irq = platform_get_irq(pdev, 0);
if (apc->irq < 0)
return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base");
if (!res)
return -EINVAL;
apc->io_res.parent = res;
apc->io_res.name = "PCI IO space";
apc->io_res.start = res->start;
apc->io_res.end = res->end;
apc->io_res.flags = IORESOURCE_IO;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base");
if (!res)
return -EINVAL;
apc->mem_res.parent = res;
apc->mem_res.name = "PCI memory space";
apc->mem_res.start = res->start;
apc->mem_res.end = res->end;
apc->mem_res.flags = IORESOURCE_MEM;
ar71xx_pci_reset();
/* setup COMMAND register */
t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK;
ar71xx_pci_local_write(apc, PCI_COMMAND, 4, t);
/* clear bus errors */
ar71xx_pci_check_error(apc, 1);
ar71xx_pci_irq_init(apc);
apc->pci_ctrl.pci_ops = &ar71xx_pci_ops;
apc->pci_ctrl.mem_resource = &apc->mem_res;
apc->pci_ctrl.io_resource = &apc->io_res;
register_pci_controller(&apc->pci_ctrl);
return 0;
}
static struct platform_driver ar71xx_pci_driver = {
.probe = ar71xx_pci_probe,
.driver = {
.name = "ar71xx-pci",
.owner = THIS_MODULE,
},
};
static int __init ar71xx_pci_init(void)
{
return platform_driver_register(&ar71xx_pci_driver);
}
postcore_initcall(ar71xx_pci_init);

435
arch/mips/pci/pci-ar724x.c Normal file
View file

@ -0,0 +1,435 @@
/*
* Atheros AR724X PCI host controller driver
*
* Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
* Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#define AR724X_PCI_REG_RESET 0x18
#define AR724X_PCI_REG_INT_STATUS 0x4c
#define AR724X_PCI_REG_INT_MASK 0x50
#define AR724X_PCI_RESET_LINK_UP BIT(0)
#define AR724X_PCI_INT_DEV0 BIT(14)
#define AR724X_PCI_IRQ_COUNT 1
#define AR7240_BAR0_WAR_VALUE 0xffff
#define AR724X_PCI_CMD_INIT (PCI_COMMAND_MEMORY | \
PCI_COMMAND_MASTER | \
PCI_COMMAND_INVALIDATE | \
PCI_COMMAND_PARITY | \
PCI_COMMAND_SERR | \
PCI_COMMAND_FAST_BACK)
struct ar724x_pci_controller {
void __iomem *devcfg_base;
void __iomem *ctrl_base;
void __iomem *crp_base;
int irq;
int irq_base;
bool link_up;
bool bar0_is_cached;
u32 bar0_value;
spinlock_t lock;
struct pci_controller pci_controller;
struct resource io_res;
struct resource mem_res;
};
static inline bool ar724x_pci_check_link(struct ar724x_pci_controller *apc)
{
u32 reset;
reset = __raw_readl(apc->ctrl_base + AR724X_PCI_REG_RESET);
return reset & AR724X_PCI_RESET_LINK_UP;
}
static inline struct ar724x_pci_controller *
pci_bus_to_ar724x_controller(struct pci_bus *bus)
{
struct pci_controller *hose;
hose = (struct pci_controller *) bus->sysdata;
return container_of(hose, struct ar724x_pci_controller, pci_controller);
}
static int ar724x_pci_local_write(struct ar724x_pci_controller *apc,
int where, int size, u32 value)
{
unsigned long flags;
void __iomem *base;
u32 data;
int s;
WARN_ON(where & (size - 1));
if (!apc->link_up)
return PCIBIOS_DEVICE_NOT_FOUND;
base = apc->crp_base;
spin_lock_irqsave(&apc->lock, flags);
data = __raw_readl(base + (where & ~3));
switch (size) {
case 1:
s = ((where & 3) * 8);
data &= ~(0xff << s);
data |= ((value & 0xff) << s);
break;
case 2:
s = ((where & 2) * 8);
data &= ~(0xffff << s);
data |= ((value & 0xffff) << s);
break;
case 4:
data = value;
break;
default:
spin_unlock_irqrestore(&apc->lock, flags);
return PCIBIOS_BAD_REGISTER_NUMBER;
}
__raw_writel(data, base + (where & ~3));
/* flush write */
__raw_readl(base + (where & ~3));
spin_unlock_irqrestore(&apc->lock, flags);
return PCIBIOS_SUCCESSFUL;
}
static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where,
int size, uint32_t *value)
{
struct ar724x_pci_controller *apc;
unsigned long flags;
void __iomem *base;
u32 data;
apc = pci_bus_to_ar724x_controller(bus);
if (!apc->link_up)
return PCIBIOS_DEVICE_NOT_FOUND;
if (devfn)
return PCIBIOS_DEVICE_NOT_FOUND;
base = apc->devcfg_base;
spin_lock_irqsave(&apc->lock, flags);
data = __raw_readl(base + (where & ~3));
switch (size) {
case 1:
if (where & 1)
data >>= 8;
if (where & 2)
data >>= 16;
data &= 0xff;
break;
case 2:
if (where & 2)
data >>= 16;
data &= 0xffff;
break;
case 4:
break;
default:
spin_unlock_irqrestore(&apc->lock, flags);
return PCIBIOS_BAD_REGISTER_NUMBER;
}
spin_unlock_irqrestore(&apc->lock, flags);
if (where == PCI_BASE_ADDRESS_0 && size == 4 &&
apc->bar0_is_cached) {
/* use the cached value */
*value = apc->bar0_value;
} else {
*value = data;
}
return PCIBIOS_SUCCESSFUL;
}
static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where,
int size, uint32_t value)
{
struct ar724x_pci_controller *apc;
unsigned long flags;
void __iomem *base;
u32 data;
int s;
apc = pci_bus_to_ar724x_controller(bus);
if (!apc->link_up)
return PCIBIOS_DEVICE_NOT_FOUND;
if (devfn)
return PCIBIOS_DEVICE_NOT_FOUND;
if (soc_is_ar7240() && where == PCI_BASE_ADDRESS_0 && size == 4) {
if (value != 0xffffffff) {
/*
* WAR for a hw issue. If the BAR0 register of the
* device is set to the proper base address, the
* memory space of the device is not accessible.
*
* Cache the intended value so it can be read back,
* and write a SoC specific constant value to the
* BAR0 register in order to make the device memory
* accessible.
*/
apc->bar0_is_cached = true;
apc->bar0_value = value;
value = AR7240_BAR0_WAR_VALUE;
} else {
apc->bar0_is_cached = false;
}
}
base = apc->devcfg_base;
spin_lock_irqsave(&apc->lock, flags);
data = __raw_readl(base + (where & ~3));
switch (size) {
case 1:
s = ((where & 3) * 8);
data &= ~(0xff << s);
data |= ((value & 0xff) << s);
break;
case 2:
s = ((where & 2) * 8);
data &= ~(0xffff << s);
data |= ((value & 0xffff) << s);
break;
case 4:
data = value;
break;
default:
spin_unlock_irqrestore(&apc->lock, flags);
return PCIBIOS_BAD_REGISTER_NUMBER;
}
__raw_writel(data, base + (where & ~3));
/* flush write */
__raw_readl(base + (where & ~3));
spin_unlock_irqrestore(&apc->lock, flags);
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops ar724x_pci_ops = {
.read = ar724x_pci_read,
.write = ar724x_pci_write,
};
static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
u32 pending;
apc = irq_get_handler_data(irq);
base = apc->ctrl_base;
pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) &
__raw_readl(base + AR724X_PCI_REG_INT_MASK);
if (pending & AR724X_PCI_INT_DEV0)
generic_handle_irq(apc->irq_base + 0);
else
spurious_interrupt();
}
static void ar724x_pci_irq_unmask(struct irq_data *d)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
int offset;
u32 t;
apc = irq_data_get_irq_chip_data(d);
base = apc->ctrl_base;
offset = apc->irq_base - d->irq;
switch (offset) {
case 0:
t = __raw_readl(base + AR724X_PCI_REG_INT_MASK);
__raw_writel(t | AR724X_PCI_INT_DEV0,
base + AR724X_PCI_REG_INT_MASK);
/* flush write */
__raw_readl(base + AR724X_PCI_REG_INT_MASK);
}
}
static void ar724x_pci_irq_mask(struct irq_data *d)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
int offset;
u32 t;
apc = irq_data_get_irq_chip_data(d);
base = apc->ctrl_base;
offset = apc->irq_base - d->irq;
switch (offset) {
case 0:
t = __raw_readl(base + AR724X_PCI_REG_INT_MASK);
__raw_writel(t & ~AR724X_PCI_INT_DEV0,
base + AR724X_PCI_REG_INT_MASK);
/* flush write */
__raw_readl(base + AR724X_PCI_REG_INT_MASK);
t = __raw_readl(base + AR724X_PCI_REG_INT_STATUS);
__raw_writel(t | AR724X_PCI_INT_DEV0,
base + AR724X_PCI_REG_INT_STATUS);
/* flush write */
__raw_readl(base + AR724X_PCI_REG_INT_STATUS);
}
}
static struct irq_chip ar724x_pci_irq_chip = {
.name = "AR724X PCI ",
.irq_mask = ar724x_pci_irq_mask,
.irq_unmask = ar724x_pci_irq_unmask,
.irq_mask_ack = ar724x_pci_irq_mask,
};
static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc,
int id)
{
void __iomem *base;
int i;
base = apc->ctrl_base;
__raw_writel(0, base + AR724X_PCI_REG_INT_MASK);
__raw_writel(0, base + AR724X_PCI_REG_INT_STATUS);
apc->irq_base = ATH79_PCI_IRQ_BASE + (id * AR724X_PCI_IRQ_COUNT);
for (i = apc->irq_base;
i < apc->irq_base + AR724X_PCI_IRQ_COUNT; i++) {
irq_set_chip_and_handler(i, &ar724x_pci_irq_chip,
handle_level_irq);
irq_set_chip_data(i, apc);
}
irq_set_handler_data(apc->irq, apc);
irq_set_chained_handler(apc->irq, ar724x_pci_irq_handler);
}
static int ar724x_pci_probe(struct platform_device *pdev)
{
struct ar724x_pci_controller *apc;
struct resource *res;
int id;
id = pdev->id;
if (id == -1)
id = 0;
apc = devm_kzalloc(&pdev->dev, sizeof(struct ar724x_pci_controller),
GFP_KERNEL);
if (!apc)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl_base");
apc->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(apc->ctrl_base))
return PTR_ERR(apc->ctrl_base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base");
apc->devcfg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(apc->devcfg_base))
return PTR_ERR(apc->devcfg_base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crp_base");
apc->crp_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(apc->crp_base))
return PTR_ERR(apc->crp_base);
apc->irq = platform_get_irq(pdev, 0);
if (apc->irq < 0)
return -EINVAL;
spin_lock_init(&apc->lock);
res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base");
if (!res)
return -EINVAL;
apc->io_res.parent = res;
apc->io_res.name = "PCI IO space";
apc->io_res.start = res->start;
apc->io_res.end = res->end;
apc->io_res.flags = IORESOURCE_IO;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base");
if (!res)
return -EINVAL;
apc->mem_res.parent = res;
apc->mem_res.name = "PCI memory space";
apc->mem_res.start = res->start;
apc->mem_res.end = res->end;
apc->mem_res.flags = IORESOURCE_MEM;
apc->pci_controller.pci_ops = &ar724x_pci_ops;
apc->pci_controller.io_resource = &apc->io_res;
apc->pci_controller.mem_resource = &apc->mem_res;
apc->link_up = ar724x_pci_check_link(apc);
if (!apc->link_up)
dev_warn(&pdev->dev, "PCIe link is down\n");
ar724x_pci_irq_init(apc, id);
ar724x_pci_local_write(apc, PCI_COMMAND, 4, AR724X_PCI_CMD_INIT);
register_pci_controller(&apc->pci_controller);
return 0;
}
static struct platform_driver ar724x_pci_driver = {
.probe = ar724x_pci_probe,
.driver = {
.name = "ar724x-pci",
.owner = THIS_MODULE,
},
};
static int __init ar724x_pci_init(void)
{
return platform_driver_register(&ar724x_pci_driver);
}
postcore_initcall(ar724x_pci_init);

268
arch/mips/pci/pci-bcm1480.c Normal file
View file

@ -0,0 +1,268 @@
/*
* Copyright (C) 2001,2002,2005 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.
*/
/*
* BCM1x80/1x55-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.
*
* XXX: AT THIS TIME, ONLY the NATIVE PCI-X INTERFACE IS SUPPORTED.
*/
#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 <linux/vt.h>
#include <asm/sibyte/bcm1480_regs.h>
#include <asm/sibyte/bcm1480_scd.h>
#include <asm/sibyte/board.h>
#include <asm/io.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 PCI_DEVICE_MODE 2
static int bcm1480_bus_status;
#define PCI_BRIDGE_DEVICE 0
/*
* 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(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (pin == 0)
return -1;
return K_BCM1480_INT_PCI_INTA - 1 + pin;
}
/* 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 bcm1480_pci_can_access(struct pci_bus *bus, int devfn)
{
u32 devno;
if (!(bcm1480_bus_status & (PCI_BUS_ENABLED | PCI_DEVICE_MODE)))
return 0;
if (bus->number == 0) {
devno = PCI_SLOT(devfn);
if (bcm1480_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 bcm1480_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 (bcm1480_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 bcm1480_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 (!bcm1480_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 bcm1480_pci_ops = {
bcm1480_pcibios_read,
bcm1480_pcibios_write,
};
static struct resource bcm1480_mem_resource = {
.name = "BCM1480 PCI MEM",
.start = A_BCM1480_PHYS_PCI_MEM_MATCH_BYTES,
.end = A_BCM1480_PHYS_PCI_MEM_MATCH_BYTES + 0xfffffffUL,
.flags = IORESOURCE_MEM,
};
static struct resource bcm1480_io_resource = {
.name = "BCM1480 PCI I/O",
.start = A_BCM1480_PHYS_PCI_IO_MATCH_BYTES,
.end = A_BCM1480_PHYS_PCI_IO_MATCH_BYTES + 0x1ffffffUL,
.flags = IORESOURCE_IO,
};
struct pci_controller bcm1480_controller = {
.pci_ops = &bcm1480_pci_ops,
.mem_resource = &bcm1480_mem_resource,
.io_resource = &bcm1480_io_resource,
.io_offset = A_BCM1480_PHYS_PCI_IO_MATCH_BYTES,
};
static int __init bcm1480_pcibios_init(void)
{
uint32_t cmdreg;
uint64_t reg;
/* CFE will assign PCI resources */
pci_set_flags(PCI_PROBE_ONLY);
/* Avoid ISA compat ranges. */
PCIBIOS_MIN_IO = 0x00008000UL;
PCIBIOS_MIN_MEM = 0x01000000UL;
/* Set I/O resource limits. - unlimited for now to accommodate HT */
ioport_resource.end = 0xffffffffUL;
iomem_resource.end = 0xffffffffUL;
cfg_space = ioremap(A_BCM1480_PHYS_PCI_CFG_MATCH_BITS, 16*1024*1024);
/*
* See if the PCI bus has been configured by the firmware.
*/
reg = __raw_readq(IOADDR(A_SCD_SYSTEM_CFG));
if (!(reg & M_BCM1480_SYS_PCI_HOST)) {
bcm1480_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 1; /* XXX */
}
bcm1480_bus_status |= PCI_BUS_ENABLED;
}
/* turn on ExpMemEn */
cmdreg = READCFG32(CFGOFFSET(0, PCI_DEVFN(PCI_BRIDGE_DEVICE, 0), 0x40));
WRITECFG32(CFGOFFSET(0, PCI_DEVFN(PCI_BRIDGE_DEVICE, 0), 0x40),
cmdreg | 0x10);
cmdreg = READCFG32(CFGOFFSET(0, PCI_DEVFN(PCI_BRIDGE_DEVICE, 0), 0x40));
/*
* 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?
*/
bcm1480_controller.io_map_base = (unsigned long)
ioremap(A_BCM1480_PHYS_PCI_IO_MATCH_BYTES, 65536);
bcm1480_controller.io_map_base -= bcm1480_controller.io_offset;
set_io_port_base(bcm1480_controller.io_map_base);
register_pci_controller(&bcm1480_controller);
#ifdef CONFIG_VGA_CONSOLE
console_lock();
do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES-1, 1);
console_unlock();
#endif
return 0;
}
arch_initcall(bcm1480_pcibios_init);

View file

@ -0,0 +1,216 @@
/*
* Copyright (C) 2001,2002,2005 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.
*/
/*
* BCM1480/1455-specific HT support (looking like PCI)
*
* 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/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/sibyte/bcm1480_regs.h>
#include <asm/sibyte/bcm1480_scd.h>
#include <asm/sibyte/board.h>
#include <asm/io.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 *ht_cfg_space;
#define PCI_BUS_ENABLED 1
#define PCI_DEVICE_MODE 2
static int bcm1480ht_bus_status;
#define PCI_BRIDGE_DEVICE 0
#define HT_BRIDGE_DEVICE 1
/*
* HT's level-sensitive interrupts require EOI, which is generated
* through a 4MB memory-mapped region
*/
unsigned long ht_eoi_space;
/*
* Read/write 32-bit values in config space.
*/
static inline u32 READCFG32(u32 addr)
{
return *(u32 *)(ht_cfg_space + (addr&~3));
}
static inline void WRITECFG32(u32 addr, u32 data)
{
*(u32 *)(ht_cfg_space + (addr & ~3)) = data;
}
/*
* 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 bcm1480ht_can_access(struct pci_bus *bus, int devfn)
{
u32 devno;
if (!(bcm1480ht_bus_status & (PCI_BUS_ENABLED | PCI_DEVICE_MODE)))
return 0;
if (bus->number == 0) {
devno = PCI_SLOT(devfn);
if (bcm1480ht_bus_status & PCI_DEVICE_MODE)
return 0;
}
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 bcm1480ht_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 (bcm1480ht_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 bcm1480ht_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 (!bcm1480ht_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;
}
static int bcm1480ht_pcibios_get_busno(void)
{
return 0;
}
struct pci_ops bcm1480ht_pci_ops = {
.read = bcm1480ht_pcibios_read,
.write = bcm1480ht_pcibios_write,
};
static struct resource bcm1480ht_mem_resource = {
.name = "BCM1480 HT MEM",
.start = A_BCM1480_PHYS_HT_MEM_MATCH_BYTES,
.end = A_BCM1480_PHYS_HT_MEM_MATCH_BYTES + 0x1fffffffUL,
.flags = IORESOURCE_MEM,
};
static struct resource bcm1480ht_io_resource = {
.name = "BCM1480 HT I/O",
.start = A_BCM1480_PHYS_HT_IO_MATCH_BYTES,
.end = A_BCM1480_PHYS_HT_IO_MATCH_BYTES + 0x01ffffffUL,
.flags = IORESOURCE_IO,
};
struct pci_controller bcm1480ht_controller = {
.pci_ops = &bcm1480ht_pci_ops,
.mem_resource = &bcm1480ht_mem_resource,
.io_resource = &bcm1480ht_io_resource,
.index = 1,
.get_busno = bcm1480ht_pcibios_get_busno,
.io_offset = A_BCM1480_PHYS_HT_IO_MATCH_BYTES,
};
static int __init bcm1480ht_pcibios_init(void)
{
ht_cfg_space = ioremap(A_BCM1480_PHYS_HT_CFG_MATCH_BITS, 16*1024*1024);
/* CFE doesn't always init all HT paths, so we always scan */
bcm1480ht_bus_status |= PCI_BUS_ENABLED;
ht_eoi_space = (unsigned long)
ioremap(A_BCM1480_PHYS_HT_SPECIAL_MATCH_BYTES,
4 * 1024 * 1024);
bcm1480ht_controller.io_map_base = (unsigned long)
ioremap(A_BCM1480_PHYS_HT_IO_MATCH_BYTES, 65536);
bcm1480ht_controller.io_map_base -= bcm1480ht_controller.io_offset;
register_pci_controller(&bcm1480ht_controller);
return 0;
}
arch_initcall(bcm1480ht_pcibios_init);

104
arch/mips/pci/pci-bcm47xx.c Normal file
View file

@ -0,0 +1,104 @@
/*
* Copyright (C) 2008 Aurelien Jarno <aurelien@aurel32.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/ssb/ssb.h>
#include <linux/bcma/bcma.h>
#include <bcm47xx.h>
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return 0;
}
#ifdef CONFIG_BCM47XX_SSB
static int bcm47xx_pcibios_plat_dev_init_ssb(struct pci_dev *dev)
{
int res;
u8 slot, pin;
res = ssb_pcibios_plat_dev_init(dev);
if (res < 0) {
printk(KERN_ALERT "PCI: Failed to init device %s\n",
pci_name(dev));
return res;
}
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
slot = PCI_SLOT(dev->devfn);
res = ssb_pcibios_map_irq(dev, slot, pin);
/* IRQ-0 and IRQ-1 are software interrupts. */
if (res < 2) {
printk(KERN_ALERT "PCI: Failed to map IRQ of device %s\n",
pci_name(dev));
return res;
}
dev->irq = res;
return 0;
}
#endif
#ifdef CONFIG_BCM47XX_BCMA
static int bcm47xx_pcibios_plat_dev_init_bcma(struct pci_dev *dev)
{
int res;
res = bcma_core_pci_plat_dev_init(dev);
if (res < 0) {
printk(KERN_ALERT "PCI: Failed to init device %s\n",
pci_name(dev));
return res;
}
res = bcma_core_pci_pcibios_map_irq(dev);
/* IRQ-0 and IRQ-1 are software interrupts. */
if (res < 2) {
printk(KERN_ALERT "PCI: Failed to map IRQ of device %s\n",
pci_name(dev));
return res;
}
dev->irq = res;
return 0;
}
#endif
int pcibios_plat_dev_init(struct pci_dev *dev)
{
#ifdef CONFIG_BCM47XX_SSB
if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_SSB)
return bcm47xx_pcibios_plat_dev_init_ssb(dev);
else
#endif
#ifdef CONFIG_BCM47XX_BCMA
if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA)
return bcm47xx_pcibios_plat_dev_init_bcma(dev);
else
#endif
return 0;
}

351
arch/mips/pci/pci-bcm63xx.c Normal file
View file

@ -0,0 +1,351 @@
/*
* 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) 2008 Maxime Bizon <mbizon@freebox.fr>
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <asm/bootinfo.h>
#include <bcm63xx_reset.h>
#include "pci-bcm63xx.h"
/*
* Allow PCI to be disabled at runtime depending on board nvram
* configuration
*/
int bcm63xx_pci_enabled;
static struct resource bcm_pci_mem_resource = {
.name = "bcm63xx PCI memory space",
.start = BCM_PCI_MEM_BASE_PA,
.end = BCM_PCI_MEM_END_PA,
.flags = IORESOURCE_MEM
};
static struct resource bcm_pci_io_resource = {
.name = "bcm63xx PCI IO space",
.start = BCM_PCI_IO_BASE_PA,
#ifdef CONFIG_CARDBUS
.end = BCM_PCI_IO_HALF_PA,
#else
.end = BCM_PCI_IO_END_PA,
#endif
.flags = IORESOURCE_IO
};
struct pci_controller bcm63xx_controller = {
.pci_ops = &bcm63xx_pci_ops,
.io_resource = &bcm_pci_io_resource,
.mem_resource = &bcm_pci_mem_resource,
};
/*
* We handle cardbus via a fake Cardbus bridge, memory and io spaces
* have to be clearly separated from PCI one since we have different
* memory decoder.
*/
#ifdef CONFIG_CARDBUS
static struct resource bcm_cb_mem_resource = {
.name = "bcm63xx Cardbus memory space",
.start = BCM_CB_MEM_BASE_PA,
.end = BCM_CB_MEM_END_PA,
.flags = IORESOURCE_MEM
};
static struct resource bcm_cb_io_resource = {
.name = "bcm63xx Cardbus IO space",
.start = BCM_PCI_IO_HALF_PA + 1,
.end = BCM_PCI_IO_END_PA,
.flags = IORESOURCE_IO
};
struct pci_controller bcm63xx_cb_controller = {
.pci_ops = &bcm63xx_cb_ops,
.io_resource = &bcm_cb_io_resource,
.mem_resource = &bcm_cb_mem_resource,
};
#endif
static struct resource bcm_pcie_mem_resource = {
.name = "bcm63xx PCIe memory space",
.start = BCM_PCIE_MEM_BASE_PA,
.end = BCM_PCIE_MEM_END_PA,
.flags = IORESOURCE_MEM,
};
static struct resource bcm_pcie_io_resource = {
.name = "bcm63xx PCIe IO space",
.start = 0,
.end = 0,
.flags = 0,
};
struct pci_controller bcm63xx_pcie_controller = {
.pci_ops = &bcm63xx_pcie_ops,
.io_resource = &bcm_pcie_io_resource,
.mem_resource = &bcm_pcie_mem_resource,
};
static u32 bcm63xx_int_cfg_readl(u32 reg)
{
u32 tmp;
tmp = reg & MPI_PCICFGCTL_CFGADDR_MASK;
tmp |= MPI_PCICFGCTL_WRITEEN_MASK;
bcm_mpi_writel(tmp, MPI_PCICFGCTL_REG);
iob();
return bcm_mpi_readl(MPI_PCICFGDATA_REG);
}
static void bcm63xx_int_cfg_writel(u32 val, u32 reg)
{
u32 tmp;
tmp = reg & MPI_PCICFGCTL_CFGADDR_MASK;
tmp |= MPI_PCICFGCTL_WRITEEN_MASK;
bcm_mpi_writel(tmp, MPI_PCICFGCTL_REG);
bcm_mpi_writel(val, MPI_PCICFGDATA_REG);
}
void __iomem *pci_iospace_start;
static void __init bcm63xx_reset_pcie(void)
{
u32 val;
u32 reg;
/* enable SERDES */
if (BCMCPU_IS_6328())
reg = MISC_SERDES_CTRL_6328_REG;
else
reg = MISC_SERDES_CTRL_6362_REG;
val = bcm_misc_readl(reg);
val |= SERDES_PCIE_EN | SERDES_PCIE_EXD_EN;
bcm_misc_writel(val, reg);
/* reset the PCIe core */
bcm63xx_core_set_reset(BCM63XX_RESET_PCIE, 1);
bcm63xx_core_set_reset(BCM63XX_RESET_PCIE_EXT, 1);
mdelay(10);
bcm63xx_core_set_reset(BCM63XX_RESET_PCIE, 0);
mdelay(10);
bcm63xx_core_set_reset(BCM63XX_RESET_PCIE_EXT, 0);
mdelay(200);
}
static struct clk *pcie_clk;
static int __init bcm63xx_register_pcie(void)
{
u32 val;
/* enable clock */
pcie_clk = clk_get(NULL, "pcie");
if (IS_ERR_OR_NULL(pcie_clk))
return -ENODEV;
clk_prepare_enable(pcie_clk);
bcm63xx_reset_pcie();
/* configure the PCIe bridge */
val = bcm_pcie_readl(PCIE_BRIDGE_OPT1_REG);
val |= OPT1_RD_BE_OPT_EN;
val |= OPT1_RD_REPLY_BE_FIX_EN;
val |= OPT1_PCIE_BRIDGE_HOLE_DET_EN;
val |= OPT1_L1_INT_STATUS_MASK_POL;
bcm_pcie_writel(val, PCIE_BRIDGE_OPT1_REG);
/* setup the interrupts */
val = bcm_pcie_readl(PCIE_BRIDGE_RC_INT_MASK_REG);
val |= PCIE_RC_INT_A | PCIE_RC_INT_B | PCIE_RC_INT_C | PCIE_RC_INT_D;
bcm_pcie_writel(val, PCIE_BRIDGE_RC_INT_MASK_REG);
val = bcm_pcie_readl(PCIE_BRIDGE_OPT2_REG);
/* enable credit checking and error checking */
val |= OPT2_TX_CREDIT_CHK_EN;
val |= OPT2_UBUS_UR_DECODE_DIS;
/* set device bus/func for the pcie device */
val |= (PCIE_BUS_DEVICE << OPT2_CFG_TYPE1_BUS_NO_SHIFT);
val |= OPT2_CFG_TYPE1_BD_SEL;
bcm_pcie_writel(val, PCIE_BRIDGE_OPT2_REG);
/* setup class code as bridge */
val = bcm_pcie_readl(PCIE_IDVAL3_REG);
val &= ~IDVAL3_CLASS_CODE_MASK;
val |= (PCI_CLASS_BRIDGE_PCI << IDVAL3_SUBCLASS_SHIFT);
bcm_pcie_writel(val, PCIE_IDVAL3_REG);
/* disable bar1 size */
val = bcm_pcie_readl(PCIE_CONFIG2_REG);
val &= ~CONFIG2_BAR1_SIZE_MASK;
bcm_pcie_writel(val, PCIE_CONFIG2_REG);
/* set bar0 to little endian */
val = (BCM_PCIE_MEM_BASE_PA >> 20) << BASEMASK_BASE_SHIFT;
val |= (BCM_PCIE_MEM_BASE_PA >> 20) << BASEMASK_MASK_SHIFT;
val |= BASEMASK_REMAP_EN;
bcm_pcie_writel(val, PCIE_BRIDGE_BAR0_BASEMASK_REG);
val = (BCM_PCIE_MEM_BASE_PA >> 20) << REBASE_ADDR_BASE_SHIFT;
bcm_pcie_writel(val, PCIE_BRIDGE_BAR0_REBASE_ADDR_REG);
register_pci_controller(&bcm63xx_pcie_controller);
return 0;
}
static int __init bcm63xx_register_pci(void)
{
unsigned int mem_size;
u32 val;
/*
* configuration access are done through IO space, remap 4
* first bytes to access it from CPU.
*
* this means that no io access from CPU should happen while
* we do a configuration cycle, but there's no way we can add
* a spinlock for each io access, so this is currently kind of
* broken on SMP.
*/
pci_iospace_start = ioremap_nocache(BCM_PCI_IO_BASE_PA, 4);
if (!pci_iospace_start)
return -ENOMEM;
/* setup local bus to PCI access (PCI memory) */
val = BCM_PCI_MEM_BASE_PA & MPI_L2P_BASE_MASK;
bcm_mpi_writel(val, MPI_L2PMEMBASE1_REG);
bcm_mpi_writel(~(BCM_PCI_MEM_SIZE - 1), MPI_L2PMEMRANGE1_REG);
bcm_mpi_writel(val | MPI_L2PREMAP_ENABLED_MASK, MPI_L2PMEMREMAP1_REG);
/* set Cardbus IDSEL (type 0 cfg access on primary bus for
* this IDSEL will be done on Cardbus instead) */
val = bcm_pcmcia_readl(PCMCIA_C1_REG);
val &= ~PCMCIA_C1_CBIDSEL_MASK;
val |= (CARDBUS_PCI_IDSEL << PCMCIA_C1_CBIDSEL_SHIFT);
bcm_pcmcia_writel(val, PCMCIA_C1_REG);
#ifdef CONFIG_CARDBUS
/* setup local bus to PCI access (Cardbus memory) */
val = BCM_CB_MEM_BASE_PA & MPI_L2P_BASE_MASK;
bcm_mpi_writel(val, MPI_L2PMEMBASE2_REG);
bcm_mpi_writel(~(BCM_CB_MEM_SIZE - 1), MPI_L2PMEMRANGE2_REG);
val |= MPI_L2PREMAP_ENABLED_MASK | MPI_L2PREMAP_IS_CARDBUS_MASK;
bcm_mpi_writel(val, MPI_L2PMEMREMAP2_REG);
#else
/* disable second access windows */
bcm_mpi_writel(0, MPI_L2PMEMREMAP2_REG);
#endif
/* setup local bus to PCI access (IO memory), we have only 1
* IO window for both PCI and cardbus, but it cannot handle
* both at the same time, assume standard PCI for now, if
* cardbus card has IO zone, PCI fixup will change window to
* cardbus */
val = BCM_PCI_IO_BASE_PA & MPI_L2P_BASE_MASK;
bcm_mpi_writel(val, MPI_L2PIOBASE_REG);
bcm_mpi_writel(~(BCM_PCI_IO_SIZE - 1), MPI_L2PIORANGE_REG);
bcm_mpi_writel(val | MPI_L2PREMAP_ENABLED_MASK, MPI_L2PIOREMAP_REG);
/* enable PCI related GPIO pins */
bcm_mpi_writel(MPI_LOCBUSCTL_EN_PCI_GPIO_MASK, MPI_LOCBUSCTL_REG);
/* setup PCI to local bus access, used by PCI device to target
* local RAM while bus mastering */
bcm63xx_int_cfg_writel(0, PCI_BASE_ADDRESS_3);
if (BCMCPU_IS_3368() || BCMCPU_IS_6358() || BCMCPU_IS_6368())
val = MPI_SP0_REMAP_ENABLE_MASK;
else
val = 0;
bcm_mpi_writel(val, MPI_SP0_REMAP_REG);
bcm63xx_int_cfg_writel(0x0, PCI_BASE_ADDRESS_4);
bcm_mpi_writel(0, MPI_SP1_REMAP_REG);
mem_size = bcm63xx_get_memory_size();
/* 6348 before rev b0 exposes only 16 MB of RAM memory through
* PCI, throw a warning if we have more memory */
if (BCMCPU_IS_6348() && (bcm63xx_get_cpu_rev() & 0xf0) == 0xa0) {
if (mem_size > (16 * 1024 * 1024))
printk(KERN_WARNING "bcm63xx: this CPU "
"revision cannot handle more than 16MB "
"of RAM for PCI bus mastering\n");
} else {
/* setup sp0 range to local RAM size */
bcm_mpi_writel(~(mem_size - 1), MPI_SP0_RANGE_REG);
bcm_mpi_writel(0, MPI_SP1_RANGE_REG);
}
/* change host bridge retry counter to infinite number of
* retry, needed for some broadcom wifi cards with Silicon
* Backplane bus where access to srom seems very slow */
val = bcm63xx_int_cfg_readl(BCMPCI_REG_TIMERS);
val &= ~REG_TIMER_RETRY_MASK;
bcm63xx_int_cfg_writel(val, BCMPCI_REG_TIMERS);
/* enable memory decoder and bus mastering */
val = bcm63xx_int_cfg_readl(PCI_COMMAND);
val |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
bcm63xx_int_cfg_writel(val, PCI_COMMAND);
/* enable read prefetching & disable byte swapping for bus
* mastering transfers */
val = bcm_mpi_readl(MPI_PCIMODESEL_REG);
val &= ~MPI_PCIMODESEL_BAR1_NOSWAP_MASK;
val &= ~MPI_PCIMODESEL_BAR2_NOSWAP_MASK;
val &= ~MPI_PCIMODESEL_PREFETCH_MASK;
val |= (8 << MPI_PCIMODESEL_PREFETCH_SHIFT);
bcm_mpi_writel(val, MPI_PCIMODESEL_REG);
/* enable pci interrupt */
val = bcm_mpi_readl(MPI_LOCINT_REG);
val |= MPI_LOCINT_MASK(MPI_LOCINT_EXT_PCI_INT);
bcm_mpi_writel(val, MPI_LOCINT_REG);
register_pci_controller(&bcm63xx_controller);
#ifdef CONFIG_CARDBUS
register_pci_controller(&bcm63xx_cb_controller);
#endif
/* mark memory space used for IO mapping as reserved */
request_mem_region(BCM_PCI_IO_BASE_PA, BCM_PCI_IO_SIZE,
"bcm63xx PCI IO space");
return 0;
}
static int __init bcm63xx_pci_init(void)
{
if (!bcm63xx_pci_enabled)
return -ENODEV;
switch (bcm63xx_get_cpu_id()) {
case BCM6328_CPU_ID:
case BCM6362_CPU_ID:
return bcm63xx_register_pcie();
case BCM3368_CPU_ID:
case BCM6348_CPU_ID:
case BCM6358_CPU_ID:
case BCM6368_CPU_ID:
return bcm63xx_register_pci();
default:
return -ENODEV;
}
}
arch_initcall(bcm63xx_pci_init);

View file

@ -0,0 +1,32 @@
#ifndef PCI_BCM63XX_H_
#define PCI_BCM63XX_H_
#include <bcm63xx_cpu.h>
#include <bcm63xx_io.h>
#include <bcm63xx_regs.h>
#include <bcm63xx_dev_pci.h>
/*
* Cardbus shares the PCI bus, but has no IDSEL, so a special id is
* reserved for it. If you have a standard PCI device at this id, you
* need to change the following definition.
*/
#define CARDBUS_PCI_IDSEL 0x8
#define PCIE_BUS_BRIDGE 0
#define PCIE_BUS_DEVICE 1
/*
* defined in ops-bcm63xx.c
*/
extern struct pci_ops bcm63xx_pci_ops;
extern struct pci_ops bcm63xx_cb_ops;
extern struct pci_ops bcm63xx_pcie_ops;
/*
* defined in pci-bcm63xx.c
*/
extern void __iomem *pci_iospace_start;
#endif /* ! PCI_BCM63XX_H_ */

View file

@ -0,0 +1,85 @@
/*
* Copyright (C) NEC Electronics Corporation 2004-2006
*
* This file is based on the arch/mips/ddb5xxx/ddb5477/pci.c
*
* Copyright 2001 MontaVista Software Inc.
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <asm/bootinfo.h>
#include <asm/emma/emma2rh.h>
static struct resource pci_io_resource = {
.name = "pci IO space",
.start = EMMA2RH_PCI_IO_BASE,
.end = EMMA2RH_PCI_IO_BASE + EMMA2RH_PCI_IO_SIZE - 1,
.flags = IORESOURCE_IO,
};
static struct resource pci_mem_resource = {
.name = "pci memory space",
.start = EMMA2RH_PCI_MEM_BASE,
.end = EMMA2RH_PCI_MEM_BASE + EMMA2RH_PCI_MEM_SIZE - 1,
.flags = IORESOURCE_MEM,
};
extern struct pci_ops emma2rh_pci_ops;
static struct pci_controller emma2rh_pci_controller = {
.pci_ops = &emma2rh_pci_ops,
.mem_resource = &pci_mem_resource,
.io_resource = &pci_io_resource,
.mem_offset = -0x04000000,
.io_offset = 0,
};
static void __init emma2rh_pci_init(void)
{
/* setup PCI interface */
emma2rh_out32(EMMA2RH_PCI_ARBIT_CTR, 0x70f);
emma2rh_out32(EMMA2RH_PCI_IWIN0_CTR, 0x80000a18);
emma2rh_out32(EMMA2RH_PCI_CONFIG_BASE + PCI_COMMAND,
PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_CAP_LIST |
PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
emma2rh_out32(EMMA2RH_PCI_CONFIG_BASE + PCI_BASE_ADDRESS_0, 0x10000000);
emma2rh_out32(EMMA2RH_PCI_CONFIG_BASE + PCI_BASE_ADDRESS_1, 0x00000000);
emma2rh_out32(EMMA2RH_PCI_IWIN0_CTR, 0x12000000 | 0x218);
emma2rh_out32(EMMA2RH_PCI_IWIN1_CTR, 0x18000000 | 0x600);
emma2rh_out32(EMMA2RH_PCI_INIT_ESWP, 0x00000200);
emma2rh_out32(EMMA2RH_PCI_TWIN_CTR, 0x00009200);
emma2rh_out32(EMMA2RH_PCI_TWIN_BADR, 0x00000000);
emma2rh_out32(EMMA2RH_PCI_TWIN0_DADR, 0x00000000);
emma2rh_out32(EMMA2RH_PCI_TWIN1_DADR, 0x00000000);
}
static int __init emma2rh_pci_setup(void)
{
emma2rh_pci_init();
register_pci_controller(&emma2rh_pci_controller);
return 0;
}
arch_initcall(emma2rh_pci_setup);

230
arch/mips/pci/pci-ip27.c Normal file
View file

@ -0,0 +1,230 @@
/*
* 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/kernel.h>
#include <linux/export.h>
#include <linux/pci.h>
#include <linux/smp.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>
/*
* 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];
extern struct pci_ops bridge_pci_ops;
int 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;
pci_set_flags(PCI_PROBE_ONLY);
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 | PCI64_ATTR_BAR;
/*
* 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;
#ifdef CONFIG_PAGE_SIZE_4KB
bridge->b_wid_control &= ~BRIDGE_CTRL_PAGE_SIZE;
#else /* 16kB or larger */
bridge->b_wid_control |= BRIDGE_CTRL_PAGE_SIZE;
#endif
/*
* 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 pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return 0;
}
static inline struct pci_dev *bridge_root_dev(struct pci_dev *dev)
{
while (dev->bus->parent) {
/* Move up the chain of bridges. */
dev = dev->bus->self;
}
return dev;
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
struct bridge_controller *bc = BRIDGE_CONTROLLER(dev->bus);
struct pci_dev *rdev = bridge_root_dev(dev);
int slot = PCI_SLOT(rdev->devfn);
int irq;
irq = bc->pci_int[slot];
if (irq == -1) {
irq = request_bridge_irq(bc);
if (irq < 0)
return irq;
bc->pci_int[slot] = irq;
}
irq_to_bridge[irq] = bc;
irq_to_slot[irq] = slot;
dev->irq = irq;
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 pci_fixup_ioc3(struct pci_dev *d)
{
pci_disable_swapping(d);
}
#ifdef CONFIG_NUMA
int pcibus_to_node(struct pci_bus *bus)
{
struct bridge_controller *bc = BRIDGE_CONTROLLER(bus);
return bc->nasid;
}
EXPORT_SYMBOL(pcibus_to_node);
#endif /* CONFIG_NUMA */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
pci_fixup_ioc3);

148
arch/mips/pci/pci-ip32.c Normal file
View file

@ -0,0 +1,148 @@
/*
* 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/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)
{
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_64BIT
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,
.io_map_base = CKSEG1ADDR(MACEPCI_LOW_IO),
};
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));
/* extend memory resources */
iomem_resource.end = mace_pci_mem_resource.end;
ioport_resource = mace_pci_io_resource;
register_pci_controller(&mace_pci_controller);
return 0;
}
arch_initcall(mace_init);

258
arch/mips/pci/pci-lantiq.c Normal file
View file

@ -0,0 +1,258 @@
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <asm/pci.h>
#include <asm/gpio.h>
#include <asm/addrspace.h>
#include <lantiq_soc.h>
#include <lantiq_irq.h>
#include "pci-lantiq.h"
#define PCI_CR_FCI_ADDR_MAP0 0x00C0
#define PCI_CR_FCI_ADDR_MAP1 0x00C4
#define PCI_CR_FCI_ADDR_MAP2 0x00C8
#define PCI_CR_FCI_ADDR_MAP3 0x00CC
#define PCI_CR_FCI_ADDR_MAP4 0x00D0
#define PCI_CR_FCI_ADDR_MAP5 0x00D4
#define PCI_CR_FCI_ADDR_MAP6 0x00D8
#define PCI_CR_FCI_ADDR_MAP7 0x00DC
#define PCI_CR_CLK_CTRL 0x0000
#define PCI_CR_PCI_MOD 0x0030
#define PCI_CR_PC_ARB 0x0080
#define PCI_CR_FCI_ADDR_MAP11hg 0x00E4
#define PCI_CR_BAR11MASK 0x0044
#define PCI_CR_BAR12MASK 0x0048
#define PCI_CR_BAR13MASK 0x004C
#define PCI_CS_BASE_ADDR1 0x0010
#define PCI_CR_PCI_ADDR_MAP11 0x0064
#define PCI_CR_FCI_BURST_LENGTH 0x00E8
#define PCI_CR_PCI_EOI 0x002C
#define PCI_CS_STS_CMD 0x0004
#define PCI_MASTER0_REQ_MASK_2BITS 8
#define PCI_MASTER1_REQ_MASK_2BITS 10
#define PCI_MASTER2_REQ_MASK_2BITS 12
#define INTERNAL_ARB_ENABLE_BIT 0
#define LTQ_CGU_IFCCR 0x0018
#define LTQ_CGU_PCICR 0x0034
#define ltq_pci_w32(x, y) ltq_w32((x), ltq_pci_membase + (y))
#define ltq_pci_r32(x) ltq_r32(ltq_pci_membase + (x))
#define ltq_pci_cfg_w32(x, y) ltq_w32((x), ltq_pci_mapped_cfg + (y))
#define ltq_pci_cfg_r32(x) ltq_r32(ltq_pci_mapped_cfg + (x))
__iomem void *ltq_pci_mapped_cfg;
static __iomem void *ltq_pci_membase;
static int reset_gpio;
static struct clk *clk_pci, *clk_external;
static struct resource pci_io_resource;
static struct resource pci_mem_resource;
static struct pci_ops pci_ops = {
.read = ltq_pci_read_config_dword,
.write = ltq_pci_write_config_dword
};
static struct pci_controller pci_controller = {
.pci_ops = &pci_ops,
.mem_resource = &pci_mem_resource,
.mem_offset = 0x00000000UL,
.io_resource = &pci_io_resource,
.io_offset = 0x00000000UL,
};
static inline u32 ltq_calc_bar11mask(void)
{
u32 mem, bar11mask;
/* BAR11MASK value depends on available memory on system. */
mem = get_num_physpages() * PAGE_SIZE;
bar11mask = (0x0ffffff0 & ~((1 << (fls(mem) - 1)) - 1)) | 8;
return bar11mask;
}
static int ltq_pci_startup(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
const __be32 *req_mask, *bus_clk;
u32 temp_buffer;
/* get our clocks */
clk_pci = clk_get(&pdev->dev, NULL);
if (IS_ERR(clk_pci)) {
dev_err(&pdev->dev, "failed to get pci clock\n");
return PTR_ERR(clk_pci);
}
clk_external = clk_get(&pdev->dev, "external");
if (IS_ERR(clk_external)) {
clk_put(clk_pci);
dev_err(&pdev->dev, "failed to get external pci clock\n");
return PTR_ERR(clk_external);
}
/* read the bus speed that we want */
bus_clk = of_get_property(node, "lantiq,bus-clock", NULL);
if (bus_clk)
clk_set_rate(clk_pci, *bus_clk);
/* and enable the clocks */
clk_enable(clk_pci);
if (of_find_property(node, "lantiq,external-clock", NULL))
clk_enable(clk_external);
else
clk_disable(clk_external);
/* setup reset gpio used by pci */
reset_gpio = of_get_named_gpio(node, "gpio-reset", 0);
if (gpio_is_valid(reset_gpio)) {
int ret = devm_gpio_request(&pdev->dev,
reset_gpio, "pci-reset");
if (ret) {
dev_err(&pdev->dev,
"failed to request gpio %d\n", reset_gpio);
return ret;
}
gpio_direction_output(reset_gpio, 1);
}
/* enable auto-switching between PCI and EBU */
ltq_pci_w32(0xa, PCI_CR_CLK_CTRL);
/* busy, i.e. configuration is not done, PCI access has to be retried */
ltq_pci_w32(ltq_pci_r32(PCI_CR_PCI_MOD) & ~(1 << 24), PCI_CR_PCI_MOD);
wmb();
/* BUS Master/IO/MEM access */
ltq_pci_cfg_w32(ltq_pci_cfg_r32(PCI_CS_STS_CMD) | 7, PCI_CS_STS_CMD);
/* enable external 2 PCI masters */
temp_buffer = ltq_pci_r32(PCI_CR_PC_ARB);
/* setup the request mask */
req_mask = of_get_property(node, "req-mask", NULL);
if (req_mask)
temp_buffer &= ~((*req_mask & 0xf) << 16);
else
temp_buffer &= ~0xf0000;
/* enable internal arbiter */
temp_buffer |= (1 << INTERNAL_ARB_ENABLE_BIT);
/* enable internal PCI master reqest */
temp_buffer &= (~(3 << PCI_MASTER0_REQ_MASK_2BITS));
/* enable EBU request */
temp_buffer &= (~(3 << PCI_MASTER1_REQ_MASK_2BITS));
/* enable all external masters request */
temp_buffer &= (~(3 << PCI_MASTER2_REQ_MASK_2BITS));
ltq_pci_w32(temp_buffer, PCI_CR_PC_ARB);
wmb();
/* setup BAR memory regions */
ltq_pci_w32(0x18000000, PCI_CR_FCI_ADDR_MAP0);
ltq_pci_w32(0x18400000, PCI_CR_FCI_ADDR_MAP1);
ltq_pci_w32(0x18800000, PCI_CR_FCI_ADDR_MAP2);
ltq_pci_w32(0x18c00000, PCI_CR_FCI_ADDR_MAP3);
ltq_pci_w32(0x19000000, PCI_CR_FCI_ADDR_MAP4);
ltq_pci_w32(0x19400000, PCI_CR_FCI_ADDR_MAP5);
ltq_pci_w32(0x19800000, PCI_CR_FCI_ADDR_MAP6);
ltq_pci_w32(0x19c00000, PCI_CR_FCI_ADDR_MAP7);
ltq_pci_w32(0x1ae00000, PCI_CR_FCI_ADDR_MAP11hg);
ltq_pci_w32(ltq_calc_bar11mask(), PCI_CR_BAR11MASK);
ltq_pci_w32(0, PCI_CR_PCI_ADDR_MAP11);
ltq_pci_w32(0, PCI_CS_BASE_ADDR1);
/* both TX and RX endian swap are enabled */
ltq_pci_w32(ltq_pci_r32(PCI_CR_PCI_EOI) | 3, PCI_CR_PCI_EOI);
wmb();
ltq_pci_w32(ltq_pci_r32(PCI_CR_BAR12MASK) | 0x80000000,
PCI_CR_BAR12MASK);
ltq_pci_w32(ltq_pci_r32(PCI_CR_BAR13MASK) | 0x80000000,
PCI_CR_BAR13MASK);
/*use 8 dw burst length */
ltq_pci_w32(0x303, PCI_CR_FCI_BURST_LENGTH);
ltq_pci_w32(ltq_pci_r32(PCI_CR_PCI_MOD) | (1 << 24), PCI_CR_PCI_MOD);
wmb();
/* setup irq line */
ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_CON) | 0xc, LTQ_EBU_PCC_CON);
ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_IEN) | 0x10, LTQ_EBU_PCC_IEN);
/* toggle reset pin */
if (gpio_is_valid(reset_gpio)) {
__gpio_set_value(reset_gpio, 0);
wmb();
mdelay(1);
__gpio_set_value(reset_gpio, 1);
}
return 0;
}
static int ltq_pci_probe(struct platform_device *pdev)
{
struct resource *res_cfg, *res_bridge;
pci_clear_flags(PCI_PROBE_ONLY);
res_bridge = platform_get_resource(pdev, IORESOURCE_MEM, 1);
ltq_pci_membase = devm_ioremap_resource(&pdev->dev, res_bridge);
if (IS_ERR(ltq_pci_membase))
return PTR_ERR(ltq_pci_membase);
res_cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ltq_pci_mapped_cfg = devm_ioremap_resource(&pdev->dev, res_cfg);
if (IS_ERR(ltq_pci_mapped_cfg))
return PTR_ERR(ltq_pci_mapped_cfg);
ltq_pci_startup(pdev);
pci_load_of_ranges(&pci_controller, pdev->dev.of_node);
register_pci_controller(&pci_controller);
return 0;
}
static const struct of_device_id ltq_pci_match[] = {
{ .compatible = "lantiq,pci-xway" },
{},
};
MODULE_DEVICE_TABLE(of, ltq_pci_match);
static struct platform_driver ltq_pci_driver = {
.probe = ltq_pci_probe,
.driver = {
.name = "pci-xway",
.owner = THIS_MODULE,
.of_match_table = ltq_pci_match,
},
};
int __init pcibios_init(void)
{
int ret = platform_driver_register(&ltq_pci_driver);
if (ret)
pr_info("pci-xway: Error registering platform driver!");
return ret;
}
arch_initcall(pcibios_init);

View file

@ -0,0 +1,18 @@
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*/
#ifndef _LTQ_PCI_H__
#define _LTQ_PCI_H__
extern __iomem void *ltq_pci_mapped_cfg;
extern int ltq_pci_read_config_dword(struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 *val);
extern int ltq_pci_write_config_dword(struct pci_bus *bus,
unsigned int devfn, int where, int size, u32 val);
#endif

88
arch/mips/pci/pci-lasat.c Normal file
View file

@ -0,0 +1,88 @@
/*
* 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/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <asm/lasat/lasat.h>
#include <irq.h>
extern struct pci_ops nile4_pci_ops;
extern struct pci_ops gt64xxx_pci0_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(KERN_DEBUG "PCI: starting\n");
if (IS_LASAT_200())
lasat_pci_controller.pci_ops = &nile4_pci_ops;
else
lasat_pci_controller.pci_ops = &gt64xxx_pci0_ops;
register_pci_controller(&lasat_pci_controller);
return 0;
}
arch_initcall(lasat_pci_setup);
#define LASAT_IRQ_ETH1 (LASAT_IRQ_BASE + 0)
#define LASAT_IRQ_ETH0 (LASAT_IRQ_BASE + 1)
#define LASAT_IRQ_HDC (LASAT_IRQ_BASE + 2)
#define LASAT_IRQ_COMP (LASAT_IRQ_BASE + 3)
#define LASAT_IRQ_HDLC (LASAT_IRQ_BASE + 4)
#define LASAT_IRQ_PCIA (LASAT_IRQ_BASE + 5)
#define LASAT_IRQ_PCIB (LASAT_IRQ_BASE + 6)
#define LASAT_IRQ_PCIC (LASAT_IRQ_BASE + 7)
#define LASAT_IRQ_PCID (LASAT_IRQ_BASE + 8)
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
switch (slot) {
case 1:
case 2:
case 3:
return LASAT_IRQ_PCIA + (((slot-1) + (pin-1)) % 4);
case 4:
return LASAT_IRQ_ETH1; /* Ethernet 1 (LAN 2) */
case 5:
return LASAT_IRQ_ETH0; /* Ethernet 0 (LAN 1) */
case 6:
return LASAT_IRQ_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;
}

254
arch/mips/pci/pci-malta.c Normal file
View file

@ -0,0 +1,254 @@
/*
* 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) 2004 by 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/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/gt64120.h>
#include <asm/mips-cm.h>
#include <asm/mips-boards/generic.h>
#include <asm/mips-boards/bonito64.h>
#include <asm/mips-boards/msc01_pci.h>
static struct resource bonito64_mem_resource = {
.name = "Bonito PCI MEM",
.flags = IORESOURCE_MEM,
};
static struct resource bonito64_io_resource = {
.name = "Bonito PCI I/O",
.start = 0x00000000UL,
.end = 0x000fffffUL,
.flags = IORESOURCE_IO,
};
static struct resource gt64120_mem_resource = {
.name = "GT-64120 PCI MEM",
.flags = IORESOURCE_MEM,
};
static struct resource gt64120_io_resource = {
.name = "GT-64120 PCI I/O",
.flags = IORESOURCE_IO,
};
static struct resource msc_mem_resource = {
.name = "MSC PCI MEM",
.flags = IORESOURCE_MEM,
};
static struct resource msc_io_resource = {
.name = "MSC PCI I/O",
.flags = IORESOURCE_IO,
};
extern struct pci_ops bonito64_pci_ops;
extern struct pci_ops gt64xxx_pci0_ops;
extern struct pci_ops msc_pci_ops;
static struct pci_controller bonito64_controller = {
.pci_ops = &bonito64_pci_ops,
.io_resource = &bonito64_io_resource,
.mem_resource = &bonito64_mem_resource,
.io_offset = 0x00000000UL,
};
static struct pci_controller gt64120_controller = {
.pci_ops = &gt64xxx_pci0_ops,
.io_resource = &gt64120_io_resource,
.mem_resource = &gt64120_mem_resource,
};
static struct pci_controller msc_controller = {
.pci_ops = &msc_pci_ops,
.io_resource = &msc_io_resource,
.mem_resource = &msc_mem_resource,
};
void __init mips_pcibios_init(void)
{
struct pci_controller *controller;
resource_size_t start, end, map, start1, end1, map1, map2, map3, mask;
switch (mips_revision_sconid) {
case MIPS_REVISION_SCON_GT64120:
/*
* Due to a bug in the Galileo system controller, we need
* to setup the PCI BAR for the Galileo internal registers.
* This should be done in the bios/bootprom and will be
* fixed in a later revision of YAMON (the MIPS boards
* boot prom).
*/
GT_WRITE(GT_PCI0_CFGADDR_OFS,
(0 << GT_PCI0_CFGADDR_BUSNUM_SHF) | /* Local bus */
(0 << GT_PCI0_CFGADDR_DEVNUM_SHF) | /* GT64120 dev */
(0 << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | /* Function 0*/
((0x20/4) << GT_PCI0_CFGADDR_REGNUM_SHF) | /* BAR 4*/
GT_PCI0_CFGADDR_CONFIGEN_BIT);
/* Perform the write */
GT_WRITE(GT_PCI0_CFGDATA_OFS, CPHYSADDR(MIPS_GT_BASE));
/* Set up resource ranges from the controller's registers. */
start = GT_READ(GT_PCI0M0LD_OFS);
end = GT_READ(GT_PCI0M0HD_OFS);
map = GT_READ(GT_PCI0M0REMAP_OFS);
end = (end & GT_PCI_HD_MSK) | (start & ~GT_PCI_HD_MSK);
start1 = GT_READ(GT_PCI0M1LD_OFS);
end1 = GT_READ(GT_PCI0M1HD_OFS);
map1 = GT_READ(GT_PCI0M1REMAP_OFS);
end1 = (end1 & GT_PCI_HD_MSK) | (start1 & ~GT_PCI_HD_MSK);
/* Cannot support multiple windows, use the wider. */
if (end1 - start1 > end - start) {
start = start1;
end = end1;
map = map1;
}
mask = ~(start ^ end);
/* We don't support remapping with a discontiguous mask. */
BUG_ON((start & GT_PCI_HD_MSK) != (map & GT_PCI_HD_MSK) &&
mask != ~((mask & -mask) - 1));
gt64120_mem_resource.start = start;
gt64120_mem_resource.end = end;
gt64120_controller.mem_offset = (start & mask) - (map & mask);
/* Addresses are 36-bit, so do shifts in the destinations. */
gt64120_mem_resource.start <<= GT_PCI_DCRM_SHF;
gt64120_mem_resource.end <<= GT_PCI_DCRM_SHF;
gt64120_mem_resource.end |= (1 << GT_PCI_DCRM_SHF) - 1;
gt64120_controller.mem_offset <<= GT_PCI_DCRM_SHF;
start = GT_READ(GT_PCI0IOLD_OFS);
end = GT_READ(GT_PCI0IOHD_OFS);
map = GT_READ(GT_PCI0IOREMAP_OFS);
end = (end & GT_PCI_HD_MSK) | (start & ~GT_PCI_HD_MSK);
mask = ~(start ^ end);
/* We don't support remapping with a discontiguous mask. */
BUG_ON((start & GT_PCI_HD_MSK) != (map & GT_PCI_HD_MSK) &&
mask != ~((mask & -mask) - 1));
gt64120_io_resource.start = map & mask;
gt64120_io_resource.end = (map & mask) | ~mask;
gt64120_controller.io_offset = 0;
/* Addresses are 36-bit, so do shifts in the destinations. */
gt64120_io_resource.start <<= GT_PCI_DCRM_SHF;
gt64120_io_resource.end <<= GT_PCI_DCRM_SHF;
gt64120_io_resource.end |= (1 << GT_PCI_DCRM_SHF) - 1;
controller = &gt64120_controller;
break;
case MIPS_REVISION_SCON_BONITO:
/* Set up resource ranges from the controller's registers. */
map = BONITO_PCIMAP;
map1 = (BONITO_PCIMAP & BONITO_PCIMAP_PCIMAP_LO0) >>
BONITO_PCIMAP_PCIMAP_LO0_SHIFT;
map2 = (BONITO_PCIMAP & BONITO_PCIMAP_PCIMAP_LO1) >>
BONITO_PCIMAP_PCIMAP_LO1_SHIFT;
map3 = (BONITO_PCIMAP & BONITO_PCIMAP_PCIMAP_LO2) >>
BONITO_PCIMAP_PCIMAP_LO2_SHIFT;
/* Combine as many adjacent windows as possible. */
map = map1;
start = BONITO_PCILO0_BASE;
end = 1;
if (map3 == map2 + 1) {
map = map2;
start = BONITO_PCILO1_BASE;
end++;
}
if (map2 == map1 + 1) {
map = map1;
start = BONITO_PCILO0_BASE;
end++;
}
bonito64_mem_resource.start = start;
bonito64_mem_resource.end = start +
BONITO_PCIMAP_WINBASE(end) - 1;
bonito64_controller.mem_offset = start -
BONITO_PCIMAP_WINBASE(map);
controller = &bonito64_controller;
break;
case MIPS_REVISION_SCON_SOCIT:
case MIPS_REVISION_SCON_ROCIT:
case MIPS_REVISION_SCON_SOCITSC:
case MIPS_REVISION_SCON_SOCITSCP:
/* Set up resource ranges from the controller's registers. */
MSC_READ(MSC01_PCI_SC2PMBASL, start);
MSC_READ(MSC01_PCI_SC2PMMSKL, mask);
MSC_READ(MSC01_PCI_SC2PMMAPL, map);
msc_mem_resource.start = start & mask;
msc_mem_resource.end = (start & mask) | ~mask;
msc_controller.mem_offset = (start & mask) - (map & mask);
if (mips_cm_numiocu()) {
write_gcr_reg0_base(start);
write_gcr_reg0_mask(mask |
CM_GCR_REGn_MASK_CMTGT_IOCU0);
}
MSC_READ(MSC01_PCI_SC2PIOBASL, start);
MSC_READ(MSC01_PCI_SC2PIOMSKL, mask);
MSC_READ(MSC01_PCI_SC2PIOMAPL, map);
msc_io_resource.start = map & mask;
msc_io_resource.end = (map & mask) | ~mask;
msc_controller.io_offset = 0;
ioport_resource.end = ~mask;
if (mips_cm_numiocu()) {
write_gcr_reg1_base(start);
write_gcr_reg1_mask(mask |
CM_GCR_REGn_MASK_CMTGT_IOCU0);
}
/* If ranges overlap I/O takes precedence. */
start = start & mask;
end = start | ~mask;
if ((start >= msc_mem_resource.start &&
start <= msc_mem_resource.end) ||
(end >= msc_mem_resource.start &&
end <= msc_mem_resource.end)) {
/* Use the larger space. */
start = max(start, msc_mem_resource.start);
end = min(end, msc_mem_resource.end);
if (start - msc_mem_resource.start >=
msc_mem_resource.end - end)
msc_mem_resource.end = start - 1;
else
msc_mem_resource.start = end + 1;
}
controller = &msc_controller;
break;
default:
return;
}
/* PIIX4 ACPI starts at 0x1000 */
if (controller->io_resource->start < 0x00001000UL)
controller->io_resource->start = 0x00001000UL;
iomem_resource.end &= 0xfffffffffULL; /* 64 GB */
ioport_resource.end = controller->io_resource->end;
controller->io_map_base = mips_io_port_base;
register_pci_controller(controller);
}

714
arch/mips/pci/pci-octeon.c Normal file
View file

@ -0,0 +1,714 @@
/*
* 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) 2005-2009 Cavium Networks
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/swiotlb.h>
#include <asm/time.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-npi-defs.h>
#include <asm/octeon/cvmx-pci-defs.h>
#include <asm/octeon/pci-octeon.h>
#include <dma-coherence.h>
#define USE_OCTEON_INTERNAL_ARBITER
/*
* Octeon's PCI controller uses did=3, subdid=2 for PCI IO
* addresses. Use PCI endian swapping 1 so no address swapping is
* necessary. The Linux io routines will endian swap the data.
*/
#define OCTEON_PCI_IOSPACE_BASE 0x80011a0400000000ull
#define OCTEON_PCI_IOSPACE_SIZE (1ull<<32)
/* Octeon't PCI controller uses did=3, subdid=3 for PCI memory. */
#define OCTEON_PCI_MEMSPACE_OFFSET (0x00011b0000000000ull)
u64 octeon_bar1_pci_phys;
/**
* This is the bit decoding used for the Octeon PCI controller addresses
*/
union octeon_pci_address {
uint64_t u64;
struct {
uint64_t upper:2;
uint64_t reserved:13;
uint64_t io:1;
uint64_t did:5;
uint64_t subdid:3;
uint64_t reserved2:4;
uint64_t endian_swap:2;
uint64_t reserved3:10;
uint64_t bus:8;
uint64_t dev:5;
uint64_t func:3;
uint64_t reg:8;
} s;
};
int __initconst (*octeon_pcibios_map_irq)(const struct pci_dev *dev,
u8 slot, u8 pin);
enum octeon_dma_bar_type octeon_dma_bar_type = OCTEON_DMA_BAR_TYPE_INVALID;
/**
* Map a PCI device to the appropriate interrupt line
*
* @dev: The Linux PCI device structure for the device to map
* @slot: The slot number for this device on __BUS 0__. Linux
* enumerates through all the bridges and figures out the
* slot on Bus 0 where this device eventually hooks to.
* @pin: The PCI interrupt pin read from the device, then swizzled
* as it goes through each bridge.
* Returns Interrupt number for the device
*/
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (octeon_pcibios_map_irq)
return octeon_pcibios_map_irq(dev, slot, pin);
else
panic("octeon_pcibios_map_irq not set.");
}
/*
* Called to perform platform specific PCI setup
*/
int pcibios_plat_dev_init(struct pci_dev *dev)
{
uint16_t config;
uint32_t dconfig;
int pos;
/*
* Force the Cache line setting to 64 bytes. The standard
* Linux bus scan doesn't seem to set it. Octeon really has
* 128 byte lines, but Intel bridges get really upset if you
* try and set values above 64 bytes. Value is specified in
* 32bit words.
*/
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 64 / 4);
/* Set latency timers for all devices */
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
/* Enable reporting System errors and parity errors on all devices */
/* Enable parity checking and error reporting */
pci_read_config_word(dev, PCI_COMMAND, &config);
config |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR;
pci_write_config_word(dev, PCI_COMMAND, config);
if (dev->subordinate) {
/* Set latency timers on sub bridges */
pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 64);
/* More bridge error detection */
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &config);
config |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR;
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, config);
}
/* Enable the PCIe normal error reporting */
config = PCI_EXP_DEVCTL_CERE; /* Correctable Error Reporting */
config |= PCI_EXP_DEVCTL_NFERE; /* Non-Fatal Error Reporting */
config |= PCI_EXP_DEVCTL_FERE; /* Fatal Error Reporting */
config |= PCI_EXP_DEVCTL_URRE; /* Unsupported Request */
pcie_capability_set_word(dev, PCI_EXP_DEVCTL, config);
/* Find the Advanced Error Reporting capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (pos) {
/* Clear Uncorrectable Error Status */
pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
&dconfig);
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
dconfig);
/* Enable reporting of all uncorrectable errors */
/* Uncorrectable Error Mask - turned on bits disable errors */
pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, 0);
/*
* Leave severity at HW default. This only controls if
* errors are reported as uncorrectable or
* correctable, not if the error is reported.
*/
/* PCI_ERR_UNCOR_SEVER - Uncorrectable Error Severity */
/* Clear Correctable Error Status */
pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &dconfig);
pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, dconfig);
/* Enable reporting of all correctable errors */
/* Correctable Error Mask - turned on bits disable errors */
pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, 0);
/* Advanced Error Capabilities */
pci_read_config_dword(dev, pos + PCI_ERR_CAP, &dconfig);
/* ECRC Generation Enable */
if (config & PCI_ERR_CAP_ECRC_GENC)
config |= PCI_ERR_CAP_ECRC_GENE;
/* ECRC Check Enable */
if (config & PCI_ERR_CAP_ECRC_CHKC)
config |= PCI_ERR_CAP_ECRC_CHKE;
pci_write_config_dword(dev, pos + PCI_ERR_CAP, dconfig);
/* PCI_ERR_HEADER_LOG - Header Log Register (16 bytes) */
/* Report all errors to the root complex */
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND,
PCI_ERR_ROOT_CMD_COR_EN |
PCI_ERR_ROOT_CMD_NONFATAL_EN |
PCI_ERR_ROOT_CMD_FATAL_EN);
/* Clear the Root status register */
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &dconfig);
pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, dconfig);
}
dev->dev.archdata.dma_ops = octeon_pci_dma_map_ops;
return 0;
}
/**
* Return the mapping of PCI device number to IRQ line. Each
* character in the return string represents the interrupt
* line for the device at that position. Device 1 maps to the
* first character, etc. The characters A-D are used for PCI
* interrupts.
*
* Returns PCI interrupt mapping
*/
const char *octeon_get_pci_interrupts(void)
{
/*
* Returning an empty string causes the interrupts to be
* routed based on the PCI specification. From the PCI spec:
*
* INTA# of Device Number 0 is connected to IRQW on the system
* board. (Device Number has no significance regarding being
* located on the system board or in a connector.) INTA# of
* Device Number 1 is connected to IRQX on the system
* board. INTA# of Device Number 2 is connected to IRQY on the
* system board. INTA# of Device Number 3 is connected to IRQZ
* on the system board. The table below describes how each
* agent's INTx# lines are connected to the system board
* interrupt lines. The following equation can be used to
* determine to which INTx# signal on the system board a given
* device's INTx# line(s) is connected.
*
* MB = (D + I) MOD 4 MB = System board Interrupt (IRQW = 0,
* IRQX = 1, IRQY = 2, and IRQZ = 3) D = Device Number I =
* Interrupt Number (INTA# = 0, INTB# = 1, INTC# = 2, and
* INTD# = 3)
*/
switch (octeon_bootinfo->board_type) {
case CVMX_BOARD_TYPE_NAO38:
/* This is really the NAC38 */
return "AAAAADABAAAAAAAAAAAAAAAAAAAAAAAA";
case CVMX_BOARD_TYPE_EBH3100:
case CVMX_BOARD_TYPE_CN3010_EVB_HS5:
case CVMX_BOARD_TYPE_CN3005_EVB_HS5:
return "AAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
case CVMX_BOARD_TYPE_BBGW_REF:
return "AABCD";
case CVMX_BOARD_TYPE_CUST_DSR1000N:
return "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
case CVMX_BOARD_TYPE_THUNDER:
case CVMX_BOARD_TYPE_EBH3000:
default:
return "";
}
}
/**
* Map a PCI device to the appropriate interrupt line
*
* @dev: The Linux PCI device structure for the device to map
* @slot: The slot number for this device on __BUS 0__. Linux
* enumerates through all the bridges and figures out the
* slot on Bus 0 where this device eventually hooks to.
* @pin: The PCI interrupt pin read from the device, then swizzled
* as it goes through each bridge.
* Returns Interrupt number for the device
*/
int __init octeon_pci_pcibios_map_irq(const struct pci_dev *dev,
u8 slot, u8 pin)
{
int irq_num;
const char *interrupts;
int dev_num;
/* Get the board specific interrupt mapping */
interrupts = octeon_get_pci_interrupts();
dev_num = dev->devfn >> 3;
if (dev_num < strlen(interrupts))
irq_num = ((interrupts[dev_num] - 'A' + pin - 1) & 3) +
OCTEON_IRQ_PCI_INT0;
else
irq_num = ((slot + pin - 3) & 3) + OCTEON_IRQ_PCI_INT0;
return irq_num;
}
/*
* Read a value from configuration space
*/
static int octeon_read_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 *val)
{
union octeon_pci_address pci_addr;
pci_addr.u64 = 0;
pci_addr.s.upper = 2;
pci_addr.s.io = 1;
pci_addr.s.did = 3;
pci_addr.s.subdid = 1;
pci_addr.s.endian_swap = 1;
pci_addr.s.bus = bus->number;
pci_addr.s.dev = devfn >> 3;
pci_addr.s.func = devfn & 0x7;
pci_addr.s.reg = reg;
switch (size) {
case 4:
*val = le32_to_cpu(cvmx_read64_uint32(pci_addr.u64));
return PCIBIOS_SUCCESSFUL;
case 2:
*val = le16_to_cpu(cvmx_read64_uint16(pci_addr.u64));
return PCIBIOS_SUCCESSFUL;
case 1:
*val = cvmx_read64_uint8(pci_addr.u64);
return PCIBIOS_SUCCESSFUL;
}
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
/*
* Write a value to PCI configuration space
*/
static int octeon_write_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 val)
{
union octeon_pci_address pci_addr;
pci_addr.u64 = 0;
pci_addr.s.upper = 2;
pci_addr.s.io = 1;
pci_addr.s.did = 3;
pci_addr.s.subdid = 1;
pci_addr.s.endian_swap = 1;
pci_addr.s.bus = bus->number;
pci_addr.s.dev = devfn >> 3;
pci_addr.s.func = devfn & 0x7;
pci_addr.s.reg = reg;
switch (size) {
case 4:
cvmx_write64_uint32(pci_addr.u64, cpu_to_le32(val));
return PCIBIOS_SUCCESSFUL;
case 2:
cvmx_write64_uint16(pci_addr.u64, cpu_to_le16(val));
return PCIBIOS_SUCCESSFUL;
case 1:
cvmx_write64_uint8(pci_addr.u64, val);
return PCIBIOS_SUCCESSFUL;
}
return PCIBIOS_FUNC_NOT_SUPPORTED;
}
static struct pci_ops octeon_pci_ops = {
octeon_read_config,
octeon_write_config,
};
static struct resource octeon_pci_mem_resource = {
.start = 0,
.end = 0,
.name = "Octeon PCI MEM",
.flags = IORESOURCE_MEM,
};
/*
* PCI ports must be above 16KB so the ISA bus filtering in the PCI-X to PCI
* bridge
*/
static struct resource octeon_pci_io_resource = {
.start = 0x4000,
.end = OCTEON_PCI_IOSPACE_SIZE - 1,
.name = "Octeon PCI IO",
.flags = IORESOURCE_IO,
};
static struct pci_controller octeon_pci_controller = {
.pci_ops = &octeon_pci_ops,
.mem_resource = &octeon_pci_mem_resource,
.mem_offset = OCTEON_PCI_MEMSPACE_OFFSET,
.io_resource = &octeon_pci_io_resource,
.io_offset = 0,
.io_map_base = OCTEON_PCI_IOSPACE_BASE,
};
/*
* Low level initialize the Octeon PCI controller
*/
static void octeon_pci_initialize(void)
{
union cvmx_pci_cfg01 cfg01;
union cvmx_npi_ctl_status ctl_status;
union cvmx_pci_ctl_status_2 ctl_status_2;
union cvmx_pci_cfg19 cfg19;
union cvmx_pci_cfg16 cfg16;
union cvmx_pci_cfg22 cfg22;
union cvmx_pci_cfg56 cfg56;
/* Reset the PCI Bus */
cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x1);
cvmx_read_csr(CVMX_CIU_SOFT_PRST);
udelay(2000); /* Hold PCI reset for 2 ms */
ctl_status.u64 = 0; /* cvmx_read_csr(CVMX_NPI_CTL_STATUS); */
ctl_status.s.max_word = 1;
ctl_status.s.timer = 1;
cvmx_write_csr(CVMX_NPI_CTL_STATUS, ctl_status.u64);
/* Deassert PCI reset and advertize PCX Host Mode Device Capability
(64b) */
cvmx_write_csr(CVMX_CIU_SOFT_PRST, 0x4);
cvmx_read_csr(CVMX_CIU_SOFT_PRST);
udelay(2000); /* Wait 2 ms after deasserting PCI reset */
ctl_status_2.u32 = 0;
ctl_status_2.s.tsr_hwm = 1; /* Initializes to 0. Must be set
before any PCI reads. */
ctl_status_2.s.bar2pres = 1; /* Enable BAR2 */
ctl_status_2.s.bar2_enb = 1;
ctl_status_2.s.bar2_cax = 1; /* Don't use L2 */
ctl_status_2.s.bar2_esx = 1;
ctl_status_2.s.pmo_amod = 1; /* Round robin priority */
if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_BIG) {
/* BAR1 hole */
ctl_status_2.s.bb1_hole = OCTEON_PCI_BAR1_HOLE_BITS;
ctl_status_2.s.bb1_siz = 1; /* BAR1 is 2GB */
ctl_status_2.s.bb_ca = 1; /* Don't use L2 with big bars */
ctl_status_2.s.bb_es = 1; /* Big bar in byte swap mode */
ctl_status_2.s.bb1 = 1; /* BAR1 is big */
ctl_status_2.s.bb0 = 1; /* BAR0 is big */
}
octeon_npi_write32(CVMX_NPI_PCI_CTL_STATUS_2, ctl_status_2.u32);
udelay(2000); /* Wait 2 ms before doing PCI reads */
ctl_status_2.u32 = octeon_npi_read32(CVMX_NPI_PCI_CTL_STATUS_2);
pr_notice("PCI Status: %s %s-bit\n",
ctl_status_2.s.ap_pcix ? "PCI-X" : "PCI",
ctl_status_2.s.ap_64ad ? "64" : "32");
if (OCTEON_IS_MODEL(OCTEON_CN58XX) || OCTEON_IS_MODEL(OCTEON_CN50XX)) {
union cvmx_pci_cnt_reg cnt_reg_start;
union cvmx_pci_cnt_reg cnt_reg_end;
unsigned long cycles, pci_clock;
cnt_reg_start.u64 = cvmx_read_csr(CVMX_NPI_PCI_CNT_REG);
cycles = read_c0_cvmcount();
udelay(1000);
cnt_reg_end.u64 = cvmx_read_csr(CVMX_NPI_PCI_CNT_REG);
cycles = read_c0_cvmcount() - cycles;
pci_clock = (cnt_reg_end.s.pcicnt - cnt_reg_start.s.pcicnt) /
(cycles / (mips_hpt_frequency / 1000000));
pr_notice("PCI Clock: %lu MHz\n", pci_clock);
}
/*
* TDOMC must be set to one in PCI mode. TDOMC should be set to 4
* in PCI-X mode to allow four outstanding splits. Otherwise,
* should not change from its reset value. Don't write PCI_CFG19
* in PCI mode (0x82000001 reset value), write it to 0x82000004
* after PCI-X mode is known. MRBCI,MDWE,MDRE -> must be zero.
* MRBCM -> must be one.
*/
if (ctl_status_2.s.ap_pcix) {
cfg19.u32 = 0;
/*
* Target Delayed/Split request outstanding maximum
* count. [1..31] and 0=32. NOTE: If the user
* programs these bits beyond the Designed Maximum
* outstanding count, then the designed maximum table
* depth will be used instead. No additional
* Deferred/Split transactions will be accepted if
* this outstanding maximum count is
* reached. Furthermore, no additional deferred/split
* transactions will be accepted if the I/O delay/ I/O
* Split Request outstanding maximum is reached.
*/
cfg19.s.tdomc = 4;
/*
* Master Deferred Read Request Outstanding Max Count
* (PCI only). CR4C[26:24] Max SAC cycles MAX DAC
* cycles 000 8 4 001 1 0 010 2 1 011 3 1 100 4 2 101
* 5 2 110 6 3 111 7 3 For example, if these bits are
* programmed to 100, the core can support 2 DAC
* cycles, 4 SAC cycles or a combination of 1 DAC and
* 2 SAC cycles. NOTE: For the PCI-X maximum
* outstanding split transactions, refer to
* CRE0[22:20].
*/
cfg19.s.mdrrmc = 2;
/*
* Master Request (Memory Read) Byte Count/Byte Enable
* select. 0 = Byte Enables valid. In PCI mode, a
* burst transaction cannot be performed using Memory
* Read command=4?h6. 1 = DWORD Byte Count valid
* (default). In PCI Mode, the memory read byte
* enables are automatically generated by the
* core. Note: N3 Master Request transaction sizes are
* always determined through the
* am_attr[<35:32>|<7:0>] field.
*/
cfg19.s.mrbcm = 1;
octeon_npi_write32(CVMX_NPI_PCI_CFG19, cfg19.u32);
}
cfg01.u32 = 0;
cfg01.s.msae = 1; /* Memory Space Access Enable */
cfg01.s.me = 1; /* Master Enable */
cfg01.s.pee = 1; /* PERR# Enable */
cfg01.s.see = 1; /* System Error Enable */
cfg01.s.fbbe = 1; /* Fast Back to Back Transaction Enable */
octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
#ifdef USE_OCTEON_INTERNAL_ARBITER
/*
* When OCTEON is a PCI host, most systems will use OCTEON's
* internal arbiter, so must enable it before any PCI/PCI-X
* traffic can occur.
*/
{
union cvmx_npi_pci_int_arb_cfg pci_int_arb_cfg;
pci_int_arb_cfg.u64 = 0;
pci_int_arb_cfg.s.en = 1; /* Internal arbiter enable */
cvmx_write_csr(CVMX_NPI_PCI_INT_ARB_CFG, pci_int_arb_cfg.u64);
}
#endif /* USE_OCTEON_INTERNAL_ARBITER */
/*
* Preferably written to 1 to set MLTD. [RDSATI,TRTAE,
* TWTAE,TMAE,DPPMR -> must be zero. TILT -> must not be set to
* 1..7.
*/
cfg16.u32 = 0;
cfg16.s.mltd = 1; /* Master Latency Timer Disable */
octeon_npi_write32(CVMX_NPI_PCI_CFG16, cfg16.u32);
/*
* Should be written to 0x4ff00. MTTV -> must be zero.
* FLUSH -> must be 1. MRV -> should be 0xFF.
*/
cfg22.u32 = 0;
/* Master Retry Value [1..255] and 0=infinite */
cfg22.s.mrv = 0xff;
/*
* AM_DO_FLUSH_I control NOTE: This bit MUST BE ONE for proper
* N3K operation.
*/
cfg22.s.flush = 1;
octeon_npi_write32(CVMX_NPI_PCI_CFG22, cfg22.u32);
/*
* MOST Indicates the maximum number of outstanding splits (in -1
* notation) when OCTEON is in PCI-X mode. PCI-X performance is
* affected by the MOST selection. Should generally be written
* with one of 0x3be807, 0x2be807, 0x1be807, or 0x0be807,
* depending on the desired MOST of 3, 2, 1, or 0, respectively.
*/
cfg56.u32 = 0;
cfg56.s.pxcid = 7; /* RO - PCI-X Capability ID */
cfg56.s.ncp = 0xe8; /* RO - Next Capability Pointer */
cfg56.s.dpere = 1; /* Data Parity Error Recovery Enable */
cfg56.s.roe = 1; /* Relaxed Ordering Enable */
cfg56.s.mmbc = 1; /* Maximum Memory Byte Count
[0=512B,1=1024B,2=2048B,3=4096B] */
cfg56.s.most = 3; /* Maximum outstanding Split transactions [0=1
.. 7=32] */
octeon_npi_write32(CVMX_NPI_PCI_CFG56, cfg56.u32);
/*
* Affects PCI performance when OCTEON services reads to its
* BAR1/BAR2. Refer to Section 10.6.1. The recommended values are
* 0x22, 0x33, and 0x33 for PCI_READ_CMD_6, PCI_READ_CMD_C, and
* PCI_READ_CMD_E, respectively. Unfortunately due to errata DDR-700,
* these values need to be changed so they won't possibly prefetch off
* of the end of memory if PCI is DMAing a buffer at the end of
* memory. Note that these values differ from their reset values.
*/
octeon_npi_write32(CVMX_NPI_PCI_READ_CMD_6, 0x21);
octeon_npi_write32(CVMX_NPI_PCI_READ_CMD_C, 0x31);
octeon_npi_write32(CVMX_NPI_PCI_READ_CMD_E, 0x31);
}
/*
* Initialize the Octeon PCI controller
*/
static int __init octeon_pci_setup(void)
{
union cvmx_npi_mem_access_subidx mem_access;
int index;
/* Only these chips have PCI */
if (octeon_has_feature(OCTEON_FEATURE_PCIE))
return 0;
/* Point pcibios_map_irq() to the PCI version of it */
octeon_pcibios_map_irq = octeon_pci_pcibios_map_irq;
/* Only use the big bars on chips that support it */
if (OCTEON_IS_MODEL(OCTEON_CN31XX) ||
OCTEON_IS_MODEL(OCTEON_CN38XX_PASS2) ||
OCTEON_IS_MODEL(OCTEON_CN38XX_PASS1))
octeon_dma_bar_type = OCTEON_DMA_BAR_TYPE_SMALL;
else
octeon_dma_bar_type = OCTEON_DMA_BAR_TYPE_BIG;
if (!octeon_is_pci_host()) {
pr_notice("Not in host mode, PCI Controller not initialized\n");
return 0;
}
/* PCI I/O and PCI MEM values */
set_io_port_base(OCTEON_PCI_IOSPACE_BASE);
ioport_resource.start = 0;
ioport_resource.end = OCTEON_PCI_IOSPACE_SIZE - 1;
pr_notice("%s Octeon big bar support\n",
(octeon_dma_bar_type ==
OCTEON_DMA_BAR_TYPE_BIG) ? "Enabling" : "Disabling");
octeon_pci_initialize();
mem_access.u64 = 0;
mem_access.s.esr = 1; /* Endian-Swap on read. */
mem_access.s.esw = 1; /* Endian-Swap on write. */
mem_access.s.nsr = 0; /* No-Snoop on read. */
mem_access.s.nsw = 0; /* No-Snoop on write. */
mem_access.s.ror = 0; /* Relax Read on read. */
mem_access.s.row = 0; /* Relax Order on write. */
mem_access.s.ba = 0; /* PCI Address bits [63:36]. */
cvmx_write_csr(CVMX_NPI_MEM_ACCESS_SUBID3, mem_access.u64);
/*
* Remap the Octeon BAR 2 above all 32 bit devices
* (0x8000000000ul). This is done here so it is remapped
* before the readl()'s below. We don't want BAR2 overlapping
* with BAR0/BAR1 during these reads.
*/
octeon_npi_write32(CVMX_NPI_PCI_CFG08,
(u32)(OCTEON_BAR2_PCI_ADDRESS & 0xffffffffull));
octeon_npi_write32(CVMX_NPI_PCI_CFG09,
(u32)(OCTEON_BAR2_PCI_ADDRESS >> 32));
if (octeon_dma_bar_type == OCTEON_DMA_BAR_TYPE_BIG) {
/* Remap the Octeon BAR 0 to 0-2GB */
octeon_npi_write32(CVMX_NPI_PCI_CFG04, 0);
octeon_npi_write32(CVMX_NPI_PCI_CFG05, 0);
/*
* Remap the Octeon BAR 1 to map 2GB-4GB (minus the
* BAR 1 hole).
*/
octeon_npi_write32(CVMX_NPI_PCI_CFG06, 2ul << 30);
octeon_npi_write32(CVMX_NPI_PCI_CFG07, 0);
/* BAR1 movable mappings set for identity mapping */
octeon_bar1_pci_phys = 0x80000000ull;
for (index = 0; index < 32; index++) {
union cvmx_pci_bar1_indexx bar1_index;
bar1_index.u32 = 0;
/* Address bits[35:22] sent to L2C */
bar1_index.s.addr_idx =
(octeon_bar1_pci_phys >> 22) + index;
/* Don't put PCI accesses in L2. */
bar1_index.s.ca = 1;
/* Endian Swap Mode */
bar1_index.s.end_swp = 1;
/* Set '1' when the selected address range is valid. */
bar1_index.s.addr_v = 1;
octeon_npi_write32(CVMX_NPI_PCI_BAR1_INDEXX(index),
bar1_index.u32);
}
/* Devices go after BAR1 */
octeon_pci_mem_resource.start =
OCTEON_PCI_MEMSPACE_OFFSET + (4ul << 30) -
(OCTEON_PCI_BAR1_HOLE_SIZE << 20);
octeon_pci_mem_resource.end =
octeon_pci_mem_resource.start + (1ul << 30);
} else {
/* Remap the Octeon BAR 0 to map 128MB-(128MB+4KB) */
octeon_npi_write32(CVMX_NPI_PCI_CFG04, 128ul << 20);
octeon_npi_write32(CVMX_NPI_PCI_CFG05, 0);
/* Remap the Octeon BAR 1 to map 0-128MB */
octeon_npi_write32(CVMX_NPI_PCI_CFG06, 0);
octeon_npi_write32(CVMX_NPI_PCI_CFG07, 0);
/* BAR1 movable regions contiguous to cover the swiotlb */
octeon_bar1_pci_phys =
virt_to_phys(octeon_swiotlb) & ~((1ull << 22) - 1);
for (index = 0; index < 32; index++) {
union cvmx_pci_bar1_indexx bar1_index;
bar1_index.u32 = 0;
/* Address bits[35:22] sent to L2C */
bar1_index.s.addr_idx =
(octeon_bar1_pci_phys >> 22) + index;
/* Don't put PCI accesses in L2. */
bar1_index.s.ca = 1;
/* Endian Swap Mode */
bar1_index.s.end_swp = 1;
/* Set '1' when the selected address range is valid. */
bar1_index.s.addr_v = 1;
octeon_npi_write32(CVMX_NPI_PCI_BAR1_INDEXX(index),
bar1_index.u32);
}
/* Devices go after BAR0 */
octeon_pci_mem_resource.start =
OCTEON_PCI_MEMSPACE_OFFSET + (128ul << 20) +
(4ul << 10);
octeon_pci_mem_resource.end =
octeon_pci_mem_resource.start + (1ul << 30);
}
register_pci_controller(&octeon_pci_controller);
/*
* Clear any errors that might be pending from before the bus
* was setup properly.
*/
cvmx_write_csr(CVMX_NPI_PCI_INT_SUM2, -1);
if (IS_ERR(platform_device_register_simple("octeon_pci_edac",
-1, NULL, 0)))
pr_err("Registation of co_pci_edac failed!\n");
octeon_pci_dma_init();
return 0;
}
arch_initcall(octeon_pci_setup);

231
arch/mips/pci/pci-rc32434.c Normal file
View file

@ -0,0 +1,231 @@
/*
* BRIEF MODULE DESCRIPTION
* PCI initialization for IDT EB434 board
*
* Copyright 2004 IDT Inc. (rischelp@idt.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/mach-rc32434/rc32434.h>
#include <asm/mach-rc32434/pci.h>
#define PCI_ACCESS_READ 0
#define PCI_ACCESS_WRITE 1
/* define an unsigned array for the PCI registers */
static unsigned int korina_cnfg_regs[25] = {
KORINA_CNFG1, KORINA_CNFG2, KORINA_CNFG3, KORINA_CNFG4,
KORINA_CNFG5, KORINA_CNFG6, KORINA_CNFG7, KORINA_CNFG8,
KORINA_CNFG9, KORINA_CNFG10, KORINA_CNFG11, KORINA_CNFG12,
KORINA_CNFG13, KORINA_CNFG14, KORINA_CNFG15, KORINA_CNFG16,
KORINA_CNFG17, KORINA_CNFG18, KORINA_CNFG19, KORINA_CNFG20,
KORINA_CNFG21, KORINA_CNFG22, KORINA_CNFG23, KORINA_CNFG24
};
static struct resource rc32434_res_pci_mem1;
static struct resource rc32434_res_pci_mem2;
static struct resource rc32434_res_pci_mem1 = {
.name = "PCI MEM1",
.start = 0x50000000,
.end = 0x5FFFFFFF,
.flags = IORESOURCE_MEM,
.sibling = NULL,
.child = &rc32434_res_pci_mem2
};
static struct resource rc32434_res_pci_mem2 = {
.name = "PCI Mem2",
.start = 0x60000000,
.end = 0x6FFFFFFF,
.flags = IORESOURCE_MEM,
.parent = &rc32434_res_pci_mem1,
.sibling = NULL,
.child = NULL
};
static struct resource rc32434_res_pci_io1 = {
.name = "PCI I/O1",
.start = 0x18800000,
.end = 0x188FFFFF,
.flags = IORESOURCE_IO,
};
extern struct pci_ops rc32434_pci_ops;
#define PCI_MEM1_START PCI_ADDR_START
#define PCI_MEM1_END (PCI_ADDR_START + CPUTOPCI_MEM_WIN - 1)
#define PCI_MEM2_START (PCI_ADDR_START + CPUTOPCI_MEM_WIN)
#define PCI_MEM2_END (PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) - 1)
#define PCI_IO1_START (PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN))
#define PCI_IO1_END \
(PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN - 1)
#define PCI_IO2_START \
(PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN)
#define PCI_IO2_END \
(PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + (2 * CPUTOPCI_IO_WIN) - 1)
struct pci_controller rc32434_controller2;
struct pci_controller rc32434_controller = {
.pci_ops = &rc32434_pci_ops,
.mem_resource = &rc32434_res_pci_mem1,
.io_resource = &rc32434_res_pci_io1,
.mem_offset = 0,
.io_offset = 0,
};
#ifdef __MIPSEB__
#define PCI_ENDIAN_FLAG PCILBAC_sb_m
#else
#define PCI_ENDIAN_FLAG 0
#endif
static int __init rc32434_pcibridge_init(void)
{
unsigned int pcicvalue, pcicdata = 0;
unsigned int dummyread, pcicntlval;
int loopCount;
unsigned int pci_config_addr;
pcicvalue = rc32434_pci->pcic;
pcicvalue = (pcicvalue >> PCIM_SHFT) & PCIM_BIT_LEN;
if (!((pcicvalue == PCIM_H_EA) ||
(pcicvalue == PCIM_H_IA_FIX) ||
(pcicvalue == PCIM_H_IA_RR))) {
pr_err("PCI init error!!!\n");
/* Not in Host Mode, return ERROR */
return -1;
}
/* Enables the Idle Grant mode, Arbiter Parking */
pcicdata |= (PCI_CTL_IGM | PCI_CTL_EAP | PCI_CTL_EN);
rc32434_pci->pcic = pcicdata; /* Enable the PCI bus Interface */
/* Zero out the PCI status & PCI Status Mask */
for (;;) {
pcicdata = rc32434_pci->pcis;
if (!(pcicdata & PCI_STAT_RIP))
break;
}
rc32434_pci->pcis = 0;
rc32434_pci->pcism = 0xFFFFFFFF;
/* Zero out the PCI decoupled registers */
rc32434_pci->pcidac = 0; /*
* disable PCI decoupled accesses at
* initialization
*/
rc32434_pci->pcidas = 0; /* clear the status */
rc32434_pci->pcidasm = 0x0000007F; /* Mask all the interrupts */
/* Mask PCI Messaging Interrupts */
rc32434_pci_msg->pciiic = 0;
rc32434_pci_msg->pciiim = 0xFFFFFFFF;
rc32434_pci_msg->pciioic = 0;
rc32434_pci_msg->pciioim = 0;
/* Setup PCILB0 as Memory Window */
rc32434_pci->pcilba[0].address = (unsigned int) (PCI_ADDR_START);
/* setup the PCI map address as same as the local address */
rc32434_pci->pcilba[0].mapping = (unsigned int) (PCI_ADDR_START);
/* Setup PCILBA1 as MEM */
rc32434_pci->pcilba[0].control =
(((SIZE_256MB & 0x1f) << PCI_LBAC_SIZE_BIT) | PCI_ENDIAN_FLAG);
dummyread = rc32434_pci->pcilba[0].control; /* flush the CPU write Buffers */
rc32434_pci->pcilba[1].address = 0x60000000;
rc32434_pci->pcilba[1].mapping = 0x60000000;
/* setup PCILBA2 as IO Window */
rc32434_pci->pcilba[1].control =
(((SIZE_256MB & 0x1f) << PCI_LBAC_SIZE_BIT) | PCI_ENDIAN_FLAG);
dummyread = rc32434_pci->pcilba[1].control; /* flush the CPU write Buffers */
rc32434_pci->pcilba[2].address = 0x18C00000;
rc32434_pci->pcilba[2].mapping = 0x18FFFFFF;
/* setup PCILBA2 as IO Window */
rc32434_pci->pcilba[2].control =
(((SIZE_4MB & 0x1f) << PCI_LBAC_SIZE_BIT) | PCI_ENDIAN_FLAG);
dummyread = rc32434_pci->pcilba[2].control; /* flush the CPU write Buffers */
/* Setup PCILBA3 as IO Window */
rc32434_pci->pcilba[3].address = 0x18800000;
rc32434_pci->pcilba[3].mapping = 0x18800000;
rc32434_pci->pcilba[3].control =
((((SIZE_1MB & 0x1ff) << PCI_LBAC_SIZE_BIT) | PCI_LBAC_MSI) |
PCI_ENDIAN_FLAG);
dummyread = rc32434_pci->pcilba[3].control; /* flush the CPU write Buffers */
pci_config_addr = (unsigned int) (0x80000004);
for (loopCount = 0; loopCount < 24; loopCount++) {
rc32434_pci->pcicfga = pci_config_addr;
dummyread = rc32434_pci->pcicfga;
rc32434_pci->pcicfgd = korina_cnfg_regs[loopCount];
dummyread = rc32434_pci->pcicfgd;
pci_config_addr += 4;
}
rc32434_pci->pcitc =
(unsigned int) ((PCITC_RTIMER_VAL & 0xff) << PCI_TC_RTIMER_BIT) |
((PCITC_DTIMER_VAL & 0xff) << PCI_TC_DTIMER_BIT);
pcicntlval = rc32434_pci->pcic;
pcicntlval &= ~PCI_CTL_TNR;
rc32434_pci->pcic = pcicntlval;
pcicntlval = rc32434_pci->pcic;
return 0;
}
static int __init rc32434_pci_init(void)
{
void __iomem *io_map_base;
pr_info("PCI: Initializing PCI\n");
ioport_resource.start = rc32434_res_pci_io1.start;
ioport_resource.end = rc32434_res_pci_io1.end;
rc32434_pcibridge_init();
io_map_base = ioremap(rc32434_res_pci_io1.start,
resource_size(&rc32434_res_pci_io1));
if (!io_map_base)
return -ENOMEM;
rc32434_controller.io_map_base =
(unsigned long)io_map_base - rc32434_res_pci_io1.start;
register_pci_controller(&rc32434_controller);
rc32434_sync();
return 0;
}
arch_initcall(rc32434_pci_init);

611
arch/mips/pci/pci-rt3883.c Normal file
View file

@ -0,0 +1,611 @@
/*
* Ralink RT3662/RT3883 SoC PCI support
*
* Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
*
* Parts of this file are based on Ralink's 2.6.21 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/platform_device.h>
#include <asm/mach-ralink/rt3883.h>
#include <asm/mach-ralink/ralink_regs.h>
#define RT3883_MEMORY_BASE 0x00000000
#define RT3883_MEMORY_SIZE 0x02000000
#define RT3883_PCI_REG_PCICFG 0x00
#define RT3883_PCICFG_P2P_BR_DEVNUM_M 0xf
#define RT3883_PCICFG_P2P_BR_DEVNUM_S 16
#define RT3883_PCICFG_PCIRST BIT(1)
#define RT3883_PCI_REG_PCIRAW 0x04
#define RT3883_PCI_REG_PCIINT 0x08
#define RT3883_PCI_REG_PCIENA 0x0c
#define RT3883_PCI_REG_CFGADDR 0x20
#define RT3883_PCI_REG_CFGDATA 0x24
#define RT3883_PCI_REG_MEMBASE 0x28
#define RT3883_PCI_REG_IOBASE 0x2c
#define RT3883_PCI_REG_ARBCTL 0x80
#define RT3883_PCI_REG_BASE(_x) (0x1000 + (_x) * 0x1000)
#define RT3883_PCI_REG_BAR0SETUP(_x) (RT3883_PCI_REG_BASE((_x)) + 0x10)
#define RT3883_PCI_REG_IMBASEBAR0(_x) (RT3883_PCI_REG_BASE((_x)) + 0x18)
#define RT3883_PCI_REG_ID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x30)
#define RT3883_PCI_REG_CLASS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x34)
#define RT3883_PCI_REG_SUBID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x38)
#define RT3883_PCI_REG_STATUS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x50)
#define RT3883_PCI_MODE_NONE 0
#define RT3883_PCI_MODE_PCI BIT(0)
#define RT3883_PCI_MODE_PCIE BIT(1)
#define RT3883_PCI_MODE_BOTH (RT3883_PCI_MODE_PCI | RT3883_PCI_MODE_PCIE)
#define RT3883_PCI_IRQ_COUNT 32
#define RT3883_P2P_BR_DEVNUM 1
struct rt3883_pci_controller {
void __iomem *base;
spinlock_t lock;
struct device_node *intc_of_node;
struct irq_domain *irq_domain;
struct pci_controller pci_controller;
struct resource io_res;
struct resource mem_res;
bool pcie_ready;
};
static inline struct rt3883_pci_controller *
pci_bus_to_rt3883_controller(struct pci_bus *bus)
{
struct pci_controller *hose;
hose = (struct pci_controller *) bus->sysdata;
return container_of(hose, struct rt3883_pci_controller, pci_controller);
}
static inline u32 rt3883_pci_r32(struct rt3883_pci_controller *rpc,
unsigned reg)
{
return ioread32(rpc->base + reg);
}
static inline void rt3883_pci_w32(struct rt3883_pci_controller *rpc,
u32 val, unsigned reg)
{
iowrite32(val, rpc->base + reg);
}
static inline u32 rt3883_pci_get_cfgaddr(unsigned int bus, unsigned int slot,
unsigned int func, unsigned int where)
{
return (bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) |
0x80000000;
}
static u32 rt3883_pci_read_cfg32(struct rt3883_pci_controller *rpc,
unsigned bus, unsigned slot,
unsigned func, unsigned reg)
{
unsigned long flags;
u32 address;
u32 ret;
address = rt3883_pci_get_cfgaddr(bus, slot, func, reg);
spin_lock_irqsave(&rpc->lock, flags);
rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR);
ret = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA);
spin_unlock_irqrestore(&rpc->lock, flags);
return ret;
}
static void rt3883_pci_write_cfg32(struct rt3883_pci_controller *rpc,
unsigned bus, unsigned slot,
unsigned func, unsigned reg, u32 val)
{
unsigned long flags;
u32 address;
address = rt3883_pci_get_cfgaddr(bus, slot, func, reg);
spin_lock_irqsave(&rpc->lock, flags);
rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR);
rt3883_pci_w32(rpc, val, RT3883_PCI_REG_CFGDATA);
spin_unlock_irqrestore(&rpc->lock, flags);
}
static void rt3883_pci_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct rt3883_pci_controller *rpc;
u32 pending;
rpc = irq_get_handler_data(irq);
pending = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIINT) &
rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA);
if (!pending) {
spurious_interrupt();
return;
}
while (pending) {
unsigned bit = __ffs(pending);
irq = irq_find_mapping(rpc->irq_domain, bit);
generic_handle_irq(irq);
pending &= ~BIT(bit);
}
}
static void rt3883_pci_irq_unmask(struct irq_data *d)
{
struct rt3883_pci_controller *rpc;
u32 t;
rpc = irq_data_get_irq_chip_data(d);
t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA);
rt3883_pci_w32(rpc, t | BIT(d->hwirq), RT3883_PCI_REG_PCIENA);
/* flush write */
rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA);
}
static void rt3883_pci_irq_mask(struct irq_data *d)
{
struct rt3883_pci_controller *rpc;
u32 t;
rpc = irq_data_get_irq_chip_data(d);
t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA);
rt3883_pci_w32(rpc, t & ~BIT(d->hwirq), RT3883_PCI_REG_PCIENA);
/* flush write */
rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA);
}
static struct irq_chip rt3883_pci_irq_chip = {
.name = "RT3883 PCI",
.irq_mask = rt3883_pci_irq_mask,
.irq_unmask = rt3883_pci_irq_unmask,
.irq_mask_ack = rt3883_pci_irq_mask,
};
static int rt3883_pci_irq_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
irq_set_chip_and_handler(irq, &rt3883_pci_irq_chip, handle_level_irq);
irq_set_chip_data(irq, d->host_data);
return 0;
}
static const struct irq_domain_ops rt3883_pci_irq_domain_ops = {
.map = rt3883_pci_irq_map,
.xlate = irq_domain_xlate_onecell,
};
static int rt3883_pci_irq_init(struct device *dev,
struct rt3883_pci_controller *rpc)
{
int irq;
irq = irq_of_parse_and_map(rpc->intc_of_node, 0);
if (irq == 0) {
dev_err(dev, "%s has no IRQ",
of_node_full_name(rpc->intc_of_node));
return -EINVAL;
}
/* disable all interrupts */
rt3883_pci_w32(rpc, 0, RT3883_PCI_REG_PCIENA);
rpc->irq_domain =
irq_domain_add_linear(rpc->intc_of_node, RT3883_PCI_IRQ_COUNT,
&rt3883_pci_irq_domain_ops,
rpc);
if (!rpc->irq_domain) {
dev_err(dev, "unable to add IRQ domain\n");
return -ENODEV;
}
irq_set_handler_data(irq, rpc);
irq_set_chained_handler(irq, rt3883_pci_irq_handler);
return 0;
}
static int rt3883_pci_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
struct rt3883_pci_controller *rpc;
unsigned long flags;
u32 address;
u32 data;
rpc = pci_bus_to_rt3883_controller(bus);
if (!rpc->pcie_ready && bus->number == 1)
return PCIBIOS_DEVICE_NOT_FOUND;
address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), where);
spin_lock_irqsave(&rpc->lock, flags);
rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR);
data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA);
spin_unlock_irqrestore(&rpc->lock, flags);
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 rt3883_pci_config_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct rt3883_pci_controller *rpc;
unsigned long flags;
u32 address;
u32 data;
rpc = pci_bus_to_rt3883_controller(bus);
if (!rpc->pcie_ready && bus->number == 1)
return PCIBIOS_DEVICE_NOT_FOUND;
address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), where);
spin_lock_irqsave(&rpc->lock, flags);
rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR);
data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA);
switch (size) {
case 1:
data = (data & ~(0xff << ((where & 3) << 3))) |
(val << ((where & 3) << 3));
break;
case 2:
data = (data & ~(0xffff << ((where & 3) << 3))) |
(val << ((where & 3) << 3));
break;
case 4:
data = val;
break;
}
rt3883_pci_w32(rpc, data, RT3883_PCI_REG_CFGDATA);
spin_unlock_irqrestore(&rpc->lock, flags);
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops rt3883_pci_ops = {
.read = rt3883_pci_config_read,
.write = rt3883_pci_config_write,
};
static void rt3883_pci_preinit(struct rt3883_pci_controller *rpc, unsigned mode)
{
u32 syscfg1;
u32 rstctrl;
u32 clkcfg1;
u32 t;
rstctrl = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL);
syscfg1 = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1);
clkcfg1 = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1);
if (mode & RT3883_PCI_MODE_PCIE) {
rstctrl |= RT3883_RSTCTRL_PCIE;
rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL);
/* setup PCI PAD drive mode */
syscfg1 &= ~(0x30);
syscfg1 |= (2 << 4);
rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1);
t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0);
t &= ~BIT(31);
rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0);
t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1);
t &= 0x80ffffff;
rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1);
t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1);
t |= 0xa << 24;
rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1);
t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0);
t |= BIT(31);
rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0);
msleep(50);
rstctrl &= ~RT3883_RSTCTRL_PCIE;
rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL);
}
syscfg1 |= (RT3883_SYSCFG1_PCIE_RC_MODE | RT3883_SYSCFG1_PCI_HOST_MODE);
clkcfg1 &= ~(RT3883_CLKCFG1_PCI_CLK_EN | RT3883_CLKCFG1_PCIE_CLK_EN);
if (mode & RT3883_PCI_MODE_PCI) {
clkcfg1 |= RT3883_CLKCFG1_PCI_CLK_EN;
rstctrl &= ~RT3883_RSTCTRL_PCI;
}
if (mode & RT3883_PCI_MODE_PCIE) {
clkcfg1 |= RT3883_CLKCFG1_PCIE_CLK_EN;
rstctrl &= ~RT3883_RSTCTRL_PCIE;
}
rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1);
rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL);
rt_sysc_w32(clkcfg1, RT3883_SYSC_REG_CLKCFG1);
msleep(500);
/*
* setup the device number of the P2P bridge
* and de-assert the reset line
*/
t = (RT3883_P2P_BR_DEVNUM << RT3883_PCICFG_P2P_BR_DEVNUM_S);
rt3883_pci_w32(rpc, t, RT3883_PCI_REG_PCICFG);
/* flush write */
rt3883_pci_r32(rpc, RT3883_PCI_REG_PCICFG);
msleep(500);
if (mode & RT3883_PCI_MODE_PCIE) {
msleep(500);
t = rt3883_pci_r32(rpc, RT3883_PCI_REG_STATUS(1));
rpc->pcie_ready = t & BIT(0);
if (!rpc->pcie_ready) {
/* reset the PCIe block */
t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL);
t |= RT3883_RSTCTRL_PCIE;
rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL);
t &= ~RT3883_RSTCTRL_PCIE;
rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL);
/* turn off PCIe clock */
t = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1);
t &= ~RT3883_CLKCFG1_PCIE_CLK_EN;
rt_sysc_w32(t, RT3883_SYSC_REG_CLKCFG1);
t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0);
t &= ~0xf000c080;
rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0);
}
}
/* enable PCI arbiter */
rt3883_pci_w32(rpc, 0x79, RT3883_PCI_REG_ARBCTL);
}
static int rt3883_pci_probe(struct platform_device *pdev)
{
struct rt3883_pci_controller *rpc;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct resource *res;
struct device_node *child;
u32 val;
int err;
int mode;
rpc = devm_kzalloc(dev, sizeof(*rpc), GFP_KERNEL);
if (!rpc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rpc->base = devm_ioremap_resource(dev, res);
if (IS_ERR(rpc->base))
return PTR_ERR(rpc->base);
/* find the interrupt controller child node */
for_each_child_of_node(np, child) {
if (of_get_property(child, "interrupt-controller", NULL) &&
of_node_get(child)) {
rpc->intc_of_node = child;
break;
}
}
if (!rpc->intc_of_node) {
dev_err(dev, "%s has no %s child node",
of_node_full_name(rpc->intc_of_node),
"interrupt controller");
return -EINVAL;
}
/* find the PCI host bridge child node */
for_each_child_of_node(np, child) {
if (child->type &&
of_node_cmp(child->type, "pci") == 0 &&
of_node_get(child)) {
rpc->pci_controller.of_node = child;
break;
}
}
if (!rpc->pci_controller.of_node) {
dev_err(dev, "%s has no %s child node",
of_node_full_name(rpc->intc_of_node),
"PCI host bridge");
err = -EINVAL;
goto err_put_intc_node;
}
mode = RT3883_PCI_MODE_NONE;
for_each_available_child_of_node(rpc->pci_controller.of_node, child) {
int devfn;
if (!child->type ||
of_node_cmp(child->type, "pci") != 0)
continue;
devfn = of_pci_get_devfn(child);
if (devfn < 0)
continue;
switch (PCI_SLOT(devfn)) {
case 1:
mode |= RT3883_PCI_MODE_PCIE;
break;
case 17:
case 18:
mode |= RT3883_PCI_MODE_PCI;
break;
}
}
if (mode == RT3883_PCI_MODE_NONE) {
dev_err(dev, "unable to determine PCI mode\n");
err = -EINVAL;
goto err_put_hb_node;
}
dev_info(dev, "mode:%s%s\n",
(mode & RT3883_PCI_MODE_PCI) ? " PCI" : "",
(mode & RT3883_PCI_MODE_PCIE) ? " PCIe" : "");
rt3883_pci_preinit(rpc, mode);
rpc->pci_controller.pci_ops = &rt3883_pci_ops;
rpc->pci_controller.io_resource = &rpc->io_res;
rpc->pci_controller.mem_resource = &rpc->mem_res;
/* Load PCI I/O and memory resources from DT */
pci_load_of_ranges(&rpc->pci_controller,
rpc->pci_controller.of_node);
rt3883_pci_w32(rpc, rpc->mem_res.start, RT3883_PCI_REG_MEMBASE);
rt3883_pci_w32(rpc, rpc->io_res.start, RT3883_PCI_REG_IOBASE);
ioport_resource.start = rpc->io_res.start;
ioport_resource.end = rpc->io_res.end;
/* PCI */
rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(0));
rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(0));
rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(0));
rt3883_pci_w32(rpc, 0x00800001, RT3883_PCI_REG_CLASS(0));
rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(0));
/* PCIe */
rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(1));
rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(1));
rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(1));
rt3883_pci_w32(rpc, 0x06040001, RT3883_PCI_REG_CLASS(1));
rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(1));
err = rt3883_pci_irq_init(dev, rpc);
if (err)
goto err_put_hb_node;
/* PCIe */
val = rt3883_pci_read_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND);
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
rt3883_pci_write_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND, val);
/* PCI */
val = rt3883_pci_read_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND);
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
rt3883_pci_write_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND, val);
if (mode == RT3883_PCI_MODE_PCIE) {
rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(0));
rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(1));
rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0,
PCI_BASE_ADDRESS_0,
RT3883_MEMORY_BASE);
/* flush write */
rt3883_pci_read_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0,
PCI_BASE_ADDRESS_0);
} else {
rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0,
PCI_IO_BASE, 0x00000101);
}
register_pci_controller(&rpc->pci_controller);
return 0;
err_put_hb_node:
of_node_put(rpc->pci_controller.of_node);
err_put_intc_node:
of_node_put(rpc->intc_of_node);
return err;
}
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return of_irq_parse_and_map_pci(dev, slot, pin);
}
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
static const struct of_device_id rt3883_pci_ids[] = {
{ .compatible = "ralink,rt3883-pci" },
{},
};
MODULE_DEVICE_TABLE(of, rt3883_pci_ids);
static struct platform_driver rt3883_pci_driver = {
.probe = rt3883_pci_probe,
.driver = {
.name = "rt3883-pci",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(rt3883_pci_ids),
},
};
static int __init rt3883_pci_init(void)
{
return platform_driver_register(&rt3883_pci_driver);
}
postcore_initcall(rt3883_pci_init);

292
arch/mips/pci/pci-sb1250.c Normal file
View file

@ -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/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 <linux/vt.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;
#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(const 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)
{
void __iomem *io_map_base;
uint32_t cmdreg;
uint64_t reg;
/* CFE will assign PCI resources */
pci_set_flags(PCI_PROBE_ONLY);
/* 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 = __raw_readq(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?
*/
io_map_base = ioremap(A_PHYS_LDTPCI_IO_MATCH_BYTES, 1024 * 1024);
sb1250_controller.io_map_base = (unsigned long)io_map_base;
set_io_port_base((unsigned long)io_map_base);
#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
console_lock();
do_take_over_console(&vga_con, 0, MAX_NR_CONSOLES - 1, 1);
console_unlock();
#endif
return 0;
}
arch_initcall(sb1250_pcibios_init);

View file

@ -0,0 +1,91 @@
/*
* Based on linux/arch/mips/txx9/rbtx4938/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
* Copyright 2001, 2003-2005 MontaVista Software Inc.
* Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
* (C) Copyright TOSHIBA CORPORATION 2000-2001, 2004-2007
*
* 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.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <asm/txx9/generic.h>
#include <asm/txx9/tx4927.h>
int __init tx4927_report_pciclk(void)
{
int pciclk = 0;
printk(KERN_INFO "PCIC --%s PCICLK:",
(__raw_readq(&tx4927_ccfgptr->ccfg) & TX4927_CCFG_PCI66) ?
" PCI66" : "");
if (__raw_readq(&tx4927_ccfgptr->pcfg) & TX4927_PCFG_PCICLKEN_ALL) {
u64 ccfg = __raw_readq(&tx4927_ccfgptr->ccfg);
switch ((unsigned long)ccfg &
TX4927_CCFG_PCIDIVMODE_MASK) {
case TX4927_CCFG_PCIDIVMODE_2_5:
pciclk = txx9_cpu_clock * 2 / 5; break;
case TX4927_CCFG_PCIDIVMODE_3:
pciclk = txx9_cpu_clock / 3; break;
case TX4927_CCFG_PCIDIVMODE_5:
pciclk = txx9_cpu_clock / 5; break;
case TX4927_CCFG_PCIDIVMODE_6:
pciclk = txx9_cpu_clock / 6; break;
}
printk("Internal(%u.%uMHz)",
(pciclk + 50000) / 1000000,
((pciclk + 50000) / 100000) % 10);
} else {
printk("External");
pciclk = -1;
}
printk("\n");
return pciclk;
}
int __init tx4927_pciclk66_setup(void)
{
int pciclk;
/* Assert M66EN */
tx4927_ccfg_set(TX4927_CCFG_PCI66);
/* Double PCICLK (if possible) */
if (__raw_readq(&tx4927_ccfgptr->pcfg) & TX4927_PCFG_PCICLKEN_ALL) {
unsigned int pcidivmode = 0;
u64 ccfg = __raw_readq(&tx4927_ccfgptr->ccfg);
pcidivmode = (unsigned long)ccfg &
TX4927_CCFG_PCIDIVMODE_MASK;
switch (pcidivmode) {
case TX4927_CCFG_PCIDIVMODE_5:
case TX4927_CCFG_PCIDIVMODE_2_5:
pcidivmode = TX4927_CCFG_PCIDIVMODE_2_5;
pciclk = txx9_cpu_clock * 2 / 5;
break;
case TX4927_CCFG_PCIDIVMODE_6:
case TX4927_CCFG_PCIDIVMODE_3:
default:
pcidivmode = TX4927_CCFG_PCIDIVMODE_3;
pciclk = txx9_cpu_clock / 3;
}
tx4927_ccfg_change(TX4927_CCFG_PCIDIVMODE_MASK,
pcidivmode);
printk(KERN_DEBUG "PCICLK: ccfg:%08lx\n",
(unsigned long)__raw_readq(&tx4927_ccfgptr->ccfg));
} else
pciclk = -1;
return pciclk;
}
void __init tx4927_setup_pcierr_irq(void)
{
if (request_irq(TXX9_IRQ_BASE + TX4927_IR_PCIERR,
tx4927_pcierr_interrupt,
0, "PCI error",
(void *)TX4927_PCIC_REG))
printk(KERN_WARNING "Failed to request irq for PCIERR\n");
}

142
arch/mips/pci/pci-tx4938.c Normal file
View file

@ -0,0 +1,142 @@
/*
* Based on linux/arch/mips/txx9/rbtx4938/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
* Copyright 2001, 2003-2005 MontaVista Software Inc.
* Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
* (C) Copyright TOSHIBA CORPORATION 2000-2001, 2004-2007
*
* 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.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <asm/txx9/generic.h>
#include <asm/txx9/tx4938.h>
int __init tx4938_report_pciclk(void)
{
int pciclk = 0;
printk(KERN_INFO "PCIC --%s PCICLK:",
(__raw_readq(&tx4938_ccfgptr->ccfg) & TX4938_CCFG_PCI66) ?
" PCI66" : "");
if (__raw_readq(&tx4938_ccfgptr->pcfg) & TX4938_PCFG_PCICLKEN_ALL) {
u64 ccfg = __raw_readq(&tx4938_ccfgptr->ccfg);
switch ((unsigned long)ccfg &
TX4938_CCFG_PCIDIVMODE_MASK) {
case TX4938_CCFG_PCIDIVMODE_4:
pciclk = txx9_cpu_clock / 4; break;
case TX4938_CCFG_PCIDIVMODE_4_5:
pciclk = txx9_cpu_clock * 2 / 9; break;
case TX4938_CCFG_PCIDIVMODE_5:
pciclk = txx9_cpu_clock / 5; break;
case TX4938_CCFG_PCIDIVMODE_5_5:
pciclk = txx9_cpu_clock * 2 / 11; break;
case TX4938_CCFG_PCIDIVMODE_8:
pciclk = txx9_cpu_clock / 8; break;
case TX4938_CCFG_PCIDIVMODE_9:
pciclk = txx9_cpu_clock / 9; break;
case TX4938_CCFG_PCIDIVMODE_10:
pciclk = txx9_cpu_clock / 10; break;
case TX4938_CCFG_PCIDIVMODE_11:
pciclk = txx9_cpu_clock / 11; break;
}
printk("Internal(%u.%uMHz)",
(pciclk + 50000) / 1000000,
((pciclk + 50000) / 100000) % 10);
} else {
printk("External");
pciclk = -1;
}
printk("\n");
return pciclk;
}
void __init tx4938_report_pci1clk(void)
{
__u64 ccfg = __raw_readq(&tx4938_ccfgptr->ccfg);
unsigned int pciclk =
txx9_gbus_clock / ((ccfg & TX4938_CCFG_PCI1DMD) ? 4 : 2);
printk(KERN_INFO "PCIC1 -- %sPCICLK:%u.%uMHz\n",
(ccfg & TX4938_CCFG_PCI1_66) ? "PCI66 " : "",
(pciclk + 50000) / 1000000,
((pciclk + 50000) / 100000) % 10);
}
int __init tx4938_pciclk66_setup(void)
{
int pciclk;
/* Assert M66EN */
tx4938_ccfg_set(TX4938_CCFG_PCI66);
/* Double PCICLK (if possible) */
if (__raw_readq(&tx4938_ccfgptr->pcfg) & TX4938_PCFG_PCICLKEN_ALL) {
unsigned int pcidivmode = 0;
u64 ccfg = __raw_readq(&tx4938_ccfgptr->ccfg);
pcidivmode = (unsigned long)ccfg &
TX4938_CCFG_PCIDIVMODE_MASK;
switch (pcidivmode) {
case TX4938_CCFG_PCIDIVMODE_8:
case TX4938_CCFG_PCIDIVMODE_4:
pcidivmode = TX4938_CCFG_PCIDIVMODE_4;
pciclk = txx9_cpu_clock / 4;
break;
case TX4938_CCFG_PCIDIVMODE_9:
case TX4938_CCFG_PCIDIVMODE_4_5:
pcidivmode = TX4938_CCFG_PCIDIVMODE_4_5;
pciclk = txx9_cpu_clock * 2 / 9;
break;
case TX4938_CCFG_PCIDIVMODE_10:
case TX4938_CCFG_PCIDIVMODE_5:
pcidivmode = TX4938_CCFG_PCIDIVMODE_5;
pciclk = txx9_cpu_clock / 5;
break;
case TX4938_CCFG_PCIDIVMODE_11:
case TX4938_CCFG_PCIDIVMODE_5_5:
default:
pcidivmode = TX4938_CCFG_PCIDIVMODE_5_5;
pciclk = txx9_cpu_clock * 2 / 11;
break;
}
tx4938_ccfg_change(TX4938_CCFG_PCIDIVMODE_MASK,
pcidivmode);
printk(KERN_DEBUG "PCICLK: ccfg:%08lx\n",
(unsigned long)__raw_readq(&tx4938_ccfgptr->ccfg));
} else
pciclk = -1;
return pciclk;
}
int __init tx4938_pcic1_map_irq(const struct pci_dev *dev, u8 slot)
{
if (get_tx4927_pcicptr(dev->bus->sysdata) == tx4938_pcic1ptr) {
switch (slot) {
case TX4927_PCIC_IDSEL_AD_TO_SLOT(31):
if (__raw_readq(&tx4938_ccfgptr->pcfg) &
TX4938_PCFG_ETH0_SEL)
return TXX9_IRQ_BASE + TX4938_IR_ETH0;
break;
case TX4927_PCIC_IDSEL_AD_TO_SLOT(30):
if (__raw_readq(&tx4938_ccfgptr->pcfg) &
TX4938_PCFG_ETH1_SEL)
return TXX9_IRQ_BASE + TX4938_IR_ETH1;
break;
}
return 0;
}
return -1;
}
void __init tx4938_setup_pcierr_irq(void)
{
if (request_irq(TXX9_IRQ_BASE + TX4938_IR_PCIERR,
tx4927_pcierr_interrupt,
0, "PCI error",
(void *)TX4927_PCIC_REG))
printk(KERN_WARNING "Failed to request irq for PCIERR\n");
}

107
arch/mips/pci/pci-tx4939.c Normal file
View file

@ -0,0 +1,107 @@
/*
* Based on linux/arch/mips/txx9/rbtx4939/setup.c,
* and RBTX49xx patch from CELF patch archive.
*
* Copyright 2001, 2003-2005 MontaVista Software Inc.
* Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
* (C) Copyright TOSHIBA CORPORATION 2000-2001, 2004-2007
*
* 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.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <asm/txx9/generic.h>
#include <asm/txx9/tx4939.h>
int __init tx4939_report_pciclk(void)
{
int pciclk = 0;
pr_info("PCIC --%s PCICLK:",
(__raw_readq(&tx4939_ccfgptr->ccfg) & TX4939_CCFG_PCI66) ?
" PCI66" : "");
if (__raw_readq(&tx4939_ccfgptr->pcfg) & TX4939_PCFG_PCICLKEN_ALL) {
pciclk = txx9_master_clock * 20 / 6;
if (!(__raw_readq(&tx4939_ccfgptr->ccfg) & TX4939_CCFG_PCI66))
pciclk /= 2;
printk(KERN_CONT "Internal(%u.%uMHz)",
(pciclk + 50000) / 1000000,
((pciclk + 50000) / 100000) % 10);
} else {
printk(KERN_CONT "External");
pciclk = -1;
}
printk(KERN_CONT "\n");
return pciclk;
}
void __init tx4939_report_pci1clk(void)
{
unsigned int pciclk = txx9_master_clock * 20 / 6;
pr_info("PCIC1 -- PCICLK:%u.%uMHz\n",
(pciclk + 50000) / 1000000,
((pciclk + 50000) / 100000) % 10);
}
int __init tx4939_pcic1_map_irq(const struct pci_dev *dev, u8 slot)
{
if (get_tx4927_pcicptr(dev->bus->sysdata) == tx4939_pcic1ptr) {
switch (slot) {
case TX4927_PCIC_IDSEL_AD_TO_SLOT(31):
if (__raw_readq(&tx4939_ccfgptr->pcfg) &
TX4939_PCFG_ET0MODE)
return TXX9_IRQ_BASE + TX4939_IR_ETH(0);
break;
case TX4927_PCIC_IDSEL_AD_TO_SLOT(30):
if (__raw_readq(&tx4939_ccfgptr->pcfg) &
TX4939_PCFG_ET1MODE)
return TXX9_IRQ_BASE + TX4939_IR_ETH(1);
break;
}
return 0;
}
return -1;
}
int __init tx4939_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq = tx4939_pcic1_map_irq(dev, slot);
if (irq >= 0)
return irq;
irq = pin;
/* IRQ rotation */
irq--; /* 0-3 */
irq = (irq + 33 - slot) % 4;
irq++; /* 1-4 */
switch (irq) {
case 1:
irq = TXX9_IRQ_BASE + TX4939_IR_INTA;
break;
case 2:
irq = TXX9_IRQ_BASE + TX4939_IR_INTB;
break;
case 3:
irq = TXX9_IRQ_BASE + TX4939_IR_INTC;
break;
case 4:
irq = TXX9_IRQ_BASE + TX4939_IR_INTD;
break;
}
return irq;
}
void __init tx4939_setup_pcierr_irq(void)
{
if (request_irq(TXX9_IRQ_BASE + TX4939_IR_PCIERR,
tx4927_pcierr_interrupt,
0, "PCI error",
(void *)TX4939_PCIC_REG))
pr_warning("Failed to request irq for PCIERR\n");
}

View file

@ -0,0 +1,131 @@
/*
* 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) 2013 Cavium, Inc.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <uapi/asm/bitfield.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#define PCI_CONFIG_ADDRESS 0xcf8
#define PCI_CONFIG_DATA 0xcfc
union pci_config_address {
struct {
__BITFIELD_FIELD(unsigned enable_bit : 1, /* 31 */
__BITFIELD_FIELD(unsigned reserved : 7, /* 30 .. 24 */
__BITFIELD_FIELD(unsigned bus_number : 8, /* 23 .. 16 */
__BITFIELD_FIELD(unsigned devfn_number : 8, /* 15 .. 8 */
__BITFIELD_FIELD(unsigned register_number : 8, /* 7 .. 0 */
)))));
};
u32 w;
};
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return ((pin + slot) % 4)+ MIPS_IRQ_PCIA;
}
static void pci_virtio_guest_write_config_addr(struct pci_bus *bus,
unsigned int devfn, int reg)
{
union pci_config_address pca = { .w = 0 };
pca.register_number = reg;
pca.devfn_number = devfn;
pca.bus_number = bus->number;
pca.enable_bit = 1;
outl(pca.w, PCI_CONFIG_ADDRESS);
}
static int pci_virtio_guest_write_config(struct pci_bus *bus,
unsigned int devfn, int reg, int size, u32 val)
{
pci_virtio_guest_write_config_addr(bus, devfn, reg);
switch (size) {
case 1:
outb(val, PCI_CONFIG_DATA + (reg & 3));
break;
case 2:
outw(val, PCI_CONFIG_DATA + (reg & 2));
break;
case 4:
outl(val, PCI_CONFIG_DATA);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int pci_virtio_guest_read_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 *val)
{
pci_virtio_guest_write_config_addr(bus, devfn, reg);
switch (size) {
case 1:
*val = inb(PCI_CONFIG_DATA + (reg & 3));
break;
case 2:
*val = inw(PCI_CONFIG_DATA + (reg & 2));
break;
case 4:
*val = inl(PCI_CONFIG_DATA);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops pci_virtio_guest_ops = {
.read = pci_virtio_guest_read_config,
.write = pci_virtio_guest_write_config,
};
static struct resource pci_virtio_guest_mem_resource = {
.name = "Virtio MEM",
.flags = IORESOURCE_MEM,
.start = 0x10000000,
.end = 0x1dffffff
};
static struct resource pci_virtio_guest_io_resource = {
.name = "Virtio IO",
.flags = IORESOURCE_IO,
.start = 0,
.end = 0xffff
};
static struct pci_controller pci_virtio_guest_controller = {
.pci_ops = &pci_virtio_guest_ops,
.mem_resource = &pci_virtio_guest_mem_resource,
.io_resource = &pci_virtio_guest_io_resource,
};
static int __init pci_virtio_guest_setup(void)
{
pr_err("pci_virtio_guest_setup\n");
/* Virtio comes pre-assigned */
pci_set_flags(PCI_PROBE_ONLY);
pci_virtio_guest_controller.io_map_base = mips_io_port_base;
register_pci_controller(&pci_virtio_guest_controller);
return 0;
}
arch_initcall(pci_virtio_guest_setup);

320
arch/mips/pci/pci-vr41xx.c Normal file
View file

@ -0,0 +1,320 @@
/*
* pci-vr41xx.c, PCI Control Unit routines for the NEC VR4100 series.
*
* Copyright (C) 2001-2003 MontaVista Software Inc.
* Author: Yoichi Yuasa <source@mvista.com>
* Copyright (C) 2004-2008 Yoichi Yuasa <yuasa@linux-mips.org>
* 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. <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/pci.h>
#include <asm/vr41xx/vr41xx.h>
#include "pci-vr41xx.h"
extern struct pci_ops vr41xx_pci_ops;
static void __iomem *pciu_base;
#define pciu_read(offset) readl(pciu_base + (offset))
#define pciu_write(offset, value) writel((value), pciu_base + (offset))
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;
if (request_mem_region(PCIU_BASE, PCIU_SIZE, "PCIU") == NULL)
return -EBUSY;
pciu_base = ioremap(PCIU_BASE, PCIU_SIZE);
if (pciu_base == NULL) {
release_mem_region(PCIU_BASE, PCIU_SIZE);
return -EBUSY;
}
/* 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)
pciu_write(PCICLKSELREG, EQUAL_VTCLOCK);
else if ((vtclock / 2) < pci_clock_max)
pciu_write(PCICLKSELREG, HALF_VTCLOCK);
else if (current_cpu_data.processor_id >= PRID_VR4131_REV2_1 &&
(vtclock / 3) < pci_clock_max)
pciu_write(PCICLKSELREG, ONE_THIRD_VTCLOCK);
else if ((vtclock / 4) < pci_clock_max)
pciu_write(PCICLKSELREG, QUARTER_VTCLOCK);
else {
printk(KERN_ERR "PCI Clock is over 33MHz.\n");
iounmap(pciu_base);
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);
pciu_write(PCIMMAW1REG, val);
} else {
val = pciu_read(PCIMMAW1REG);
val &= ~WINEN;
pciu_write(PCIMMAW1REG, val);
}
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);
pciu_write(PCIMMAW2REG, val);
} else {
val = pciu_read(PCIMMAW2REG);
val &= ~WINEN;
pciu_write(PCIMMAW2REG, val);
}
if (setup->target_memory1 != NULL) {
target = setup->target_memory1;
val = TARGET_MSK(target->address_mask) |
WINEN |
ITA(target->bus_base_address);
pciu_write(PCITAW1REG, val);
} else {
val = pciu_read(PCITAW1REG);
val &= ~WINEN;
pciu_write(PCITAW1REG, val);
}
if (setup->target_memory2 != NULL) {
target = setup->target_memory2;
val = TARGET_MSK(target->address_mask) |
WINEN |
ITA(target->bus_base_address);
pciu_write(PCITAW2REG, val);
} else {
val = pciu_read(PCITAW2REG);
val &= ~WINEN;
pciu_write(PCITAW2REG, val);
}
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);
pciu_write(PCIMIOAWREG, val);
} else {
val = pciu_read(PCIMIOAWREG);
val &= ~WINEN;
pciu_write(PCIMIOAWREG, val);
}
if (setup->exclusive_access == CANNOT_LOCK_FROM_DEVICE)
pciu_write(PCIEXACCREG, UNLOCK);
else
pciu_write(PCIEXACCREG, 0);
if (current_cpu_type() == CPU_VR4122)
pciu_write(PCITRDYVREG, TRDYV(setup->wait_time_limit_from_irdy_to_trdy));
pciu_write(LATTIMEREG, MLTIM(setup->master_latency_timer));
if (setup->mailbox != NULL) {
mailbox = setup->mailbox;
val = MBADD(mailbox->base_address) | TYPE_32BITSPACE |
MSI_MEMORY | PREF_APPROVAL;
pciu_write(MAILBAREG, val);
}
if (setup->target_window1) {
window = setup->target_window1;
val = PMBA(window->base_address) | TYPE_32BITSPACE |
MSI_MEMORY | PREF_APPROVAL;
pciu_write(PCIMBA1REG, val);
}
if (setup->target_window2) {
window = setup->target_window2;
val = PMBA(window->base_address) | TYPE_32BITSPACE |
MSI_MEMORY | PREF_APPROVAL;
pciu_write(PCIMBA2REG, val);
}
val = pciu_read(RETVALREG);
val &= ~RTYVAL_MASK;
val |= RTYVAL(setup->retry_limit);
pciu_write(RETVALREG, val);
val = pciu_read(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;
pciu_write(PCIAPCNTREG, val);
pciu_write(COMMANDREG, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
PCI_COMMAND_SERR);
/* Clear bus error */
pciu_read(BUSERRADREG);
pciu_write(PCIENREG, PCIU_CONFIG_DONE);
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;
}
if (setup->master_io) {
void __iomem *io_map_base;
struct resource *res = vr41xx_pci_controller.io_resource;
master = setup->master_io;
io_map_base = ioremap(master->bus_base_address,
resource_size(res));
if (!io_map_base)
return -EBUSY;
vr41xx_pci_controller.io_map_base = (unsigned long)io_map_base;
}
register_pci_controller(&vr41xx_pci_controller);
return 0;
}
arch_initcall(vr41xx_pciu_init);

154
arch/mips/pci/pci-vr41xx.h Normal file
View file

@ -0,0 +1,154 @@
/*
* pci-vr41xx.h, Include file for PCI Control Unit of the NEC VR4100 series.
*
* Copyright (C) 2002 MontaVista Software Inc.
* Author: Yoichi Yuasa <source@mvista.com>
* Copyright (C) 2004-2005 Yoichi Yuasa <yuasa@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
*/
#ifndef __PCI_VR41XX_H
#define __PCI_VR41XX_H
#define PCIU_BASE 0x0f000c00UL
#define PCIU_SIZE 0x200UL
#define PCIMMAW1REG 0x00
#define PCIMMAW2REG 0x04
#define PCITAW1REG 0x08
#define PCITAW2REG 0x0c
#define PCIMIOAWREG 0x10
#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 0x14
#define PCICONFAREG 0x18
#define PCIMAILREG 0x1c
#define BUSERRADREG 0x24
#define EA(reg) ((reg) &0xfffffffc)
#define INTCNTSTAREG 0x28
#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 0x2c
#define UNLOCK 0x2U
#define EAREQ 0x1U
#define PCIRECONTREG 0x30
#define RTRYCNT(reg) ((reg) & 0x000000ffU)
#define PCIENREG 0x34
#define PCIU_CONFIG_DONE 0x4U
#define PCICLKSELREG 0x38
#define EQUAL_VTCLOCK 0x2U
#define HALF_VTCLOCK 0x0U
#define ONE_THIRD_VTCLOCK 0x3U
#define QUARTER_VTCLOCK 0x1U
#define PCITRDYVREG 0x3c
#define TRDYV(val) ((uint32_t)(val) & 0xffU)
#define PCICLKRUNREG 0x60
#define VENDORIDREG 0x100
#define DEVICEIDREG 0x100
#define COMMANDREG 0x104
#define STATUSREG 0x104
#define REVIDREG 0x108
#define CLASSREG 0x108
#define CACHELSREG 0x10c
#define LATTIMEREG 0x10c
#define MLTIM(val) (((uint32_t)(val) << 7) & 0xff00U)
#define MAILBAREG 0x110
#define PCIMBA1REG 0x114
#define PCIMBA2REG 0x118
#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 0x13c
#define INTPINREG 0x13c
#define RETVALREG 0x140
#define PCIAPCNTREG 0x140
#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 */

332
arch/mips/pci/pci-xlp.c Normal file
View file

@ -0,0 +1,332 @@
/*
* Copyright (c) 2003-2012 Broadcom Corporation
* All Rights Reserved
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the Broadcom
* license below:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY BROADCOM ``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 BROADCOM OR CONTRIBUTORS 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.
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/msi.h>
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/console.h>
#include <asm/io.h>
#include <asm/netlogic/interrupt.h>
#include <asm/netlogic/haldefs.h>
#include <asm/netlogic/common.h>
#include <asm/netlogic/mips-extns.h>
#include <asm/netlogic/xlp-hal/iomap.h>
#include <asm/netlogic/xlp-hal/xlp.h>
#include <asm/netlogic/xlp-hal/pic.h>
#include <asm/netlogic/xlp-hal/pcibus.h>
#include <asm/netlogic/xlp-hal/bridge.h>
static void *pci_config_base;
#define pci_cfg_addr(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
/* PCI ops */
static inline u32 pci_cfg_read_32bit(struct pci_bus *bus, unsigned int devfn,
int where)
{
u32 data;
u32 *cfgaddr;
where &= ~3;
if (cpu_is_xlp9xx()) {
/* be very careful on SoC buses */
if (bus->number == 0) {
/* Scan only existing nodes - uboot bug? */
if (PCI_SLOT(devfn) != 0 ||
!nlm_node_present(PCI_FUNC(devfn)))
return 0xffffffff;
} else if (bus->parent->number == 0) { /* SoC bus */
if (PCI_SLOT(devfn) == 0) /* b.0.0 hangs */
return 0xffffffff;
if (devfn == 44) /* b.5.4 hangs */
return 0xffffffff;
}
} else if (bus->number == 0 && PCI_SLOT(devfn) == 1 && where == 0x954) {
return 0xffffffff;
}
cfgaddr = (u32 *)(pci_config_base +
pci_cfg_addr(bus->number, devfn, where));
data = *cfgaddr;
return data;
}
static inline void pci_cfg_write_32bit(struct pci_bus *bus, unsigned int devfn,
int where, u32 data)
{
u32 *cfgaddr;
cfgaddr = (u32 *)(pci_config_base +
pci_cfg_addr(bus->number, devfn, where & ~3));
*cfgaddr = data;
}
static int nlm_pcibios_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u32 data;
if ((size == 2) && (where & 1))
return PCIBIOS_BAD_REGISTER_NUMBER;
else if ((size == 4) && (where & 3))
return PCIBIOS_BAD_REGISTER_NUMBER;
data = pci_cfg_read_32bit(bus, devfn, where);
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 nlm_pcibios_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
u32 data;
if ((size == 2) && (where & 1))
return PCIBIOS_BAD_REGISTER_NUMBER;
else if ((size == 4) && (where & 3))
return PCIBIOS_BAD_REGISTER_NUMBER;
data = pci_cfg_read_32bit(bus, devfn, where);
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;
pci_cfg_write_32bit(bus, devfn, where, data);
return PCIBIOS_SUCCESSFUL;
}
struct pci_ops nlm_pci_ops = {
.read = nlm_pcibios_read,
.write = nlm_pcibios_write
};
static struct resource nlm_pci_mem_resource = {
.name = "XLP PCI MEM",
.start = 0xd0000000UL, /* 256MB PCI mem @ 0xd000_0000 */
.end = 0xdfffffffUL,
.flags = IORESOURCE_MEM,
};
static struct resource nlm_pci_io_resource = {
.name = "XLP IO MEM",
.start = 0x14000000UL, /* 64MB PCI IO @ 0x1000_0000 */
.end = 0x17ffffffUL,
.flags = IORESOURCE_IO,
};
struct pci_controller nlm_pci_controller = {
.index = 0,
.pci_ops = &nlm_pci_ops,
.mem_resource = &nlm_pci_mem_resource,
.mem_offset = 0x00000000UL,
.io_resource = &nlm_pci_io_resource,
.io_offset = 0x00000000UL,
};
struct pci_dev *xlp_get_pcie_link(const struct pci_dev *dev)
{
struct pci_bus *bus, *p;
bus = dev->bus;
if (cpu_is_xlp9xx()) {
/* find bus with grand parent number == 0 */
for (p = bus->parent; p && p->parent && p->parent->number != 0;
p = p->parent)
bus = p;
return (p && p->parent) ? bus->self : NULL;
} else {
/* Find the bridge on bus 0 */
for (p = bus->parent; p && p->number != 0; p = p->parent)
bus = p;
return p ? bus->self : NULL;
}
}
int xlp_socdev_to_node(const struct pci_dev *lnkdev)
{
if (cpu_is_xlp9xx())
return PCI_FUNC(lnkdev->bus->self->devfn);
else
return PCI_SLOT(lnkdev->devfn) / 8;
}
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
struct pci_dev *lnkdev;
int lnkfunc, node;
/*
* For XLP PCIe, there is an IRQ per Link, find out which
* link the device is on to assign interrupts
*/
lnkdev = xlp_get_pcie_link(dev);
if (lnkdev == NULL)
return 0;
lnkfunc = PCI_FUNC(lnkdev->devfn);
node = xlp_socdev_to_node(lnkdev);
return nlm_irq_to_xirq(node, PIC_PCIE_LINK_LEGACY_IRQ(lnkfunc));
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
/*
* If big-endian, enable hardware byteswap on the PCIe bridges.
* This will make both the SoC and PCIe devices behave consistently with
* readl/writel.
*/
#ifdef __BIG_ENDIAN
static void xlp_config_pci_bswap(int node, int link)
{
uint64_t nbubase, lnkbase;
u32 reg;
nbubase = nlm_get_bridge_regbase(node);
lnkbase = nlm_get_pcie_base(node, link);
/*
* Enable byte swap in hardware. Program each link's PCIe SWAP regions
* from the link's address ranges.
*/
if (cpu_is_xlp9xx()) {
reg = nlm_read_bridge_reg(nbubase,
BRIDGE_9XX_PCIEMEM_BASE0 + link);
nlm_write_pci_reg(lnkbase, PCIE_9XX_BYTE_SWAP_MEM_BASE, reg);
reg = nlm_read_bridge_reg(nbubase,
BRIDGE_9XX_PCIEMEM_LIMIT0 + link);
nlm_write_pci_reg(lnkbase,
PCIE_9XX_BYTE_SWAP_MEM_LIM, reg | 0xfff);
reg = nlm_read_bridge_reg(nbubase,
BRIDGE_9XX_PCIEIO_BASE0 + link);
nlm_write_pci_reg(lnkbase, PCIE_9XX_BYTE_SWAP_IO_BASE, reg);
reg = nlm_read_bridge_reg(nbubase,
BRIDGE_9XX_PCIEIO_LIMIT0 + link);
nlm_write_pci_reg(lnkbase,
PCIE_9XX_BYTE_SWAP_IO_LIM, reg | 0xfff);
} else {
reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_BASE0 + link);
nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_BASE, reg);
reg = nlm_read_bridge_reg(nbubase,
BRIDGE_PCIEMEM_LIMIT0 + link);
nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_LIM, reg | 0xfff);
reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_BASE0 + link);
nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_BASE, reg);
reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_LIMIT0 + link);
nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff);
}
}
#else
/* Swap configuration not needed in little-endian mode */
static inline void xlp_config_pci_bswap(int node, int link) {}
#endif /* __BIG_ENDIAN */
static int __init pcibios_init(void)
{
uint64_t pciebase;
int link, n;
u32 reg;
/* Firmware assigns PCI resources */
pci_set_flags(PCI_PROBE_ONLY);
pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20);
/* Extend IO port for memory mapped io */
ioport_resource.start = 0;
ioport_resource.end = ~0;
for (n = 0; n < NLM_NR_NODES; n++) {
if (!nlm_node_present(n))
continue;
for (link = 0; link < PCIE_NLINKS; link++) {
pciebase = nlm_get_pcie_base(n, link);
if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff)
continue;
xlp_config_pci_bswap(n, link);
xlp_init_node_msi_irqs(n, link);
/* put in intpin and irq - u-boot does not */
reg = nlm_read_pci_reg(pciebase, 0xf);
reg &= ~0x1ffu;
reg |= (1 << 8) | PIC_PCIE_LINK_LEGACY_IRQ(link);
nlm_write_pci_reg(pciebase, 0xf, reg);
pr_info("XLP PCIe: Link %d-%d initialized.\n", n, link);
}
}
set_io_port_base(CKSEG1);
nlm_pci_controller.io_map_base = CKSEG1;
register_pci_controller(&nlm_pci_controller);
pr_info("XLP PCIe Controller %pR%pR.\n", &nlm_pci_io_resource,
&nlm_pci_mem_resource);
return 0;
}
arch_initcall(pcibios_init);

368
arch/mips/pci/pci-xlr.c Normal file
View file

@ -0,0 +1,368 @@
/*
* Copyright 2003-2011 NetLogic Microsystems, Inc. (NetLogic). All rights
* reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the NetLogic
* license below:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY NETLOGIC ``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 NETLOGIC OR CONTRIBUTORS 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.
*/
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/msi.h>
#include <linux/mm.h>
#include <linux/irq.h>
#include <linux/irqdesc.h>
#include <linux/console.h>
#include <linux/pci_regs.h>
#include <asm/io.h>
#include <asm/netlogic/interrupt.h>
#include <asm/netlogic/haldefs.h>
#include <asm/netlogic/common.h>
#include <asm/netlogic/xlr/msidef.h>
#include <asm/netlogic/xlr/iomap.h>
#include <asm/netlogic/xlr/pic.h>
#include <asm/netlogic/xlr/xlr.h>
static void *pci_config_base;
#define pci_cfg_addr(bus, devfn, off) (((bus) << 16) | ((devfn) << 8) | (off))
/* PCI ops */
static inline u32 pci_cfg_read_32bit(struct pci_bus *bus, unsigned int devfn,
int where)
{
u32 data;
u32 *cfgaddr;
cfgaddr = (u32 *)(pci_config_base +
pci_cfg_addr(bus->number, devfn, where & ~3));
data = *cfgaddr;
return cpu_to_le32(data);
}
static inline void pci_cfg_write_32bit(struct pci_bus *bus, unsigned int devfn,
int where, u32 data)
{
u32 *cfgaddr;
cfgaddr = (u32 *)(pci_config_base +
pci_cfg_addr(bus->number, devfn, where & ~3));
*cfgaddr = cpu_to_le32(data);
}
static int nlm_pcibios_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
u32 data;
if ((size == 2) && (where & 1))
return PCIBIOS_BAD_REGISTER_NUMBER;
else if ((size == 4) && (where & 3))
return PCIBIOS_BAD_REGISTER_NUMBER;
data = pci_cfg_read_32bit(bus, devfn, where);
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 nlm_pcibios_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
u32 data;
if ((size == 2) && (where & 1))
return PCIBIOS_BAD_REGISTER_NUMBER;
else if ((size == 4) && (where & 3))
return PCIBIOS_BAD_REGISTER_NUMBER;
data = pci_cfg_read_32bit(bus, devfn, where);
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;
pci_cfg_write_32bit(bus, devfn, where, data);
return PCIBIOS_SUCCESSFUL;
}
struct pci_ops nlm_pci_ops = {
.read = nlm_pcibios_read,
.write = nlm_pcibios_write
};
static struct resource nlm_pci_mem_resource = {
.name = "XLR PCI MEM",
.start = 0xd0000000UL, /* 256MB PCI mem @ 0xd000_0000 */
.end = 0xdfffffffUL,
.flags = IORESOURCE_MEM,
};
static struct resource nlm_pci_io_resource = {
.name = "XLR IO MEM",
.start = 0x10000000UL, /* 16MB PCI IO @ 0x1000_0000 */
.end = 0x100fffffUL,
.flags = IORESOURCE_IO,
};
struct pci_controller nlm_pci_controller = {
.index = 0,
.pci_ops = &nlm_pci_ops,
.mem_resource = &nlm_pci_mem_resource,
.mem_offset = 0x00000000UL,
.io_resource = &nlm_pci_io_resource,
.io_offset = 0x00000000UL,
};
/*
* The top level PCIe links on the XLS PCIe controller appear as
* bridges. Given a device, this function finds which link it is
* on.
*/
static struct pci_dev *xls_get_pcie_link(const struct pci_dev *dev)
{
struct pci_bus *bus, *p;
/* Find the bridge on bus 0 */
bus = dev->bus;
for (p = bus->parent; p && p->number != 0; p = p->parent)
bus = p;
return p ? bus->self : NULL;
}
static int nlm_pci_link_to_irq(int link)
{
switch (link) {
case 0:
return PIC_PCIE_LINK0_IRQ;
case 1:
return PIC_PCIE_LINK1_IRQ;
case 2:
if (nlm_chip_is_xls_b())
return PIC_PCIE_XLSB0_LINK2_IRQ;
else
return PIC_PCIE_LINK2_IRQ;
case 3:
if (nlm_chip_is_xls_b())
return PIC_PCIE_XLSB0_LINK3_IRQ;
else
return PIC_PCIE_LINK3_IRQ;
}
WARN(1, "Unexpected link %d\n", link);
return 0;
}
static int get_irq_vector(const struct pci_dev *dev)
{
struct pci_dev *lnk;
int link;
if (!nlm_chip_is_xls())
return PIC_PCIX_IRQ; /* for XLR just one IRQ */
lnk = xls_get_pcie_link(dev);
if (lnk == NULL)
return 0;
link = PCI_SLOT(lnk->devfn);
return nlm_pci_link_to_irq(link);
}
#ifdef CONFIG_PCI_MSI
void arch_teardown_msi_irq(unsigned int irq)
{
}
int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc)
{
struct msi_msg msg;
struct pci_dev *lnk;
int irq, ret;
u16 val;
/* MSI not supported on XLR */
if (!nlm_chip_is_xls())
return 1;
/*
* Enable MSI on the XLS PCIe controller bridge which was disabled
* at enumeration, the bridge MSI capability is at 0x50
*/
lnk = xls_get_pcie_link(dev);
if (lnk == NULL)
return 1;
pci_read_config_word(lnk, 0x50 + PCI_MSI_FLAGS, &val);
if ((val & PCI_MSI_FLAGS_ENABLE) == 0) {
val |= PCI_MSI_FLAGS_ENABLE;
pci_write_config_word(lnk, 0x50 + PCI_MSI_FLAGS, val);
}
irq = get_irq_vector(dev);
if (irq <= 0)
return 1;
msg.address_hi = MSI_ADDR_BASE_HI;
msg.address_lo = MSI_ADDR_BASE_LO |
MSI_ADDR_DEST_MODE_PHYSICAL |
MSI_ADDR_REDIRECTION_CPU;
msg.data = MSI_DATA_TRIGGER_EDGE |
MSI_DATA_LEVEL_ASSERT |
MSI_DATA_DELIVERY_FIXED;
ret = irq_set_msi_desc(irq, desc);
if (ret < 0)
return ret;
write_msi_msg(irq, &msg);
return 0;
}
#endif
/* Extra ACK needed for XLR on chip PCI controller */
static void xlr_pci_ack(struct irq_data *d)
{
uint64_t pcibase = nlm_mmio_base(NETLOGIC_IO_PCIX_OFFSET);
nlm_read_reg(pcibase, (0x140 >> 2));
}
/* Extra ACK needed for XLS on chip PCIe controller */
static void xls_pcie_ack(struct irq_data *d)
{
uint64_t pciebase_le = nlm_mmio_base(NETLOGIC_IO_PCIE_1_OFFSET);
switch (d->irq) {
case PIC_PCIE_LINK0_IRQ:
nlm_write_reg(pciebase_le, (0x90 >> 2), 0xffffffff);
break;
case PIC_PCIE_LINK1_IRQ:
nlm_write_reg(pciebase_le, (0x94 >> 2), 0xffffffff);
break;
case PIC_PCIE_LINK2_IRQ:
nlm_write_reg(pciebase_le, (0x190 >> 2), 0xffffffff);
break;
case PIC_PCIE_LINK3_IRQ:
nlm_write_reg(pciebase_le, (0x194 >> 2), 0xffffffff);
break;
}
}
/* For XLS B silicon, the 3,4 PCI interrupts are different */
static void xls_pcie_ack_b(struct irq_data *d)
{
uint64_t pciebase_le = nlm_mmio_base(NETLOGIC_IO_PCIE_1_OFFSET);
switch (d->irq) {
case PIC_PCIE_LINK0_IRQ:
nlm_write_reg(pciebase_le, (0x90 >> 2), 0xffffffff);
break;
case PIC_PCIE_LINK1_IRQ:
nlm_write_reg(pciebase_le, (0x94 >> 2), 0xffffffff);
break;
case PIC_PCIE_XLSB0_LINK2_IRQ:
nlm_write_reg(pciebase_le, (0x190 >> 2), 0xffffffff);
break;
case PIC_PCIE_XLSB0_LINK3_IRQ:
nlm_write_reg(pciebase_le, (0x194 >> 2), 0xffffffff);
break;
}
}
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return get_irq_vector(dev);
}
/* Do platform specific device initialization at pci_enable_device() time */
int pcibios_plat_dev_init(struct pci_dev *dev)
{
return 0;
}
static int __init pcibios_init(void)
{
void (*extra_ack)(struct irq_data *);
int link, irq;
/* PSB assigns PCI resources */
pci_set_flags(PCI_PROBE_ONLY);
pci_config_base = ioremap(DEFAULT_PCI_CONFIG_BASE, 16 << 20);
/* Extend IO port for memory mapped io */
ioport_resource.start = 0;
ioport_resource.end = ~0;
set_io_port_base(CKSEG1);
nlm_pci_controller.io_map_base = CKSEG1;
pr_info("Registering XLR/XLS PCIX/PCIE Controller.\n");
register_pci_controller(&nlm_pci_controller);
/*
* For PCI interrupts, we need to ack the PCI controller too, overload
* irq handler data to do this
*/
if (!nlm_chip_is_xls()) {
/* XLR PCI controller ACK */
nlm_set_pic_extra_ack(0, PIC_PCIX_IRQ, xlr_pci_ack);
} else {
if (nlm_chip_is_xls_b())
extra_ack = xls_pcie_ack_b;
else
extra_ack = xls_pcie_ack;
for (link = 0; link < 4; link++) {
irq = nlm_pci_link_to_irq(link);
nlm_set_pic_extra_ack(0, irq, extra_ack);
}
}
return 0;
}
arch_initcall(pcibios_init);

351
arch/mips/pci/pci.c Normal file
View file

@ -0,0 +1,351 @@
/*
* 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, 11 Ralf Baechle (ralf@linux-mips.org)
* Copyright (C) 2011 Wind River Systems,
* written by Ralf Baechle (ralf@linux-mips.org)
*/
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/bootmem.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/of_address.h>
#include <asm/cpu-info.h>
/*
* If PCI_PROBE_ONLY in pci_flags is set, we don't change any PCI resource
* assignments.
*/
/*
* The PCI controller list.
*/
static struct pci_controller *hose_head, **hose_tail = &hose_head;
unsigned long PCIBIOS_MIN_IO;
unsigned long PCIBIOS_MIN_MEM;
static int pci_initialized;
/*
* 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..
*/
resource_size_t
pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
struct pci_dev *dev = data;
struct pci_controller *hose = dev->sysdata;
resource_size_t 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;
}
return start;
}
static void pcibios_scanbus(struct pci_controller *hose)
{
static int next_busno;
static int need_domain_info;
LIST_HEAD(resources);
struct pci_bus *bus;
if (!hose->iommu)
PCI_DMA_BUS_IS_PHYS = 1;
if (hose->get_busno && pci_has_flag(PCI_PROBE_ONLY))
next_busno = (*hose->get_busno)();
pci_add_resource_offset(&resources,
hose->mem_resource, hose->mem_offset);
pci_add_resource_offset(&resources, hose->io_resource, hose->io_offset);
bus = pci_scan_root_bus(NULL, next_busno, hose->pci_ops, hose,
&resources);
if (!bus)
pci_free_resource_list(&resources);
hose->bus = bus;
need_domain_info = need_domain_info || hose->index;
hose->need_domain_info = need_domain_info;
if (bus) {
next_busno = bus->busn_res.end + 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;
}
if (!pci_has_flag(PCI_PROBE_ONLY)) {
pci_bus_size_bridges(bus);
pci_bus_assign_resources(bus);
}
}
}
#ifdef CONFIG_OF
void pci_load_of_ranges(struct pci_controller *hose, struct device_node *node)
{
struct of_pci_range range;
struct of_pci_range_parser parser;
pr_info("PCI host bridge %s ranges:\n", node->full_name);
hose->of_node = node;
if (of_pci_range_parser_init(&parser, node))
return;
for_each_of_pci_range(&parser, &range) {
struct resource *res = NULL;
switch (range.flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_IO:
pr_info(" IO 0x%016llx..0x%016llx\n",
range.cpu_addr,
range.cpu_addr + range.size - 1);
hose->io_map_base =
(unsigned long)ioremap(range.cpu_addr,
range.size);
res = hose->io_resource;
break;
case IORESOURCE_MEM:
pr_info(" MEM 0x%016llx..0x%016llx\n",
range.cpu_addr,
range.cpu_addr + range.size - 1);
res = hose->mem_resource;
break;
}
if (res != NULL)
of_pci_range_to_resource(&range, node, res);
}
}
struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
{
struct pci_controller *hose = bus->sysdata;
return of_node_get(hose->of_node);
}
#endif
static DEFINE_MUTEX(pci_scan_mutex);
void register_pci_controller(struct pci_controller *hose)
{
struct resource *parent;
parent = hose->mem_resource->parent;
if (!parent)
parent = &iomem_resource;
if (request_resource(parent, hose->mem_resource) < 0)
goto out;
parent = hose->io_resource->parent;
if (!parent)
parent = &ioport_resource;
if (request_resource(parent, hose->io_resource) < 0) {
release_resource(hose->mem_resource);
goto out;
}
*hose_tail = hose;
hose_tail = &hose->next;
/*
* Do not panic here but later - this might happen before console init.
*/
if (!hose->io_map_base) {
printk(KERN_WARNING
"registering PCI controller with io_map_base unset\n");
}
/*
* Scan the bus if it is register after the PCI subsystem
* initialization.
*/
if (pci_initialized) {
mutex_lock(&pci_scan_mutex);
pcibios_scanbus(hose);
mutex_unlock(&pci_scan_mutex);
}
return;
out:
printk(KERN_WARNING
"Skipping PCI bus scan due to resource conflict\n");
}
static void __init pcibios_set_cache_line_size(void)
{
struct cpuinfo_mips *c = &current_cpu_data;
unsigned int lsize;
/*
* Set PCI cacheline size to that of the highest level in the
* cache hierarchy.
*/
lsize = c->dcache.linesz;
lsize = c->scache.linesz ? : lsize;
lsize = c->tcache.linesz ? : lsize;
BUG_ON(!lsize);
pci_dfl_cache_line_size = lsize >> 2;
pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize);
}
static int __init pcibios_init(void)
{
struct pci_controller *hose;
pcibios_set_cache_line_size();
/* Scan all of the recorded PCI controllers. */
for (hose = hose_head; hose; hose = hose->next)
pcibios_scanbus(hose);
pci_fixup_irqs(pci_common_swizzle, pcibios_map_irq);
pci_initialized = 1;
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 < PCI_NUM_RESOURCES; idx++) {
/* Only set up the requested stuff */
if (!(mask & (1<<idx)))
continue;
r = &dev->resource[idx];
if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
continue;
if ((idx == PCI_ROM_RESOURCE) &&
(!(r->flags & IORESOURCE_ROM_ENABLE)))
continue;
if (!r->start && r->end) {
printk(KERN_ERR "PCI: Device %s not available "
"because of resource collisions\n",
pci_name(dev));
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
if (cmd != old_cmd) {
printk("PCI: Enabling device %s (%04x -> %04x)\n",
pci_name(dev), old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
return 0;
}
unsigned int pcibios_assign_all_busses(void)
{
return 1;
}
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);
}
void pcibios_fixup_bus(struct pci_bus *bus)
{
struct pci_dev *dev = bus->self;
if (pci_has_flag(PCI_PROBE_ONLY) && dev &&
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
pci_read_bridge_bases(bus);
}
}
EXPORT_SYMBOL(PCIBIOS_MIN_IO);
EXPORT_SYMBOL(PCIBIOS_MIN_MEM);
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state, int write_combine)
{
unsigned long prot;
/*
* I/O space can be accessed via normal processor loads and stores on
* this platform but for now we elect not to do this and portable
* drivers should not do this anyway.
*/
if (mmap_state == pci_mmap_io)
return -EINVAL;
/*
* Ignore write-combine; for now only return uncached mappings.
*/
prot = pgprot_val(vma->vm_page_prot);
prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED;
vma->vm_page_prot = __pgprot(prot);
return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
char * (*pcibios_plat_setup)(char *str) __initdata;
char *__init pcibios_setup(char *str)
{
if (pcibios_plat_setup)
return pcibios_plat_setup(str);
return str;
}

2091
arch/mips/pci/pcie-octeon.c Normal file

File diff suppressed because it is too large Load diff