summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev')
-rw-r--r--arch/powerpc/sysdev/cpm2_pic.c28
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c8
-rw-r--r--arch/powerpc/sysdev/mpc8xxx_gpio.c21
-rw-r--r--arch/powerpc/sysdev/mpic.c19
-rw-r--r--arch/powerpc/sysdev/mpic_msi.c11
-rw-r--r--arch/powerpc/sysdev/mpic_u3msi.c46
6 files changed, 102 insertions, 31 deletions
diff --git a/arch/powerpc/sysdev/cpm2_pic.c b/arch/powerpc/sysdev/cpm2_pic.c
index 971483f0dfa..1709ac5aac7 100644
--- a/arch/powerpc/sysdev/cpm2_pic.c
+++ b/arch/powerpc/sysdev/cpm2_pic.c
@@ -143,13 +143,23 @@ static int cpm2_set_irq_type(unsigned int virq, unsigned int flow_type)
struct irq_desc *desc = irq_to_desc(virq);
unsigned int vold, vnew, edibit;
- if (flow_type == IRQ_TYPE_NONE)
- flow_type = IRQ_TYPE_LEVEL_LOW;
-
- if (flow_type & IRQ_TYPE_EDGE_RISING) {
- printk(KERN_ERR "CPM2 PIC: sense type 0x%x not supported\n",
- flow_type);
- return -EINVAL;
+ /* Port C interrupts are either IRQ_TYPE_EDGE_FALLING or
+ * IRQ_TYPE_EDGE_BOTH (default). All others are IRQ_TYPE_EDGE_FALLING
+ * or IRQ_TYPE_LEVEL_LOW (default)
+ */
+ if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) {
+ if (flow_type == IRQ_TYPE_NONE)
+ flow_type = IRQ_TYPE_EDGE_BOTH;
+
+ if (flow_type != IRQ_TYPE_EDGE_BOTH &&
+ flow_type != IRQ_TYPE_EDGE_FALLING)
+ goto err_sense;
+ } else {
+ if (flow_type == IRQ_TYPE_NONE)
+ flow_type = IRQ_TYPE_LEVEL_LOW;
+
+ if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
+ goto err_sense;
}
desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
@@ -181,6 +191,10 @@ static int cpm2_set_irq_type(unsigned int virq, unsigned int flow_type)
if (vold != vnew)
out_be32(&cpm2_intctl->ic_siexr, vnew);
return 0;
+
+err_sense:
+ pr_err("CPM2 PIC: sense type 0x%x not supported\n", flow_type);
+ return -EINVAL;
}
static struct irq_chip cpm2_pic = {
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 4e3a3e345ab..e1a028c1f18 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -464,8 +464,7 @@ static void __iomem *mpc83xx_pcie_remap_cfg(struct pci_bus *bus,
{
struct pci_controller *hose = pci_bus_to_host(bus);
struct mpc83xx_pcie_priv *pcie = hose->dn->data;
- u8 bus_no = bus->number - hose->first_busno;
- u32 dev_base = bus_no << 24 | devfn << 16;
+ u32 dev_base = bus->number << 24 | devfn << 16;
int ret;
ret = mpc83xx_pcie_exclude_device(bus, devfn);
@@ -515,12 +514,17 @@ static int mpc83xx_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
static int mpc83xx_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 val)
{
+ struct pci_controller *hose = pci_bus_to_host(bus);
void __iomem *cfg_addr;
cfg_addr = mpc83xx_pcie_remap_cfg(bus, devfn, offset);
if (!cfg_addr)
return PCIBIOS_DEVICE_NOT_FOUND;
+ /* PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS */
+ if (offset == PCI_PRIMARY_BUS && bus->number == hose->first_busno)
+ val &= 0xffffff00;
+
switch (len) {
case 1:
out_8(cfg_addr, val);
diff --git a/arch/powerpc/sysdev/mpc8xxx_gpio.c b/arch/powerpc/sysdev/mpc8xxx_gpio.c
index 103eace3619..ee1c0e1cf4a 100644
--- a/arch/powerpc/sysdev/mpc8xxx_gpio.c
+++ b/arch/powerpc/sysdev/mpc8xxx_gpio.c
@@ -54,6 +54,22 @@ static void mpc8xxx_gpio_save_regs(struct of_mm_gpio_chip *mm)
mpc8xxx_gc->data = in_be32(mm->regs + GPIO_DAT);
}
+/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs
+ * defined as output cannot be determined by reading GPDAT register,
+ * so we use shadow data register instead. The status of input pins
+ * is determined by reading GPDAT register.
+ */
+static int mpc8572_gpio_get(struct gpio_chip *gc, unsigned int gpio)
+{
+ u32 val;
+ struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
+
+ val = in_be32(mm->regs + GPIO_DAT) & ~in_be32(mm->regs + GPIO_DIR);
+
+ return (val | mpc8xxx_gc->data) & mpc8xxx_gpio2mask(gpio);
+}
+
static int mpc8xxx_gpio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
@@ -136,7 +152,10 @@ static void __init mpc8xxx_add_controller(struct device_node *np)
gc->ngpio = MPC8XXX_GPIO_PINS;
gc->direction_input = mpc8xxx_gpio_dir_in;
gc->direction_output = mpc8xxx_gpio_dir_out;
- gc->get = mpc8xxx_gpio_get;
+ if (of_device_is_compatible(np, "fsl,mpc8572-gpio"))
+ gc->get = mpc8572_gpio_get;
+ else
+ gc->get = mpc8xxx_gpio_get;
gc->set = mpc8xxx_gpio_set;
ret = of_mm_gpiochip_add(np, mm_gc);
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index aa9d06e5925..470dc6c11d5 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -567,13 +567,11 @@ static void __init mpic_scan_ht_pics(struct mpic *mpic)
#endif /* CONFIG_MPIC_U3_HT_IRQS */
#ifdef CONFIG_SMP
-static int irq_choose_cpu(unsigned int virt_irq)
+static int irq_choose_cpu(const cpumask_t *mask)
{
- cpumask_t mask;
int cpuid;
- cpumask_copy(&mask, irq_to_desc(virt_irq)->affinity);
- if (cpus_equal(mask, CPU_MASK_ALL)) {
+ if (cpumask_equal(mask, cpu_all_mask)) {
static int irq_rover;
static DEFINE_SPINLOCK(irq_rover_lock);
unsigned long flags;
@@ -594,20 +592,15 @@ static int irq_choose_cpu(unsigned int virt_irq)
spin_unlock_irqrestore(&irq_rover_lock, flags);
} else {
- cpumask_t tmp;
-
- cpus_and(tmp, cpu_online_map, mask);
-
- if (cpus_empty(tmp))
+ cpuid = cpumask_first_and(mask, cpu_online_mask);
+ if (cpuid >= nr_cpu_ids)
goto do_round_robin;
-
- cpuid = first_cpu(tmp);
}
return get_hard_smp_processor_id(cpuid);
}
#else
-static int irq_choose_cpu(unsigned int virt_irq)
+static int irq_choose_cpu(const cpumask_t *mask)
{
return hard_smp_processor_id();
}
@@ -816,7 +809,7 @@ int mpic_set_affinity(unsigned int irq, const struct cpumask *cpumask)
unsigned int src = mpic_irq_to_hw(irq);
if (mpic->flags & MPIC_SINGLE_DEST_CPU) {
- int cpuid = irq_choose_cpu(irq);
+ int cpuid = irq_choose_cpu(cpumask);
mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid);
} else {
diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c
index 1d44eee80fa..0f67cd79d48 100644
--- a/arch/powerpc/sysdev/mpic_msi.c
+++ b/arch/powerpc/sysdev/mpic_msi.c
@@ -39,7 +39,12 @@ static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic)
pr_debug("mpic: found U3, guessing msi allocator setup\n");
- /* Reserve source numbers we know are reserved in the HW */
+ /* Reserve source numbers we know are reserved in the HW.
+ *
+ * This is a bit of a mix of U3 and U4 reserves but that's going
+ * to work fine, we have plenty enugh numbers left so let's just
+ * mark anything we don't like reserved.
+ */
for (i = 0; i < 8; i++)
msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
@@ -49,6 +54,10 @@ static int mpic_msi_reserve_u3_hwirqs(struct mpic *mpic)
for (i = 100; i < 105; i++)
msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
+ for (i = 124; i < mpic->irq_count; i++)
+ msi_bitmap_reserve_hwirq(&mpic->msi_bitmap, i);
+
+
np = NULL;
while ((np = of_find_all_nodes(np))) {
pr_debug("mpic: mapping hwirqs for %s\n", np->full_name);
diff --git a/arch/powerpc/sysdev/mpic_u3msi.c b/arch/powerpc/sysdev/mpic_u3msi.c
index d3caf23e631..bcbfe79c704 100644
--- a/arch/powerpc/sysdev/mpic_u3msi.c
+++ b/arch/powerpc/sysdev/mpic_u3msi.c
@@ -64,12 +64,12 @@ static u64 read_ht_magic_addr(struct pci_dev *pdev, unsigned int pos)
return addr;
}
-static u64 find_ht_magic_addr(struct pci_dev *pdev)
+static u64 find_ht_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
{
struct pci_bus *bus;
unsigned int pos;
- for (bus = pdev->bus; bus; bus = bus->parent) {
+ for (bus = pdev->bus; bus && bus->self; bus = bus->parent) {
pos = pci_find_ht_capability(bus->self, HT_CAPTYPE_MSI_MAPPING);
if (pos)
return read_ht_magic_addr(bus->self, pos);
@@ -78,13 +78,41 @@ static u64 find_ht_magic_addr(struct pci_dev *pdev)
return 0;
}
+static u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq)
+{
+ struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+
+ /* U4 PCIe MSIs need to write to the special register in
+ * the bridge that generates interrupts. There should be
+ * theorically a register at 0xf8005000 where you just write
+ * the MSI number and that triggers the right interrupt, but
+ * unfortunately, this is busted in HW, the bridge endian swaps
+ * the value and hits the wrong nibble in the register.
+ *
+ * So instead we use another register set which is used normally
+ * for converting HT interrupts to MPIC interrupts, which decodes
+ * the interrupt number as part of the low address bits
+ *
+ * This will not work if we ever use more than one legacy MSI in
+ * a block but we never do. For one MSI or multiple MSI-X where
+ * each interrupt address can be specified separately, it works
+ * just fine.
+ */
+ if (of_device_is_compatible(hose->dn, "u4-pcie") ||
+ of_device_is_compatible(hose->dn, "U4-pcie"))
+ return 0xf8004000 | (hwirq << 4);
+
+ return 0;
+}
+
static int u3msi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
{
if (type == PCI_CAP_ID_MSIX)
pr_debug("u3msi: MSI-X untested, trying anyway.\n");
/* If we can't find a magic address then MSI ain't gonna work */
- if (find_ht_magic_addr(pdev) == 0) {
+ if (find_ht_magic_addr(pdev, 0) == 0 &&
+ find_u4_magic_addr(pdev, 0) == 0) {
pr_debug("u3msi: no magic address found for %s\n",
pci_name(pdev));
return -ENXIO;
@@ -118,10 +146,6 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
u64 addr;
int hwirq;
- addr = find_ht_magic_addr(pdev);
- msg.address_lo = addr & 0xFFFFFFFF;
- msg.address_hi = addr >> 32;
-
list_for_each_entry(entry, &pdev->msi_list, list) {
hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap, 1);
if (hwirq < 0) {
@@ -129,6 +153,12 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
return hwirq;
}
+ addr = find_ht_magic_addr(pdev, hwirq);
+ if (addr == 0)
+ addr = find_u4_magic_addr(pdev, hwirq);
+ msg.address_lo = addr & 0xFFFFFFFF;
+ msg.address_hi = addr >> 32;
+
virq = irq_create_mapping(msi_mpic->irqhost, hwirq);
if (virq == NO_IRQ) {
pr_debug("u3msi: failed mapping hwirq 0x%x\n", hwirq);
@@ -143,6 +173,8 @@ static int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
pr_debug("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
virq, hwirq, (unsigned long)addr);
+ printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n",
+ virq, hwirq, (unsigned long)addr);
msg.data = hwirq;
write_msi_msg(virq, &msg);