aboutsummaryrefslogtreecommitdiff
path: root/arch/sh/drivers/pci
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/drivers/pci')
-rw-r--r--arch/sh/drivers/pci/Makefile2
-rw-r--r--arch/sh/drivers/pci/common.c64
-rw-r--r--arch/sh/drivers/pci/pci-sh7780.c29
-rw-r--r--arch/sh/drivers/pci/pci.c2
4 files changed, 96 insertions, 1 deletions
diff --git a/arch/sh/drivers/pci/Makefile b/arch/sh/drivers/pci/Makefile
index 2c458b602be..4a59e689087 100644
--- a/arch/sh/drivers/pci/Makefile
+++ b/arch/sh/drivers/pci/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for the PCI specific kernel interface routines under Linux.
#
-obj-y += pci.o
+obj-y += common.o pci.o
obj-$(CONFIG_CPU_SUBTYPE_SH7751) += pci-sh7751.o ops-sh4.o
obj-$(CONFIG_CPU_SUBTYPE_SH7751R) += pci-sh7751.o ops-sh4.o
diff --git a/arch/sh/drivers/pci/common.c b/arch/sh/drivers/pci/common.c
new file mode 100644
index 00000000000..f67c946a861
--- /dev/null
+++ b/arch/sh/drivers/pci/common.c
@@ -0,0 +1,64 @@
+#include <linux/pci.h>
+#include <linux/kernel.h>
+
+static int __init
+early_read_config_word(struct pci_channel *hose,
+ int top_bus, int bus, int devfn, int offset, u16 *value)
+{
+ struct pci_dev fake_dev;
+ struct pci_bus fake_bus;
+
+ fake_dev.bus = &fake_bus;
+ fake_dev.sysdata = hose;
+ fake_dev.devfn = devfn;
+ fake_bus.number = bus;
+ fake_bus.sysdata = hose;
+ fake_bus.ops = hose->pci_ops;
+
+ if (bus != top_bus)
+ /* Fake a parent bus structure. */
+ fake_bus.parent = &fake_bus;
+ else
+ fake_bus.parent = NULL;
+
+ return pci_read_config_word(&fake_dev, offset, value);
+}
+
+int __init pci_is_66mhz_capable(struct pci_channel *hose,
+ int top_bus, int current_bus)
+{
+ u32 pci_devfn;
+ unsigned short vid;
+ int cap66 = -1;
+ u16 stat;
+
+ printk(KERN_INFO "PCI: Checking 66MHz capabilities...\n");
+
+ for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
+ if (PCI_FUNC(pci_devfn))
+ continue;
+ if (early_read_config_word(hose, top_bus, current_bus,
+ pci_devfn, PCI_VENDOR_ID, &vid) !=
+ PCIBIOS_SUCCESSFUL)
+ continue;
+ if (vid == 0xffff)
+ continue;
+
+ /* check 66MHz capability */
+ if (cap66 < 0)
+ cap66 = 1;
+ if (cap66) {
+ early_read_config_word(hose, top_bus, current_bus,
+ pci_devfn, PCI_STATUS, &stat);
+ if (!(stat & PCI_STATUS_66MHZ)) {
+ printk(KERN_DEBUG
+ "PCI: %02x:%02x not 66MHz capable.\n",
+ current_bus, pci_devfn);
+ cap66 = 0;
+ break;
+ }
+ }
+ }
+
+ return cap66 > 0;
+}
diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c
index 8405c8fded6..b68f45b6451 100644
--- a/arch/sh/drivers/pci/pci-sh7780.c
+++ b/arch/sh/drivers/pci/pci-sh7780.c
@@ -41,6 +41,29 @@ static struct pci_channel sh7780_pci_controller = {
.io_map_base = SH7780_PCI_IO_BASE,
};
+static void __init sh7780_pci66_init(struct pci_channel *hose)
+{
+ unsigned int tmp;
+
+ if (!pci_is_66mhz_capable(hose, 0, 0))
+ return;
+
+ /* Enable register access */
+ tmp = __raw_readl(hose->reg_base + SH4_PCICR);
+ tmp |= SH4_PCICR_PREFIX;
+ __raw_writel(tmp, hose->reg_base + SH4_PCICR);
+
+ /* Enable 66MHz operation */
+ tmp = __raw_readw(hose->reg_base + PCI_STATUS);
+ tmp |= PCI_STATUS_66MHZ;
+ __raw_writew(tmp, hose->reg_base + PCI_STATUS);
+
+ /* Done */
+ tmp = __raw_readl(hose->reg_base + SH4_PCICR);
+ tmp |= SH4_PCICR_PREFIX | SH4_PCICR_CFIN;
+ __raw_writel(tmp, hose->reg_base + SH4_PCICR);
+}
+
static int __init sh7780_pci_init(void)
{
struct pci_channel *chan = &sh7780_pci_controller;
@@ -176,6 +199,12 @@ static int __init sh7780_pci_init(void)
register_pci_controller(chan);
+ sh7780_pci66_init(chan);
+
+ printk(KERN_NOTICE "PCI: Running at %dMHz.\n",
+ (__raw_readw(chan->reg_base + PCI_STATUS) & PCI_STATUS_66MHZ) ?
+ 66 : 33);
+
return 0;
}
arch_initcall(sh7780_pci_init);
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
index 45a15cab01d..63b11fddffe 100644
--- a/arch/sh/drivers/pci/pci.c
+++ b/arch/sh/drivers/pci/pci.c
@@ -88,6 +88,8 @@ void __devinit register_pci_controller(struct pci_channel *hose)
mutex_unlock(&pci_scan_mutex);
}
+ return;
+
out:
printk(KERN_WARNING "Skipping PCI bus scan due to resource conflict\n");
}