aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sh/drivers')
-rw-r--r--arch/sh/drivers/dma/dma-api.c4
-rw-r--r--arch/sh/drivers/pci/Makefile1
-rw-r--r--arch/sh/drivers/pci/fixups-sdk7786.c67
-rw-r--r--arch/sh/drivers/pci/ops-sh4.c11
-rw-r--r--arch/sh/drivers/pci/ops-sh7786.c69
-rw-r--r--arch/sh/drivers/pci/pci-sh7751.c2
-rw-r--r--arch/sh/drivers/pci/pci-sh7780.c2
-rw-r--r--arch/sh/drivers/pci/pci-sh7780.h6
-rw-r--r--arch/sh/drivers/pci/pci.c43
-rw-r--r--arch/sh/drivers/pci/pcie-sh7786.c251
-rw-r--r--arch/sh/drivers/pci/pcie-sh7786.h56
11 files changed, 387 insertions, 125 deletions
diff --git a/arch/sh/drivers/dma/dma-api.c b/arch/sh/drivers/dma/dma-api.c
index 4a277224a871..f46848f088e4 100644
--- a/arch/sh/drivers/dma/dma-api.c
+++ b/arch/sh/drivers/dma/dma-api.c
@@ -412,8 +412,8 @@ EXPORT_SYMBOL(unregister_dmac);
static int __init dma_api_init(void)
{
printk(KERN_NOTICE "DMA: Registering DMA API.\n");
- create_proc_read_entry("dma", 0, 0, dma_read_proc, 0);
- return 0;
+ return create_proc_read_entry("dma", 0, 0, dma_read_proc, 0)
+ ? 0 : -ENOMEM;
}
subsys_initcall(dma_api_init);
diff --git a/arch/sh/drivers/pci/Makefile b/arch/sh/drivers/pci/Makefile
index 4a59e6890876..82f0a335fd19 100644
--- a/arch/sh/drivers/pci/Makefile
+++ b/arch/sh/drivers/pci/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_SH_RTS7751R2D) += fixups-rts7751r2d.o
obj-$(CONFIG_SH_SH03) += fixups-sh03.o
obj-$(CONFIG_SH_HIGHLANDER) += fixups-r7780rp.o
obj-$(CONFIG_SH_SH7785LCR) += fixups-r7780rp.o
+obj-$(CONFIG_SH_SDK7786) += fixups-sdk7786.o
obj-$(CONFIG_SH_SDK7780) += fixups-sdk7780.o
obj-$(CONFIG_SH_7780_SOLUTION_ENGINE) += fixups-sdk7780.o
obj-$(CONFIG_SH_TITAN) += fixups-titan.o
diff --git a/arch/sh/drivers/pci/fixups-sdk7786.c b/arch/sh/drivers/pci/fixups-sdk7786.c
new file mode 100644
index 000000000000..0e18ee332553
--- /dev/null
+++ b/arch/sh/drivers/pci/fixups-sdk7786.c
@@ -0,0 +1,67 @@
+/*
+ * SDK7786 FPGA PCIe mux handling
+ *
+ * Copyright (C) 2010 Paul Mundt
+ *
+ * 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.
+ */
+#define pr_fmt(fmt) "PCI: " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <mach/fpga.h>
+
+/*
+ * The SDK7786 FPGA supports mangling of most of the slots in some way or
+ * another. Slots 3/4 are special in that only one can be supported at a
+ * time, and both appear on port 3 to the PCI bus scan. Enabling slot 4
+ * (the horizontal edge connector) will disable slot 3 entirely.
+ *
+ * Misconfigurations can be detected through the FPGA via the slot
+ * resistors to determine card presence. Hotplug remains unsupported.
+ */
+static unsigned int slot4en __devinitdata;
+
+char *__devinit pcibios_setup(char *str)
+{
+ if (strcmp(str, "slot4en") == 0) {
+ slot4en = 1;
+ return NULL;
+ }
+
+ return str;
+}
+
+static int __init sdk7786_pci_init(void)
+{
+ u16 data = fpga_read_reg(PCIECR);
+
+ /*
+ * Enable slot #4 if it's been specified on the command line.
+ *
+ * Optionally reroute if slot #4 has a card present while slot #3
+ * does not, regardless of command line value.
+ *
+ * Card presence is logically inverted.
+ */
+ slot4en ?: (!(data & PCIECR_PRST4) && (data & PCIECR_PRST3));
+ if (slot4en) {
+ pr_info("Activating PCIe slot#4 (disabling slot#3)\n");
+
+ data &= ~PCIECR_PCIEMUX1;
+ fpga_write_reg(data, PCIECR);
+
+ /* Warn about forced rerouting if slot#3 is occupied */
+ if ((data & PCIECR_PRST3) == 0) {
+ pr_warning("Unreachable card detected in slot#3\n");
+ return -EBUSY;
+ }
+ } else
+ pr_info("PCIe slot#4 disabled\n");
+
+ return 0;
+}
+postcore_initcall(sdk7786_pci_init);
diff --git a/arch/sh/drivers/pci/ops-sh4.c b/arch/sh/drivers/pci/ops-sh4.c
index 0b81999fb88b..b6234203e0ac 100644
--- a/arch/sh/drivers/pci/ops-sh4.c
+++ b/arch/sh/drivers/pci/ops-sh4.c
@@ -9,6 +9,7 @@
*/
#include <linux/pci.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <asm/addrspace.h>
#include "pci-sh4.h"
@@ -18,8 +19,6 @@
#define CONFIG_CMD(bus, devfn, where) \
(0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
-static DEFINE_SPINLOCK(sh4_pci_lock);
-
/*
* Functions for accessing PCI configuration space with type 1 accesses
*/
@@ -34,10 +33,10 @@ static int sh4_pci_read(struct pci_bus *bus, unsigned int devfn,
* PCIPDR may only be accessed as 32 bit words,
* so we must do byte alignment by hand
*/
- spin_lock_irqsave(&sh4_pci_lock, flags);
+ raw_spin_lock_irqsave(&pci_config_lock, flags);
pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR);
data = pci_read_reg(chan, SH4_PCIPDR);
- spin_unlock_irqrestore(&sh4_pci_lock, flags);
+ raw_spin_unlock_irqrestore(&pci_config_lock, flags);
switch (size) {
case 1:
@@ -69,10 +68,10 @@ static int sh4_pci_write(struct pci_bus *bus, unsigned int devfn,
int shift;
u32 data;
- spin_lock_irqsave(&sh4_pci_lock, flags);
+ raw_spin_lock_irqsave(&pci_config_lock, flags);
pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR);
data = pci_read_reg(chan, SH4_PCIPDR);
- spin_unlock_irqrestore(&sh4_pci_lock, flags);
+ raw_spin_unlock_irqrestore(&pci_config_lock, flags);
switch (size) {
case 1:
diff --git a/arch/sh/drivers/pci/ops-sh7786.c b/arch/sh/drivers/pci/ops-sh7786.c
index 48f594b9582b..128421009e3f 100644
--- a/arch/sh/drivers/pci/ops-sh7786.c
+++ b/arch/sh/drivers/pci/ops-sh7786.c
@@ -1,7 +1,7 @@
/*
* Generic SH7786 PCI-Express operations.
*
- * Copyright (C) 2009 Paul Mundt
+ * Copyright (C) 2009 - 2010 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License v2. See the file "COPYING" in the main directory of this archive
@@ -19,37 +19,72 @@ enum {
PCI_ACCESS_WRITE,
};
-static DEFINE_SPINLOCK(sh7786_pcie_lock);
-
static int sh7786_pcie_config_access(unsigned char access_type,
struct pci_bus *bus, unsigned int devfn, int where, u32 *data)
{
struct pci_channel *chan = bus->sysdata;
- int dev, func;
+ int dev, func, type, reg;
dev = PCI_SLOT(devfn);
func = PCI_FUNC(devfn);
+ type = !!bus->parent;
+ reg = where & ~3;
if (bus->number > 255 || dev > 31 || func > 7)
return PCIBIOS_FUNC_NOT_SUPPORTED;
- if (devfn)
- return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /*
+ * While each channel has its own memory-mapped extended config
+ * space, it's generally only accessible when in endpoint mode.
+ * When in root complex mode, the controller is unable to target
+ * itself with either type 0 or type 1 accesses, and indeed, any
+ * controller initiated target transfer to its own config space
+ * result in a completer abort.
+ *
+ * Each channel effectively only supports a single device, but as
+ * the same channel <-> device access works for any PCI_SLOT()
+ * value, we cheat a bit here and bind the controller's config
+ * space to devfn 0 in order to enable self-enumeration. In this
+ * case the regular PAR/PDR path is sidelined and the mangled
+ * config access itself is initiated as a SuperHyway transaction.
+ */
+ if (pci_is_root_bus(bus)) {
+ if (dev == 0) {
+ if (access_type == PCI_ACCESS_READ)
+ *data = pci_read_reg(chan, PCI_REG(reg));
+ else
+ pci_write_reg(chan, *data, PCI_REG(reg));
+
+ return PCIBIOS_SUCCESSFUL;
+ } else if (dev > 1)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ /* Clear errors */
+ pci_write_reg(chan, pci_read_reg(chan, SH4A_PCIEERRFR), SH4A_PCIEERRFR);
/* Set the PIO address */
pci_write_reg(chan, (bus->number << 24) | (dev << 19) |
- (func << 16) | (where & ~3), SH4A_PCIEPAR);
+ (func << 16) | reg, SH4A_PCIEPAR);
/* Enable the configuration access */
- pci_write_reg(chan, (1 << 31), SH4A_PCIEPCTLR);
+ pci_write_reg(chan, (1 << 31) | (type << 8), SH4A_PCIEPCTLR);
+
+ /* Check for errors */
+ if (pci_read_reg(chan, SH4A_PCIEERRFR) & 0x10)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /* Check for master and target aborts */
+ if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28)))
+ return PCIBIOS_DEVICE_NOT_FOUND;
if (access_type == PCI_ACCESS_READ)
*data = pci_read_reg(chan, SH4A_PCIEPDR);
else
pci_write_reg(chan, *data, SH4A_PCIEPDR);
- /* Check for master and target aborts */
- if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28)))
- return PCIBIOS_DEVICE_NOT_FOUND;
+ /* Disable the configuration access */
+ pci_write_reg(chan, 0, SH4A_PCIEPCTLR);
return PCIBIOS_SUCCESSFUL;
}
@@ -66,11 +101,13 @@ static int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn,
else if ((size == 4) && (where & 3))
return PCIBIOS_BAD_REGISTER_NUMBER;
- spin_lock_irqsave(&sh7786_pcie_lock, flags);
+ raw_spin_lock_irqsave(&pci_config_lock, flags);
ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus,
devfn, where, &data);
- if (ret != PCIBIOS_SUCCESSFUL)
+ if (ret != PCIBIOS_SUCCESSFUL) {
+ *val = 0xffffffff;
goto out;
+ }
if (size == 1)
*val = (data >> ((where & 3) << 3)) & 0xff;
@@ -84,7 +121,7 @@ static int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn,
devfn, where, size, (unsigned long)*val);
out:
- spin_unlock_irqrestore(&sh7786_pcie_lock, flags);
+ raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return ret;
}
@@ -100,7 +137,7 @@ static int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn,
else if ((size == 4) && (where & 3))
return PCIBIOS_BAD_REGISTER_NUMBER;
- spin_lock_irqsave(&sh7786_pcie_lock, flags);
+ raw_spin_lock_irqsave(&pci_config_lock, flags);
ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus,
devfn, where, &data);
if (ret != PCIBIOS_SUCCESSFUL)
@@ -124,7 +161,7 @@ static int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn,
ret = sh7786_pcie_config_access(PCI_ACCESS_WRITE, bus,
devfn, where, &data);
out:
- spin_unlock_irqrestore(&sh7786_pcie_lock, flags);
+ raw_spin_unlock_irqrestore(&pci_config_lock, flags);
return ret;
}
diff --git a/arch/sh/drivers/pci/pci-sh7751.c b/arch/sh/drivers/pci/pci-sh7751.c
index f98141b3b7d7..86adb1e235cd 100644
--- a/arch/sh/drivers/pci/pci-sh7751.c
+++ b/arch/sh/drivers/pci/pci-sh7751.c
@@ -81,7 +81,7 @@ static int __init sh7751_pci_init(void)
unsigned int id;
u32 word, reg;
- printk(KERN_NOTICE "PCI: Starting intialization.\n");
+ printk(KERN_NOTICE "PCI: Starting initialization.\n");
chan->reg_base = 0xfe200000;
diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c
index ffdcbf10b95e..edb7cca14882 100644
--- a/arch/sh/drivers/pci/pci-sh7780.c
+++ b/arch/sh/drivers/pci/pci-sh7780.c
@@ -246,7 +246,7 @@ static int __init sh7780_pci_init(void)
const char *type;
int ret, i;
- printk(KERN_NOTICE "PCI: Starting intialization.\n");
+ printk(KERN_NOTICE "PCI: Starting initialization.\n");
chan->reg_base = 0xfe040000;
diff --git a/arch/sh/drivers/pci/pci-sh7780.h b/arch/sh/drivers/pci/pci-sh7780.h
index 205dcbefe275..1742e2c9db7a 100644
--- a/arch/sh/drivers/pci/pci-sh7780.h
+++ b/arch/sh/drivers/pci/pci-sh7780.h
@@ -12,12 +12,6 @@
#ifndef _PCI_SH7780_H_
#define _PCI_SH7780_H_
-#define PCI_VENDOR_ID_RENESAS 0x1912
-#define PCI_DEVICE_ID_RENESAS_SH7781 0x0001
-#define PCI_DEVICE_ID_RENESAS_SH7780 0x0002
-#define PCI_DEVICE_ID_RENESAS_SH7763 0x0004
-#define PCI_DEVICE_ID_RENESAS_SH7785 0x0007
-
/* SH7780 Control Registers */
#define PCIECR 0xFE000008
#define PCIECR_ENBL 0x01
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
index 1e9598d2bbf4..60ee09a4e121 100644
--- a/arch/sh/drivers/pci/pci.c
+++ b/arch/sh/drivers/pci/pci.c
@@ -19,6 +19,7 @@
#include <linux/dma-debug.h>
#include <linux/io.h>
#include <linux/mutex.h>
+#include <linux/spinlock.h>
unsigned long PCIBIOS_MIN_IO = 0x0000;
unsigned long PCIBIOS_MIN_MEM = 0;
@@ -56,6 +57,11 @@ static void __devinit pcibios_scanbus(struct pci_channel *hose)
}
}
+/*
+ * This interrupt-safe spinlock protects all accesses to PCI
+ * configuration space.
+ */
+DEFINE_RAW_SPINLOCK(pci_config_lock);
static DEFINE_MUTEX(pci_scan_mutex);
int __devinit register_pci_controller(struct pci_channel *hose)
@@ -233,40 +239,7 @@ void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
int pcibios_enable_device(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;
+ return pci_enable_resources(dev, mask);
}
/*
@@ -295,7 +268,7 @@ void __init pcibios_update_irq(struct pci_dev *dev, int irq)
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
}
-char * __devinit pcibios_setup(char *str)
+char * __devinit __weak pcibios_setup(char *str)
{
return str;
}
diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c
index 68cb9b0ac9d2..96e9b058aa1d 100644
--- a/arch/sh/drivers/pci/pcie-sh7786.c
+++ b/arch/sh/drivers/pci/pcie-sh7786.c
@@ -13,11 +13,14 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/sh_clk.h>
#include "pcie-sh7786.h"
#include <asm/sizes.h>
struct sh7786_pcie_port {
struct pci_channel *hose;
+ struct clk *fclk, phy_clk;
unsigned int index;
int endpoint;
int link;
@@ -51,6 +54,7 @@ static struct resource sh7786_pci0_resources[] = {
.name = "PCIe0 MEM 2",
.start = 0xfe100000,
.end = 0xfe100000 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
},
};
@@ -74,6 +78,7 @@ static struct resource sh7786_pci1_resources[] = {
.name = "PCIe1 MEM 2",
.start = 0xfe300000,
.end = 0xfe300000 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
},
};
@@ -82,6 +87,7 @@ static struct resource sh7786_pci2_resources[] = {
.name = "PCIe2 IO",
.start = 0xfc800000,
.end = 0xfc800000 + SZ_4M - 1,
+ .flags = IORESOURCE_IO,
}, {
.name = "PCIe2 MEM 0",
.start = 0x80000000,
@@ -96,6 +102,7 @@ static struct resource sh7786_pci2_resources[] = {
.name = "PCIe2 MEM 2",
.start = 0xfcd00000,
.end = 0xfcd00000 + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
},
};
@@ -117,7 +124,29 @@ static struct pci_channel sh7786_pci_channels[] = {
DEFINE_CONTROLLER(0xfcc00000, 2),
};
-static int phy_wait_for_ack(struct pci_channel *chan)
+static struct clk fixed_pciexclkp = {
+ .rate = 100000000, /* 100 MHz reference clock */
+};
+
+static void __devinit sh7786_pci_fixup(struct pci_dev *dev)
+{
+ /*
+ * Prevent enumeration of root complex resources.
+ */
+ if (pci_is_root_bus(dev->bus) && dev->devfn == 0) {
+ int i;
+
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ dev->resource[i].start = 0;
+ dev->resource[i].end = 0;
+ dev->resource[i].flags = 0;
+ }
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_SH7786,
+ sh7786_pci_fixup);
+
+static int __init phy_wait_for_ack(struct pci_channel *chan)
{
unsigned int timeout = 100;
@@ -131,7 +160,7 @@ static int phy_wait_for_ack(struct pci_channel *chan)
return -ETIMEDOUT;
}
-static int pci_wait_for_irq(struct pci_channel *chan, unsigned int mask)
+static int __init pci_wait_for_irq(struct pci_channel *chan, unsigned int mask)
{
unsigned int timeout = 100;
@@ -145,19 +174,14 @@ static int pci_wait_for_irq(struct pci_channel *chan, unsigned int mask)
return -ETIMEDOUT;
}
-static void phy_write_reg(struct pci_channel *chan, unsigned int addr,
- unsigned int lane, unsigned int data)
+static void __init phy_write_reg(struct pci_channel *chan, unsigned int addr,
+ unsigned int lane, unsigned int data)
{
- unsigned long phyaddr, ctrl;
+ unsigned long phyaddr;
phyaddr = (1 << BITS_CMD) + ((lane & 0xf) << BITS_LANE) +
((addr & 0xff) << BITS_ADR);
- /* Enable clock */
- ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR);
- ctrl |= (1 << BITS_CKE);
- pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR);
-
/* Set write data */
pci_write_reg(chan, data, SH4A_PCIEPHYDOUTR);
pci_write_reg(chan, phyaddr, SH4A_PCIEPHYADRR);
@@ -165,20 +189,74 @@ static void phy_write_reg(struct pci_channel *chan, unsigned int addr,
phy_wait_for_ack(chan);
/* Clear command */
+ pci_write_reg(chan, 0, SH4A_PCIEPHYDOUTR);
pci_write_reg(chan, 0, SH4A_PCIEPHYADRR);
phy_wait_for_ack(chan);
+}
- /* Disable clock */
- ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR);
- ctrl &= ~(1 << BITS_CKE);
- pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR);
+static int __init pcie_clk_init(struct sh7786_pcie_port *port)
+{
+ struct pci_channel *chan = port->hose;
+ struct clk *clk;
+ char fclk_name[16];
+ int ret;
+
+ /*
+ * First register the fixed clock
+ */
+ ret = clk_register(&fixed_pciexclkp);
+ if (unlikely(ret != 0))
+ return ret;
+
+ /*
+ * Grab the port's function clock, which the PHY clock depends
+ * on. clock lookups don't help us much at this point, since no
+ * dev_id is available this early. Lame.
+ */
+ snprintf(fclk_name, sizeof(fclk_name), "pcie%d_fck", port->index);
+
+ port->fclk = clk_get(NULL, fclk_name);
+ if (IS_ERR(port->fclk)) {
+ ret = PTR_ERR(port->fclk);
+ goto err_fclk;
+ }
+
+ clk_enable(port->fclk);
+
+ /*
+ * And now, set up the PHY clock
+ */
+ clk = &port->phy_clk;
+
+ memset(clk, 0, sizeof(struct clk));
+
+ clk->parent = &fixed_pciexclkp;
+ clk->enable_reg = (void __iomem *)(chan->reg_base + SH4A_PCIEPHYCTLR);
+ clk->enable_bit = BITS_CKE;
+
+ ret = sh_clk_mstp32_register(clk, 1);
+ if (unlikely(ret < 0))
+ goto err_phy;
+
+ return 0;
+
+err_phy:
+ clk_disable(port->fclk);
+ clk_put(port->fclk);
+err_fclk:
+ clk_unregister(&fixed_pciexclkp);
+
+ return ret;
}
-static int phy_init(struct pci_channel *chan)
+static int __init phy_init(struct sh7786_pcie_port *port)
{
+ struct pci_channel *chan = port->hose;
unsigned int timeout = 100;
+ clk_enable(&port->phy_clk);
+
/* Initialize the phy */
phy_write_reg(chan, 0x60, 0xf, 0x004b008b);
phy_write_reg(chan, 0x61, 0xf, 0x00007b41);
@@ -187,9 +265,13 @@ static int phy_init(struct pci_channel *chan)
phy_write_reg(chan, 0x66, 0xf, 0x00000010);
phy_write_reg(chan, 0x74, 0xf, 0x0007001c);
phy_write_reg(chan, 0x79, 0xf, 0x01fc000d);
+ phy_write_reg(chan, 0xb0, 0xf, 0x00000610);
/* Deassert Standby */
- phy_write_reg(chan, 0x67, 0xf, 0x00000400);
+ phy_write_reg(chan, 0x67, 0x1, 0x00000400);
+
+ /* Disable clock */
+ clk_disable(&port->phy_clk);
while (timeout--) {
if (pci_read_reg(chan, SH4A_PCIEPHYSR))
@@ -201,22 +283,33 @@ static int phy_init(struct pci_channel *chan)
return -ETIMEDOUT;
}
-static int pcie_init(struct sh7786_pcie_port *port)
+static void __init pcie_reset(struct sh7786_pcie_port *port)
+{
+ struct pci_channel *chan = port->hose;
+
+ pci_write_reg(chan, 1, SH4A_PCIESRSTR);
+ pci_write_reg(chan, 0, SH4A_PCIETCTLR);
+ pci_write_reg(chan, 0, SH4A_PCIESRSTR);
+ pci_write_reg(chan, 0, SH4A_PCIETXVC0SR);
+}
+
+static int __init pcie_init(struct sh7786_pcie_port *port)
{
struct pci_channel *chan = port->hose;
unsigned int data;
phys_addr_t memphys;
size_t memsize;
- int ret, i;
+ int ret, i, win;
/* Begin initialization */
- pci_write_reg(chan, 0, SH4A_PCIETCTLR);
+ pcie_reset(port);
- /* Initialize as type1. */
- data = pci_read_reg(chan, SH4A_PCIEPCICONF3);
- data &= ~(0x7f << 16);
- data |= PCI_HEADER_TYPE_BRIDGE << 16;
- pci_write_reg(chan, data, SH4A_PCIEPCICONF3);
+ /*
+ * Initial header for port config space is type 1, set the device
+ * class to match. Hardware takes care of propagating the IDSETR
+ * settings, so there is no need to bother with a quirk.
+ */
+ pci_write_reg(chan, PCI_CLASS_BRIDGE_PCI << 16, SH4A_PCIEIDSETR1);
/* Initialize default capabilities. */
data = pci_read_reg(chan, SH4A_PCIEEXPCAP0);
@@ -268,30 +361,33 @@ static int pcie_init(struct sh7786_pcie_port *port)
* LAR1/LAMR1.
*/
if (memsize > SZ_512M) {
- __raw_writel(memphys + SZ_512M, chan->reg_base + SH4A_PCIELAR1);
- __raw_writel(((memsize - SZ_512M) - SZ_256) | 1,
- chan->reg_base + SH4A_PCIELAMR1);
+ pci_write_reg(chan, memphys + SZ_512M, SH4A_PCIELAR1);
+ pci_write_reg(chan, ((memsize - SZ_512M) - SZ_256) | 1,
+ SH4A_PCIELAMR1);
memsize = SZ_512M;
} else {
/*
* Otherwise just zero it out and disable it.
*/
- __raw_writel(0, chan->reg_base + SH4A_PCIELAR1);
- __raw_writel(0, chan->reg_base + SH4A_PCIELAMR1);
+ pci_write_reg(chan, 0, SH4A_PCIELAR1);
+ pci_write_reg(chan, 0, SH4A_PCIELAMR1);
}
/*
* LAR0/LAMR0 covers up to the first 512MB, which is enough to
* cover all of lowmem on most platforms.
*/
- __raw_writel(memphys, chan->reg_base + SH4A_PCIELAR0);
- __raw_writel((memsize - SZ_256) | 1, chan->reg_base + SH4A_PCIELAMR0);
+ pci_write_reg(chan, memphys, SH4A_PCIELAR0);
+ pci_write_reg(chan, (memsize - SZ_256) | 1, SH4A_PCIELAMR0);
/* Finish initialization */
data = pci_read_reg(chan, SH4A_PCIETCTLR);
data |= 0x1;
pci_write_reg(chan, data, SH4A_PCIETCTLR);
+ /* Let things settle down a bit.. */
+ mdelay(100);
+
/* Enable DL_Active Interrupt generation */
data = pci_read_reg(chan, SH4A_PCIEDLINTENR);
data |= PCIEDLINTENR_DLL_ACT_ENABLE;
@@ -302,9 +398,12 @@ static int pcie_init(struct sh7786_pcie_port *port)
data |= PCIEMACCTLR_SCR_DIS | (0xff << 16);
pci_write_reg(chan, data, SH4A_PCIEMACCTLR);
+ /*
+ * This will timeout if we don't have a link, but we permit the
+ * port to register anyways in order to support hotplug on future
+ * hardware.
+ */
ret = pci_wait_for_irq(chan, MASK_INT_TX_CTRL);
- if (unlikely(ret != 0))
- return -ENODEV;
data = pci_read_reg(chan, SH4A_PCIEPCICONF1);
data &= ~(PCI_STATUS_DEVSEL_MASK << 16);
@@ -317,35 +416,48 @@ static int pcie_init(struct sh7786_pcie_port *port)
wmb();
- data = pci_read_reg(chan, SH4A_PCIEMACSR);
- printk(KERN_NOTICE "PCI: PCIe#%d link width %d\n",
- port->index, (data >> 20) & 0x3f);
-
+ if (ret == 0) {
+ data = pci_read_reg(chan, SH4A_PCIEMACSR);
+ printk(KERN_NOTICE "PCI: PCIe#%d x%d link detected\n",
+ port->index, (data >> 20) & 0x3f);
+ } else
+ printk(KERN_NOTICE "PCI: PCIe#%d link down\n",
+ port->index);
- for (i = 0; i < chan->nr_resources; i++) {
+ for (i = win = 0; i < chan->nr_resources; i++) {
struct resource *res = chan->resources + i;
resource_size_t size;
- u32 enable_mask;
+ u32 mask;
- pci_write_reg(chan, 0x00000000, SH4A_PCIEPTCTLR(i));
+ /*
+ * We can't use the 32-bit mode windows in legacy 29-bit
+ * mode, so just skip them entirely.
+ */
+ if ((res->flags & IORESOURCE_MEM_32BIT) && __in_29bit_mode())
+ continue;
- size = resource_size(res);
+ pci_write_reg(chan, 0x00000000, SH4A_PCIEPTCTLR(win));
/*
* The PAMR mask is calculated in units of 256kB, which
* keeps things pretty simple.
*/
- __raw_writel(((roundup_pow_of_two(size) / SZ_256K) - 1) << 18,
- chan->reg_base + SH4A_PCIEPAMR(i));
+ size = resource_size(res);
+ mask = (roundup_pow_of_two(size) / SZ_256K) - 1;
+ pci_write_reg(chan, mask << 18, SH4A_PCIEPAMR(win));
- pci_write_reg(chan, 0x00000000, SH4A_PCIEPARH(i));
- pci_write_reg(chan, 0x00000000, SH4A_PCIEPARL(i));
+ pci_write_reg(chan, upper_32_bits(res->start),
+ SH4A_PCIEPARH(win));
+ pci_write_reg(chan, lower_32_bits(res->start),
+ SH4A_PCIEPARL(win));
- enable_mask = MASK_PARE;
+ mask = MASK_PARE;
if (res->flags & IORESOURCE_IO)
- enable_mask |= MASK_SPC;
+ mask |= MASK_SPC;
+
+ pci_write_reg(chan, mask, SH4A_PCIEPTCTLR(win));
- pci_write_reg(chan, enable_mask, SH4A_PCIEPTCTLR(i));
+ win++;
}
return 0;
@@ -356,26 +468,33 @@ int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
return 71;
}
-static int sh7786_pcie_core_init(void)
+static int __init sh7786_pcie_core_init(void)
{
/* Return the number of ports */
return test_mode_pin(MODE_PIN12) ? 3 : 2;
}
-static int __devinit sh7786_pcie_init_hw(struct sh7786_pcie_port *port)
+static int __init sh7786_pcie_init_hw(struct sh7786_pcie_port *port)
{
int ret;
- ret = phy_init(port->hose);
- if (unlikely(ret < 0))
- return ret;
-
/*
* Check if we are configured in endpoint or root complex mode,
* this is a fixed pin setting that applies to all PCIe ports.
*/
port->endpoint = test_mode_pin(MODE_PIN11);
+ /*
+ * Setup clocks, needed both for PHY and PCIe registers.
+ */
+ ret = pcie_clk_init(port);
+ if (unlikely(ret < 0))
+ return ret;
+
+ ret = phy_init(port);
+ if (unlikely(ret < 0))
+ return ret;
+
ret = pcie_init(port);
if (unlikely(ret < 0))
return ret;
@@ -390,9 +509,10 @@ static struct sh7786_pcie_hwops sh7786_65nm_pcie_hwops __initdata = {
static int __init sh7786_pcie_init(void)
{
+ struct clk *platclk;
int ret = 0, i;
- printk(KERN_NOTICE "PCI: Starting intialization.\n");
+ printk(KERN_NOTICE "PCI: Starting initialization.\n");
sh7786_pcie_hwops = &sh7786_65nm_pcie_hwops;
@@ -407,6 +527,22 @@ static int __init sh7786_pcie_init(void)
if (unlikely(!sh7786_pcie_ports))
return -ENOMEM;
+ /*
+ * Fetch any optional platform clock associated with this block.
+ *
+ * This is a rather nasty hack for boards with spec-mocking FPGAs
+ * that have a secondary set of clocks outside of the on-chip
+ * ones that need to be accounted for before there is any chance
+ * of touching the existing MSTP bits or CPG clocks.
+ */
+ platclk = clk_get(NULL, "pcie_plat_clk");
+ if (IS_ERR(platclk)) {
+ /* Sane hardware should probably get a WARN_ON.. */
+ platclk = NULL;
+ }
+
+ clk_enable(platclk);
+
printk(KERN_NOTICE "PCI: probing %d ports.\n", nr_ports);
for (i = 0; i < nr_ports; i++) {
@@ -419,8 +555,11 @@ static int __init sh7786_pcie_init(void)
ret |= sh7786_pcie_hwops->port_init_hw(port);
}
- if (unlikely(ret))
+ if (unlikely(ret)) {
+ clk_disable(platclk);
+ clk_put(platclk);
return ret;
+ }
return 0;
}
diff --git a/arch/sh/drivers/pci/pcie-sh7786.h b/arch/sh/drivers/pci/pcie-sh7786.h
index 90a6992576b0..1ee054e47eae 100644
--- a/arch/sh/drivers/pci/pcie-sh7786.h
+++ b/arch/sh/drivers/pci/pcie-sh7786.h
@@ -55,8 +55,11 @@
#define BITS_ERRRCV (0) /* 0 ERRRCV 0 */
#define MASK_ERRRCV (1<<BITS_ERRRCV)
+/* PCIEENBLR */
+#define SH4A_PCIEENBLR (0x000008) /* R/W - 0x0000 0001 32 */
+
/* PCIEECR */
-#define SH4A_PCIEECR (0x000008) /* R/W - 0x0000 0000 32 */
+#define SH4A_PCIEECR (0x00000C) /* R/W - 0x0000 0000 32 */
#define BITS_ENBL (0) /* 0 ENBL 0 R/W */
#define MASK_ENBL (1<<BITS_ENBL)
@@ -113,6 +116,27 @@
#define BITS_MDATA (0)
#define MASK_MDATA (0xffffffff<<BITS_MDATA)
+/* PCIEUNLOCKCR */
+#define SH4A_PCIEUNLOCKCR (0x000048) /* R/W - 0x0000 0000 32 */
+
+/* PCIEIDR */
+#define SH4A_PCIEIDR (0x000060) /* R/W - 0x0101 1101 32 */
+
+/* PCIEDBGCTLR */
+#define SH4A_PCIEDBGCTLR (0x000100) /* R/W - 0x0000 0000 32 */
+
+/* PCIEINTXR */
+#define SH4A_PCIEINTXR (0x004000) /* R/W - 0x0000 0000 32 */
+
+/* PCIERMSGR */
+#define SH4A_PCIERMSGR (0x004010) /* R/W - 0x0000 0000 32 */
+
+/* PCIERSTR */
+#define SH4A_PCIERSTR(x) (0x008000 + ((x) * 0x4)) /* R/W - 0x0000 0000 32 */
+
+/* PCIESRSTR */
+#define SH4A_PCIESRSTR (0x008040) /* R/W - 0x0000 0000 32 */
+
/* PCIEPHYCTLR */
#define SH4A_PCIEPHYCTLR (0x010000) /* R/W - 0x0000 0000 32 */
#define BITS_CKE (0)
@@ -121,6 +145,9 @@
/* PCIERMSGIER */
#define SH4A_PCIERMSGIER (0x004040) /* R/W - 0x0000 0000 32 */
+/* PCIEPHYCTLR */
+#define SH4A_PCIEPHYCTLR (0x010000) /* R/W - 0x0000 0000 32 */
+
/* PCIEPHYADRR */
#define SH4A_PCIEPHYADRR (0x010004) /* R/W - 0x0000 0000 32 */
#define BITS_ACK (24) // Rev1.171
@@ -152,7 +179,7 @@
#define MASK_CFINT (1<<BITS_CFINT)
/* PCIETSTR */
-#define SH4A_PCIETSTR (0x020004) /* R/W R/W 0x0000 0000 32 */
+#define SH4A_PCIETSTR (0x020004) /* R 0x0000 0000 32 */
/* PCIEINTR */
#define SH4A_PCIEINTR (0x020008) /* R/W R/W 0x0000 0000 32 */
@@ -236,6 +263,9 @@
#define BITS_INTPM (8)
#define MASK_INTPM (1<<BITS_INTPM)
+/* PCIEEH0R */
+#define SH4A_PCIEEHR(x) (0x020010 + ((x) * 0x4)) /* R - 0x0000 0000 32 */
+
/* PCIEAIR */
#define SH4A_PCIEAIR (SH4A_PCIE_BASE + 0x020010) /* R/W R/W 0xxxxx xxxx 32 */
@@ -244,6 +274,25 @@
/* PCIEERRFR */ // Rev1.18
#define SH4A_PCIEERRFR (0x020020) /* R/W R/W 0xxxxx xxxx 32 */ // Rev1.18
+
+/* PCIEERRFER */
+#define SH4A_PCIEERRFER (0x020024) /* R/W R/W 0x0000 0000 32 */
+
+/* PCIEERRFR2 */
+#define SH4A_PCIEERRFR2 (0x020028) /* R/W R/W 0x0000 0000 32 */
+
+/* PCIEMSIR */
+#define SH4A_PCIEMSIR (0x020040) /* R/W - 0x0000 0000 32 */
+
+/* PCIEMSIFR */
+#define SH4A_PCIEMSIFR (0x020044) /* R/W R/W 0x0000 0000 32 */
+
+/* PCIEPWRCTLR */
+#define SH4A_PCIEPWRCTLR (0x020100) /* R/W - 0x0000 0000 32 */
+
+/* PCIEPCCTLR */
+#define SH4A_PCIEPCCTLR (0x020180) /* R/W - 0x0000 0000 32 */
+
// Rev1.18
/* PCIELAR0 */
#define SH4A_PCIELAR0 (0x020200) /* R/W R/W 0x0000 0000 32 */
@@ -352,6 +401,7 @@
#define SH4A_PCIEDMCCR0 (0x021120) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEDMCC2R0 (0x021124) /* R/W R/W 0x0000 0000 - */
#define SH4A_PCIEDMCCCR0 (0x021128) /* R/W R/W 0x0000 0000 32 */
+#define SH4A_PCIEDMCHSR0 (0x02112C) /* R/W - 0x0000 0000 32 */
#define SH4A_PCIEDMSAR1 (0x021140) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEDMSAHR1 (0x021144) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEDMDAR1 (0x021148) /* R/W R/W 0x0000 0000 32 */
@@ -363,6 +413,7 @@
#define SH4A_PCIEDMCCR1 (0x021160) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEDMCC2R1 (0x021164) /* R/W R/W 0x0000 0000 - */
#define SH4A_PCIEDMCCCR1 (0x021168) /* R/W R/W 0x0000 0000 32 */
+#define SH4A_PCIEDMCHSR1 (0x02116C) /* R/W - 0x0000 0000 32 */
#define SH4A_PCIEDMSAR2 (0x021180) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEDMSAHR2 (0x021184) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEDMDAR2 (0x021188) /* R/W R/W 0x0000 0000 32 */
@@ -385,6 +436,7 @@
#define SH4A_PCIEDMCCR3 (0x0211E0) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEDMCC2R3 (0x0211E4) /* R/W R/W 0x0000 0000 - */
#define SH4A_PCIEDMCCCR3 (0x0211E8) /* R/W R/W 0x0000 0000 32 */
+#define SH4A_PCIEDMCHSR3 (0x0211EC) /* R/W R/W 0x0000 0000 32 */
#define SH4A_PCIEPCICONF0 (0x040000) /* R R - 8/16/32 */
#define SH4A_PCIEPCICONF1 (0x040004) /* R/W R/W 0x0008 0000 8/16/32 */
#define SH4A_PCIEPCICONF2 (0x040008) /* R/W R/W 0xFF00 0000 8/16/32 */