105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman/* 205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * Copyright 2006, Segher Boessenkool, IBM Corporation. 305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * Copyright 2006-2007, Michael Ellerman, IBM Corporation. 405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * 505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * This program is free software; you can redistribute it and/or 605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * modify it under the terms of the GNU General Public License 705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * as published by the Free Software Foundation; version 2 of the 805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * License. 905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman * 1005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman */ 1105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 1205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include <linux/irq.h> 1305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include <linux/bootmem.h> 1405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include <linux/msi.h> 1505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include <asm/mpic.h> 1605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include <asm/prom.h> 1705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include <asm/hw_irq.h> 1805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include <asm/ppc-pci.h> 1925235f712b680d00756a73ee64289137989fc6fdMichael Ellerman#include <asm/msi_bitmap.h> 2005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 2105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman#include "mpic.h" 2205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 2305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman/* A bit ugly, can we get this from the pci_dev somehow? */ 2405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellermanstatic struct mpic *msi_mpic; 2505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 261c9db52534a2c0e9776788cd34ccc193289fc18cThomas Gleixnerstatic void mpic_u3msi_mask_irq(struct irq_data *data) 2705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman{ 281c9db52534a2c0e9776788cd34ccc193289fc18cThomas Gleixner mask_msi_irq(data); 29835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek mpic_mask_irq(data); 3005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman} 3105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 321c9db52534a2c0e9776788cd34ccc193289fc18cThomas Gleixnerstatic void mpic_u3msi_unmask_irq(struct irq_data *data) 3305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman{ 34835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek mpic_unmask_irq(data); 351c9db52534a2c0e9776788cd34ccc193289fc18cThomas Gleixner unmask_msi_irq(data); 3605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman} 3705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 3805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellermanstatic struct irq_chip mpic_u3msi_chip = { 39835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek .irq_shutdown = mpic_u3msi_mask_irq, 40835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek .irq_mask = mpic_u3msi_mask_irq, 41835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek .irq_unmask = mpic_u3msi_unmask_irq, 42835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek .irq_eoi = mpic_end_irq, 43835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek .irq_set_type = mpic_set_irq_type, 44835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek .irq_set_affinity = mpic_set_affinity, 45835c0553eb151588b6a1b52b28ecbbd59f7ff052Lennert Buytenhek .name = "MPIC-U3MSI", 4605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman}; 4705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 4805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellermanstatic u64 read_ht_magic_addr(struct pci_dev *pdev, unsigned int pos) 4905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman{ 5005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman u8 flags; 5105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman u32 tmp; 5205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman u64 addr; 5305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 5405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman pci_read_config_byte(pdev, pos + HT_MSI_FLAGS, &flags); 5505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 5605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman if (flags & HT_MSI_FLAGS_FIXED) 5705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return HT_MSI_FIXED_ADDR; 5805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 5905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman pci_read_config_dword(pdev, pos + HT_MSI_ADDR_LO, &tmp); 6005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman addr = tmp & HT_MSI_ADDR_LO_MASK; 6105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman pci_read_config_dword(pdev, pos + HT_MSI_ADDR_HI, &tmp); 6205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman addr = addr | ((u64)tmp << 32); 6305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 6405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return addr; 6505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman} 6605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 677a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidtstatic u64 find_ht_magic_addr(struct pci_dev *pdev, unsigned int hwirq) 6805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman{ 6905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman struct pci_bus *bus; 7005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman unsigned int pos; 7105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 727a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt for (bus = pdev->bus; bus && bus->self; bus = bus->parent) { 7305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman pos = pci_find_ht_capability(bus->self, HT_CAPTYPE_MSI_MAPPING); 7405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman if (pos) 7505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return read_ht_magic_addr(bus->self, pos); 7605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman } 7705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 7805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return 0; 7905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman} 8005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 817a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidtstatic u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq) 827a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt{ 837a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt struct pci_controller *hose = pci_bus_to_host(pdev->bus); 847a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt 857a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt /* U4 PCIe MSIs need to write to the special register in 867a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * the bridge that generates interrupts. There should be 877a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * theorically a register at 0xf8005000 where you just write 887a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * the MSI number and that triggers the right interrupt, but 897a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * unfortunately, this is busted in HW, the bridge endian swaps 907a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * the value and hits the wrong nibble in the register. 917a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * 927a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * So instead we use another register set which is used normally 937a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * for converting HT interrupts to MPIC interrupts, which decodes 947a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * the interrupt number as part of the low address bits 957a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * 967a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * This will not work if we ever use more than one legacy MSI in 977a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * a block but we never do. For one MSI or multiple MSI-X where 987a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * each interrupt address can be specified separately, it works 997a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt * just fine. 1007a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt */ 1017a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt if (of_device_is_compatible(hose->dn, "u4-pcie") || 1027a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt of_device_is_compatible(hose->dn, "U4-pcie")) 1037a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt return 0xf8004000 | (hwirq << 4); 1047a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt 1057a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt return 0; 1067a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt} 1077a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt 10805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellermanstatic void u3msi_teardown_msi_irqs(struct pci_dev *pdev) 10905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman{ 11005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman struct msi_desc *entry; 11105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 11205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman list_for_each_entry(entry, &pdev->msi_list, list) { 11305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman if (entry->irq == NO_IRQ) 11405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman continue; 11505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 116ec775d0e70eb6b7116406b3441cb8501c2849dd2Thomas Gleixner irq_set_msi_desc(entry->irq, NULL); 11725235f712b680d00756a73ee64289137989fc6fdMichael Ellerman msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, 11825235f712b680d00756a73ee64289137989fc6fdMichael Ellerman virq_to_hw(entry->irq), 1); 11905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman irq_dispose_mapping(entry->irq); 12005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman } 12105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 12205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return; 12305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman} 12405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 12505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellermanstatic int u3msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) 12605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman{ 12705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman unsigned int virq; 12805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman struct msi_desc *entry; 12905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman struct msi_msg msg; 13021ccdd31e9c70f42b00d9ea152f6c4e0ff3f536eMichael Ellerman u64 addr; 13125235f712b680d00756a73ee64289137989fc6fdMichael Ellerman int hwirq; 13221ccdd31e9c70f42b00d9ea152f6c4e0ff3f536eMichael Ellerman 1336b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev if (type == PCI_CAP_ID_MSIX) 1346b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev pr_debug("u3msi: MSI-X untested, trying anyway.\n"); 1356b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev 1366b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev /* If we can't find a magic address then MSI ain't gonna work */ 1376b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev if (find_ht_magic_addr(pdev, 0) == 0 && 1386b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev find_u4_magic_addr(pdev, 0) == 0) { 1396b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev pr_debug("u3msi: no magic address found for %s\n", 1406b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev pci_name(pdev)); 1416b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev return -ENXIO; 1426b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev } 1436b2fd7efeb888fa781c1f767de6c36497ac1596bAlexander Gordeev 14405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman list_for_each_entry(entry, &pdev->msi_list, list) { 14525235f712b680d00756a73ee64289137989fc6fdMichael Ellerman hwirq = msi_bitmap_alloc_hwirqs(&msi_mpic->msi_bitmap, 1); 14625235f712b680d00756a73ee64289137989fc6fdMichael Ellerman if (hwirq < 0) { 14705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman pr_debug("u3msi: failed allocating hwirq\n"); 14825235f712b680d00756a73ee64289137989fc6fdMichael Ellerman return hwirq; 14905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman } 15005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 1517a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt addr = find_ht_magic_addr(pdev, hwirq); 1527a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt if (addr == 0) 1537a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt addr = find_u4_magic_addr(pdev, hwirq); 1547a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt msg.address_lo = addr & 0xFFFFFFFF; 1557a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt msg.address_hi = addr >> 32; 1567a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt 15705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman virq = irq_create_mapping(msi_mpic->irqhost, hwirq); 15805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman if (virq == NO_IRQ) { 15925235f712b680d00756a73ee64289137989fc6fdMichael Ellerman pr_debug("u3msi: failed mapping hwirq 0x%x\n", hwirq); 16025235f712b680d00756a73ee64289137989fc6fdMichael Ellerman msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq, 1); 161d9303d662fa3fca8a6d27dee82b961a5f5524f20Michael Ellerman return -ENOSPC; 16205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman } 16305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 164ec775d0e70eb6b7116406b3441cb8501c2849dd2Thomas Gleixner irq_set_msi_desc(virq, entry); 165ec775d0e70eb6b7116406b3441cb8501c2849dd2Thomas Gleixner irq_set_chip(virq, &mpic_u3msi_chip); 166ec775d0e70eb6b7116406b3441cb8501c2849dd2Thomas Gleixner irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING); 16705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 16825235f712b680d00756a73ee64289137989fc6fdMichael Ellerman pr_debug("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n", 16925235f712b680d00756a73ee64289137989fc6fdMichael Ellerman virq, hwirq, (unsigned long)addr); 17021ccdd31e9c70f42b00d9ea152f6c4e0ff3f536eMichael Ellerman 1717a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt printk("u3msi: allocated virq 0x%x (hw 0x%x) addr 0x%lx\n", 1727a96c6b22efbd84e195836e192a3ce478cd6e14cBenjamin Herrenschmidt virq, hwirq, (unsigned long)addr); 17321ccdd31e9c70f42b00d9ea152f6c4e0ff3f536eMichael Ellerman msg.data = hwirq; 17405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman write_msi_msg(virq, &msg); 17505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 17605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman hwirq++; 17705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman } 17805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 17905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return 0; 18005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman} 18105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 18205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellermanint mpic_u3msi_init(struct mpic *mpic) 18305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman{ 18405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman int rc; 18505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 18605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman rc = mpic_msi_init_allocator(mpic); 18705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman if (rc) { 18805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman pr_debug("u3msi: Error allocating bitmap!\n"); 18905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return rc; 19005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman } 19105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 19205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman pr_debug("u3msi: Registering MPIC U3 MSI callbacks.\n"); 19305af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 19405af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman BUG_ON(msi_mpic); 19505af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman msi_mpic = mpic; 19605af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 19705af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman WARN_ON(ppc_md.setup_msi_irqs); 19805af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman ppc_md.setup_msi_irqs = u3msi_setup_msi_irqs; 19905af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman ppc_md.teardown_msi_irqs = u3msi_teardown_msi_irqs; 20005af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman 20105af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman return 0; 20205af7bd2d75e5098021864d83fbb831111755fa0Michael Ellerman} 203