|
@@ -255,3 +255,171 @@ struct pci_ops cia_pci_ops =
|
|
|
void
|
|
|
cia_pci_tbi(struct pci_controller *hose, dma_addr_t start, dma_addr_t end)
|
|
|
{
|
|
|
+ wmb();
|
|
|
+ *(vip)CIA_IOC_PCI_TBIA = 3; /* Flush all locked and unlocked. */
|
|
|
+ mb();
|
|
|
+ *(vip)CIA_IOC_PCI_TBIA;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * On PYXIS, even if the tbia works, we cannot use it. It effectively locks
|
|
|
+ * the chip (as well as direct write to the tag registers) if there is a
|
|
|
+ * SG DMA operation in progress. This is true at least for PYXIS rev. 1,
|
|
|
+ * so always use the method below.
|
|
|
+ */
|
|
|
+/*
|
|
|
+ * This is the method NT and NetBSD use.
|
|
|
+ *
|
|
|
+ * Allocate mappings, and put the chip into DMA loopback mode to read a
|
|
|
+ * garbage page. This works by causing TLB misses, causing old entries to
|
|
|
+ * be purged to make room for the new entries coming in for the garbage page.
|
|
|
+ */
|
|
|
+
|
|
|
+#define CIA_BROKEN_TBIA_BASE 0x30000000
|
|
|
+#define CIA_BROKEN_TBIA_SIZE 1024
|
|
|
+
|
|
|
+/* Always called with interrupts disabled */
|
|
|
+void
|
|
|
+cia_pci_tbi_try2(struct pci_controller *hose,
|
|
|
+ dma_addr_t start, dma_addr_t end)
|
|
|
+{
|
|
|
+ void __iomem *bus_addr;
|
|
|
+ int ctrl;
|
|
|
+
|
|
|
+ /* Put the chip into PCI loopback mode. */
|
|
|
+ mb();
|
|
|
+ ctrl = *(vip)CIA_IOC_CIA_CTRL;
|
|
|
+ *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN;
|
|
|
+ mb();
|
|
|
+ *(vip)CIA_IOC_CIA_CTRL;
|
|
|
+ mb();
|
|
|
+
|
|
|
+ /* Read from PCI dense memory space at TBI_ADDR, skipping 32k on
|
|
|
+ each read. This forces SG TLB misses. NetBSD claims that the
|
|
|
+ TLB entries are not quite LRU, meaning that we need to read more
|
|
|
+ times than there are actual tags. The 2117x docs claim strict
|
|
|
+ round-robin. Oh well, we've come this far... */
|
|
|
+ /* Even better - as seen on the PYXIS rev 1 the TLB tags 0-3 can
|
|
|
+ be filled by the TLB misses *only once* after being invalidated
|
|
|
+ (by tbia or direct write). Next misses won't update them even
|
|
|
+ though the lock bits are cleared. Tags 4-7 are "quite LRU" though,
|
|
|
+ so use them and read at window 3 base exactly 4 times. Reading
|
|
|
+ more sometimes makes the chip crazy. -ink */
|
|
|
+
|
|
|
+ bus_addr = cia_ioremap(CIA_BROKEN_TBIA_BASE, 32768 * 4);
|
|
|
+
|
|
|
+ cia_readl(bus_addr + 0x00000);
|
|
|
+ cia_readl(bus_addr + 0x08000);
|
|
|
+ cia_readl(bus_addr + 0x10000);
|
|
|
+ cia_readl(bus_addr + 0x18000);
|
|
|
+
|
|
|
+ cia_iounmap(bus_addr);
|
|
|
+
|
|
|
+ /* Restore normal PCI operation. */
|
|
|
+ mb();
|
|
|
+ *(vip)CIA_IOC_CIA_CTRL = ctrl;
|
|
|
+ mb();
|
|
|
+ *(vip)CIA_IOC_CIA_CTRL;
|
|
|
+ mb();
|
|
|
+}
|
|
|
+
|
|
|
+static inline void
|
|
|
+cia_prepare_tbia_workaround(int window)
|
|
|
+{
|
|
|
+ unsigned long *ppte, pte;
|
|
|
+ long i;
|
|
|
+
|
|
|
+ /* Use minimal 1K map. */
|
|
|
+ ppte = __alloc_bootmem(CIA_BROKEN_TBIA_SIZE, 32768, 0);
|
|
|
+ pte = (virt_to_phys(ppte) >> (PAGE_SHIFT - 1)) | 1;
|
|
|
+
|
|
|
+ for (i = 0; i < CIA_BROKEN_TBIA_SIZE / sizeof(unsigned long); ++i)
|
|
|
+ ppte[i] = pte;
|
|
|
+
|
|
|
+ *(vip)CIA_IOC_PCI_Wn_BASE(window) = CIA_BROKEN_TBIA_BASE | 3;
|
|
|
+ *(vip)CIA_IOC_PCI_Wn_MASK(window)
|
|
|
+ = (CIA_BROKEN_TBIA_SIZE*1024 - 1) & 0xfff00000;
|
|
|
+ *(vip)CIA_IOC_PCI_Tn_BASE(window) = virt_to_phys(ppte) >> 2;
|
|
|
+}
|
|
|
+
|
|
|
+static void __init
|
|
|
+verify_tb_operation(void)
|
|
|
+{
|
|
|
+ static int page[PAGE_SIZE/4]
|
|
|
+ __attribute__((aligned(PAGE_SIZE)))
|
|
|
+ __initdata = { 0 };
|
|
|
+
|
|
|
+ struct pci_iommu_arena *arena = pci_isa_hose->sg_isa;
|
|
|
+ int ctrl, addr0, tag0, pte0, data0;
|
|
|
+ int temp, use_tbia_try2 = 0;
|
|
|
+ void __iomem *bus_addr;
|
|
|
+
|
|
|
+ /* pyxis -- tbia is broken */
|
|
|
+ if (pci_isa_hose->dense_io_base)
|
|
|
+ use_tbia_try2 = 1;
|
|
|
+
|
|
|
+ /* Put the chip into PCI loopback mode. */
|
|
|
+ mb();
|
|
|
+ ctrl = *(vip)CIA_IOC_CIA_CTRL;
|
|
|
+ *(vip)CIA_IOC_CIA_CTRL = ctrl | CIA_CTRL_PCI_LOOP_EN;
|
|
|
+ mb();
|
|
|
+ *(vip)CIA_IOC_CIA_CTRL;
|
|
|
+ mb();
|
|
|
+
|
|
|
+ /* Write a valid entry directly into the TLB registers. */
|
|
|
+
|
|
|
+ addr0 = arena->dma_base;
|
|
|
+ tag0 = addr0 | 1;
|
|
|
+ pte0 = (virt_to_phys(page) >> (PAGE_SHIFT - 1)) | 1;
|
|
|
+
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(0) = tag0;
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(1) = 0;
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(2) = 0;
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(3) = 0;
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(4) = 0;
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(5) = 0;
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(6) = 0;
|
|
|
+ *(vip)CIA_IOC_TB_TAGn(7) = 0;
|
|
|
+ *(vip)CIA_IOC_TBn_PAGEm(0,0) = pte0;
|
|
|
+ *(vip)CIA_IOC_TBn_PAGEm(0,1) = 0;
|
|
|
+ *(vip)CIA_IOC_TBn_PAGEm(0,2) = 0;
|
|
|
+ *(vip)CIA_IOC_TBn_PAGEm(0,3) = 0;
|
|
|
+ mb();
|
|
|
+
|
|
|
+ /* Get a usable bus address */
|
|
|
+ bus_addr = cia_ioremap(addr0, 8*PAGE_SIZE);
|
|
|
+
|
|
|
+ /* First, verify we can read back what we've written. If
|
|
|
+ this fails, we can't be sure of any of the other testing
|
|
|
+ we're going to do, so bail. */
|
|
|
+ /* ??? Actually, we could do the work with machine checks.
|
|
|
+ By passing this register update test, we pretty much
|
|
|
+ guarantee that cia_pci_tbi_try1 works. If this test
|
|
|
+ fails, cia_pci_tbi_try2 might still work. */
|
|
|
+
|
|
|
+ temp = *(vip)CIA_IOC_TB_TAGn(0);
|
|
|
+ if (temp != tag0) {
|
|
|
+ printk("pci: failed tb register update test "
|
|
|
+ "(tag0 %#x != %#x)\n", temp, tag0);
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+ temp = *(vip)CIA_IOC_TB_TAGn(1);
|
|
|
+ if (temp != 0) {
|
|
|
+ printk("pci: failed tb register update test "
|
|
|
+ "(tag1 %#x != 0)\n", temp);
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+ temp = *(vip)CIA_IOC_TBn_PAGEm(0,0);
|
|
|
+ if (temp != pte0) {
|
|
|
+ printk("pci: failed tb register update test "
|
|
|
+ "(pte0 %#x != %#x)\n", temp, pte0);
|
|
|
+ goto failed;
|
|
|
+ }
|
|
|
+ printk("pci: passed tb register update test\n");
|
|
|
+
|
|
|
+ /* Second, verify we can actually do I/O through this entry. */
|
|
|
+
|
|
|
+ data0 = 0xdeadbeef;
|
|
|
+ page[0] = data0;
|
|
|
+ mcheck_expected(0) = 1;
|
|
|
+ mcheck_taken(0) = 0;
|