/* * Support for the Tundra Universe I/II VME-PCI Bridge Chips * * Author: Martyn Welch * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc. * * Based on work by Tom Armistead and Ajit Prem * Copyright 2004 Motorola Inc. * * Derived from ca91c042.c by Michael Wyrick * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../vme.h" #include "../vme_bridge.h" #include "vme_ca91cx42.h" extern struct vmeSharedData *vmechip_interboard_data; extern dma_addr_t vmechip_interboard_datap; extern const int vmechip_revision; extern const int vmechip_devid; extern const int vmechip_irq; extern int vmechip_irq_overhead_ticks; extern char *vmechip_baseaddr; extern const int vme_slotnum; extern int vme_syscon; extern unsigned int out_image_va[]; extern unsigned int vme_irqlog[8][0x100]; static int outCTL[] = { LSI0_CTL, LSI1_CTL, LSI2_CTL, LSI3_CTL, LSI4_CTL, LSI5_CTL, LSI6_CTL, LSI7_CTL }; static int outBS[] = { LSI0_BS, LSI1_BS, LSI2_BS, LSI3_BS, LSI4_BS, LSI5_BS, LSI6_BS, LSI7_BS }; static int outBD[] = { LSI0_BD, LSI1_BD, LSI2_BD, LSI3_BD, LSI4_BD, LSI5_BD, LSI6_BD, LSI7_BD }; static int outTO[] = { LSI0_TO, LSI1_TO, LSI2_TO, LSI3_TO, LSI4_TO, LSI5_TO, LSI6_TO, LSI7_TO }; static int inCTL[] = { VSI0_CTL, VSI1_CTL, VSI2_CTL, VSI3_CTL, VSI4_CTL, VSI5_CTL, VSI6_CTL, VSI7_CTL }; static int inBS[] = { VSI0_BS, VSI1_BS, VSI2_BS, VSI3_BS, VSI4_BS, VSI5_BS, VSI6_BS, VSI7_BS }; static int inBD[] = { VSI0_BD, VSI1_BD, VSI2_BD, VSI3_BD, VSI4_BD, VSI5_BD, VSI6_BD, VSI7_BD }; static int inTO[] = { VSI0_TO, VSI1_TO, VSI2_TO, VSI3_TO, VSI4_TO, VSI5_TO, VSI6_TO, VSI7_TO }; static int vmevec[7] = { V1_STATID, V2_STATID, V3_STATID, V4_STATID, V5_STATID, V6_STATID, V7_STATID }; struct interrupt_counters { unsigned int acfail; unsigned int sysfail; unsigned int sw_int; unsigned int sw_iack; unsigned int verr; unsigned int lerr; unsigned int lm; unsigned int mbox; unsigned int dma; unsigned int virq[7]; unsigned int vown; }; extern wait_queue_head_t dma_queue[]; extern wait_queue_head_t lm_queue; extern wait_queue_head_t mbox_queue; extern int tb_speed; unsigned int uni_irq_time; unsigned int uni_dma_irq_time; unsigned int uni_lm_event; static spinlock_t lm_lock = SPIN_LOCK_UNLOCKED; static struct interrupt_counters Interrupt_counters = { 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0}, 0 }; #define read_register(offset) readl(vmechip_baseaddr + offset) #define write_register(value,offset) writel(value, vmechip_baseaddr + offset) #define read_register_word(offset) readw(vmechip_baseaddr + offset) #define write_register_word(value,offset) writew(value, vmechip_baseaddr + offset) int uni_procinfo(char *buf) { char *p; p = buf; p += sprintf(p, "\n"); { unsigned long misc_ctl; misc_ctl = read_register(MISC_CTL); p += sprintf(p, "MISC_CTL:\t\t\t0x%08lx\n", misc_ctl); p += sprintf(p, "VME Bus Time Out:\t\t"); switch ((misc_ctl & UNIV_BM_MISC_CTL_VBTO) >> UNIV_OF_MISC_CTL_VBTO) { case 0x0: p += sprintf(p, "Disabled\n"); break; case 0x1: p += sprintf(p, "16 us\n"); break; case 0x2: p += sprintf(p, "32 us\n"); break; case 0x3: p += sprintf(p, "64 us\n"); break; case 0x4: p += sprintf(p, "128 us\n"); break; case 0x5: p += sprintf(p, "256 us\n"); break; case 0x6: p += sprintf(p, "512 us\n"); break; case 0x7: p += sprintf(p, "1024 us\n"); break; default: p += sprintf(p, "Reserved Value, Undefined\n"); } p += sprintf(p, "VME Arbitration Time Out:\t"); switch ((misc_ctl & UNIV_BM_MISC_CTL_VARBTO) >> UNIV_OF_MISC_CTL_VARBTO) { case 0x0: p += sprintf(p, "Disabled"); break; case 0x1: p += sprintf(p, "16 us"); break; case 0x2: p += sprintf(p, "256 us"); break; default: p += sprintf(p, "Reserved Value, Undefined"); } if (misc_ctl & UNIV_BM_MISC_CTL_VARB) p += sprintf(p, ", Priority Arbitration\n"); else p += sprintf(p, ", Round Robin Arbitration\n"); p += sprintf(p, "\n"); } { unsigned int lmisc; unsigned int crt; unsigned int cwt; lmisc = read_register(LMISC); p += sprintf(p, "LMISC:\t\t\t\t0x%08x\n", lmisc); crt = (lmisc & UNIV_BM_LMISC_CRT) >> UNIV_OF_LMISC_CRT; cwt = (lmisc & UNIV_BM_LMISC_CWT) >> UNIV_OF_LMISC_CWT; p += sprintf(p, "Coupled Request Timer:\t\t"); switch (crt) { case 0x0: p += sprintf(p, "Disabled\n"); break; case 0x1: p += sprintf(p, "128 us\n"); break; case 0x2: p += sprintf(p, "256 us\n"); break; case 0x3: p += sprintf(p, "512 us\n"); break; case 0x4: p += sprintf(p, "1024 us\n"); break; case 0x5: p += sprintf(p, "2048 us\n"); break; case 0x6: p += sprintf(p, "4096 us\n"); break; default: p += sprintf(p, "Reserved\n"); } p += sprintf(p, "Coupled Window Timer:\t\t"); switch (cwt) { case 0x0: p += sprintf(p, "Disabled\n"); break; case 0x1: p += sprintf(p, "16 PCI Clocks\n"); break; case 0x2: p += sprintf(p, "32 PCI Clocks\n"); break; case 0x3: p += sprintf(p, "64 PCI Clocks\n"); break; case 0x4: p += sprintf(p, "128 PCI Clocks\n"); break; case 0x5: p += sprintf(p, "256 PCI Clocks\n"); break; case 0x6: p += sprintf(p, "512 PCI Clocks\n"); break; default: p += sprintf(p, "Reserved\n"); } p += sprintf(p, "\n"); } { unsigned int mast_ctl; mast_ctl = read_register(MAST_CTL); p += sprintf(p, "MAST_CTL:\t\t\t0x%08x\n", mast_ctl); { int retries; retries = ((mast_ctl & UNIV_BM_MAST_CTL_MAXRTRY) >> UNIV_OF_MAST_CTL_MAXRTRY) * 64; p += sprintf(p, "Max PCI Master Retries:\t\t"); if (retries) p += sprintf(p, "%d\n", retries); else p += sprintf(p, "Forever\n"); } p += sprintf(p, "Posted Write Transfer Count:\t"); switch ((mast_ctl & UNIV_BM_MAST_CTL_PWON) >> UNIV_OF_MAST_CTL_PWON) { case 0x0: p += sprintf(p, "128 Bytes\n"); break; case 0x1: p += sprintf(p, "256 Bytes\n"); break; case 0x2: p += sprintf(p, "512 Bytes\n"); break; case 0x3: p += sprintf(p, "1024 Bytes\n"); break; case 0x4: p += sprintf(p, "2048 Bytes\n"); break; case 0x5: p += sprintf(p, "4096 Bytes\n"); break; default: p += sprintf(p, "Undefined\n"); } p += sprintf(p, "VMEbus Request Level:\t\t"); switch ((mast_ctl & UNIV_BM_MAST_CTL_VRL) >> UNIV_OF_MAST_CTL_VRL) { case 0x0: p += sprintf(p, "Level 0\n"); case 0x1: p += sprintf(p, "Level 1\n"); case 0x2: p += sprintf(p, "Level 2\n"); case 0x3: p += sprintf(p, "Level 3\n"); } p += sprintf(p, "VMEbus Request Mode:\t\t"); if (mast_ctl & UNIV_BM_MAST_CTL_VRM) p += sprintf(p, "Fair Request Mode\n"); else p += sprintf(p, "Demand Request Mode\n"); p += sprintf(p, "VMEbus Release Mode:\t\t"); if (mast_ctl & UNIV_BM_MAST_CTL_VREL) p += sprintf(p, "Release on Request\n"); else p += sprintf(p, "Release when Done\n"); p += sprintf(p, "VMEbus Ownership Bit:\t\t"); if (mast_ctl & UNIV_BM_MAST_CTL_VOWN) p += sprintf(p, "Acquire and hold VMEbus\n"); else p += sprintf(p, "Release VMEbus\n"); p += sprintf(p, "VMEbus Ownership Bit Ack:\t"); if (mast_ctl & UNIV_BM_MAST_CTL_VOWN_ACK) p += sprintf(p, "Owning VMEbus\n"); else p += sprintf(p, "Not Owning VMEbus\n"); p += sprintf(p, "\n"); } { unsigned int misc_stat; misc_stat = read_register(MISC_STAT); p += sprintf(p, "MISC_STAT:\t\t\t0x%08x\n", misc_stat); p += sprintf(p, "Universe BBSY:\t\t\t"); if (misc_stat & UNIV_BM_MISC_STAT_MYBBSY) p += sprintf(p, "Negated\n"); else p += sprintf(p, "Asserted\n"); p += sprintf(p, "Transmit FIFO:\t\t\t"); if (misc_stat & UNIV_BM_MISC_STAT_TXFE) p += sprintf(p, "Empty\n"); else p += sprintf(p, "Not empty\n"); p += sprintf(p, "Receive FIFO:\t\t\t"); if (misc_stat & UNIV_BM_MISC_STAT_RXFE) p += sprintf(p, "Empty\n"); else p += sprintf(p, "Not Empty\n"); p += sprintf(p, "\n"); } p += sprintf(p, "Latency Timer:\t\t\t%02d Clocks\n\n", (read_register(UNIV_PCI_MISC0) & UNIV_BM_PCI_MISC0_LTIMER) >> UNIV_OF_PCI_MISC0_LTIMER); { unsigned int lint_en; unsigned int lint_stat; lint_en = read_register(LINT_EN); lint_stat = read_register(LINT_STAT); #define REPORT_IRQ(name,field) \ p += sprintf(p, (lint_en & UNIV_BM_LINT_##name) ? "Enabled" : "Masked"); \ p += sprintf(p, ", triggered %d times", Interrupt_counters.field); \ p += sprintf(p, (lint_stat & UNIV_BM_LINT_##name) ? ", irq now active\n" : "\n"); p += sprintf(p, "ACFAIL Interrupt:\t\t"); REPORT_IRQ(ACFAIL, acfail); p += sprintf(p, "SYSFAIL Interrupt:\t\t"); REPORT_IRQ(SYSFAIL, sysfail); p += sprintf(p, "SW_INT Interrupt:\t\t"); REPORT_IRQ(SW_INT, sw_int); p += sprintf(p, "SW_IACK Interrupt:\t\t"); REPORT_IRQ(SW_IACK, sw_iack); p += sprintf(p, "VERR Interrupt:\t\t\t"); REPORT_IRQ(VERR, verr); p += sprintf(p, "LERR Interrupt:\t\t\t"); REPORT_IRQ(LERR, lerr); p += sprintf(p, "LM Interrupt:\t\t\t"); REPORT_IRQ(LM, lm); p += sprintf(p, "MBOX Interrupt:\t\t\t"); REPORT_IRQ(MBOX, mbox); p += sprintf(p, "DMA Interrupt:\t\t\t"); REPORT_IRQ(DMA, dma); p += sprintf(p, "VIRQ7 Interrupt:\t\t"); REPORT_IRQ(VIRQ7, virq[7 - 1]); p += sprintf(p, "VIRQ6 Interrupt:\t\t"); REPORT_IRQ(VIRQ6, virq[6 - 1]); p += sprintf(p, "VIRQ5 Interrupt:\t\t"); REPORT_IRQ(VIRQ5, virq[5 - 1]); p += sprintf(p, "VIRQ4 Interrupt:\t\t"); REPORT_IRQ(VIRQ4, virq[4 - 1]); p += sprintf(p, "VIRQ3 Interrupt:\t\t"); REPORT_IRQ(VIRQ3, virq[3 - 1]); p += sprintf(p, "VIRQ2 Interrupt:\t\t"); REPORT_IRQ(VIRQ2, virq[2 - 1]); p += sprintf(p, "VIRQ1 Interrupt:\t\t"); REPORT_IRQ(VIRQ1, virq[1 - 1]); p += sprintf(p, "VOWN Interrupt:\t\t\t"); REPORT_IRQ(VOWN, vown); p += sprintf(p, "\n"); #undef REPORT_IRQ } { unsigned long vrai_ctl; vrai_ctl = read_register(VRAI_CTL); if (vrai_ctl & UNIV_BM_VRAI_CTL_EN) { unsigned int vrai_bs; vrai_bs = read_register(VRAI_BS); p += sprintf(p, "VME Register Image:\t\tEnabled at VME-Address 0x%x\n", vrai_bs); } else p += sprintf(p, "VME Register Image:\t\tDisabled\n"); } { unsigned int slsi; slsi = read_register(SLSI); if (slsi & UNIV_BM_SLSI_EN) { /* Not implemented */ } else { p += sprintf(p, "Special PCI Slave Image:\tDisabled\n"); } } { int i; for (i = 0; i < (vmechip_revision > 0 ? 8 : 4); i++) { unsigned int ctl, bs, bd, to, vstart, vend; ctl = readl(vmechip_baseaddr + outCTL[i]); bs = readl(vmechip_baseaddr + outBS[i]); bd = readl(vmechip_baseaddr + outBD[i]); to = readl(vmechip_baseaddr + outTO[i]); vstart = bs + to; vend = bd + to; p += sprintf(p, "PCI Slave Image %d:\t\t", i); if (ctl & UNIV_BM_LSI_CTL_EN) { p += sprintf(p, "Enabled"); if (ctl & UNIV_BM_LSI_CTL_PWEN) p += sprintf(p, ", Posted Write Enabled\n"); else p += sprintf(p, "\n"); p += sprintf(p, "\t\t\t\tPCI Addresses from 0x%x to 0x%x\n", bs, bd); p += sprintf(p, "\t\t\t\tVME Addresses from 0x%x to 0x%x\n", vstart, vend); } else p += sprintf(p, "Disabled\n"); } p += sprintf(p, "\n"); } { int i; for (i = 0; i < (vmechip_revision > 0 ? 8 : 4); i++) { unsigned int ctl, bs, bd, to, vstart, vend; ctl = readl(vmechip_baseaddr + inCTL[i]); bs = readl(vmechip_baseaddr + inBS[i]); bd = readl(vmechip_baseaddr + inBD[i]); to = readl(vmechip_baseaddr + inTO[i]); vstart = bs + to; vend = bd + to; p += sprintf(p, "VME Slave Image %d:\t\t", i); if (ctl & UNIV_BM_LSI_CTL_EN) { p += sprintf(p, "Enabled"); if (ctl & UNIV_BM_LSI_CTL_PWEN) p += sprintf(p, ", Posted Write Enabled\n"); else p += sprintf(p, "\n"); p += sprintf(p, "\t\t\t\tVME Addresses from 0x%x to 0x%x\n", bs, bd); p += sprintf(p, "\t\t\t\tPCI Addresses from 0x%x to 0x%x\n", vstart, vend); } else p += sprintf(p, "Disabled\n"); } } return p - buf; } //---------------------------------------------------------------------------- // uni_bus_error_chk() //---------------------------------------------------------------------------- int uni_bus_error_chk(int clrflag) { int tmp; tmp = readl(vmechip_baseaddr + PCI_COMMAND); if (tmp & 0x08000000) { // S_TA is Set if (clrflag) writel(tmp | 0x08000000, vmechip_baseaddr + PCI_COMMAND); return (1); } return (0); } //----------------------------------------------------------------------------- // Function : DMA_uni_irqhandler // Inputs : void // Outputs : void // Description: Saves DMA completion timestamp and then wakes up DMA queue //----------------------------------------------------------------------------- static void DMA_uni_irqhandler(void) { uni_dma_irq_time = uni_irq_time; wake_up(&dma_queue[0]); } //----------------------------------------------------------------------------- // Function : LERR_uni_irqhandler // Inputs : void // Outputs : void // Description: //----------------------------------------------------------------------------- static void LERR_uni_irqhandler(void) { int val; val = readl(vmechip_baseaddr + DGCS); if (!(val & 0x00000800)) { printk(KERN_ERR "ca91c042: LERR_uni_irqhandler DMA Read Error DGCS=%08X\n", val); } } //----------------------------------------------------------------------------- // Function : VERR_uni_irqhandler // Inputs : void // Outputs : void // Description: //----------------------------------------------------------------------------- static void VERR_uni_irqhandler(void) { int val; val = readl(vmechip_baseaddr + DGCS); if (!(val & 0x00000800)) { printk(KERN_ERR "ca91c042: VERR_uni_irqhandler DMA Read Error DGCS=%08X\n", val); } } //----------------------------------------------------------------------------- // Function : MB_uni_irqhandler // Inputs : void // Outputs : void // Description: //----------------------------------------------------------------------------- static void MB_uni_irqhandler(int mbox_mask) { if (vmechip_irq_overhead_ticks != 0) { wake_up(&mbox_queue); } } //----------------------------------------------------------------------------- // Function : LM_uni_irqhandler // Inputs : void // Outputs : void // Description: //----------------------------------------------------------------------------- static void LM_uni_irqhandler(int lm_mask) { uni_lm_event = lm_mask; wake_up(&lm_queue); } //----------------------------------------------------------------------------- // Function : VIRQ_uni_irqhandler // Inputs : void // Outputs : void // Description: //----------------------------------------------------------------------------- static void VIRQ_uni_irqhandler(int virq_mask) { int iackvec, i; for (i = 7; i > 0; i--) { if (virq_mask & (1 << i)) { Interrupt_counters.virq[i - 1]++; iackvec = readl(vmechip_baseaddr + vmevec[i - 1]); vme_irqlog[i][iackvec]++; } } } //----------------------------------------------------------------------------- // Function : uni_irqhandler // Inputs : int irq, void *dev_id, struct pt_regs *regs // Outputs : void // Description: //----------------------------------------------------------------------------- static irqreturn_t uni_irqhandler(int irq, void *dev_id) { long stat, enable; if (dev_id != vmechip_baseaddr) return IRQ_NONE; uni_irq_time = get_tbl(); stat = readl(vmechip_baseaddr + LINT_STAT); writel(stat, vmechip_baseaddr + LINT_STAT); // Clear all pending ints enable = readl(vmechip_baseaddr + LINT_EN); stat = stat & enable; if (stat & 0x0100) { Interrupt_counters.dma++; DMA_uni_irqhandler(); } if (stat & 0x0200) { Interrupt_counters.lerr++; LERR_uni_irqhandler(); } if (stat & 0x0400) { Interrupt_counters.verr++; VERR_uni_irqhandler(); } if (stat & 0xF0000) { Interrupt_counters.mbox++; MB_uni_irqhandler((stat & 0xF0000) >> 16); } if (stat & 0xF00000) { Interrupt_counters.lm++; LM_uni_irqhandler((stat & 0xF00000) >> 20); } if (stat & 0x0000FE) { VIRQ_uni_irqhandler(stat & 0x0000FE); } if (stat & UNIV_BM_LINT_ACFAIL) { Interrupt_counters.acfail++; } if (stat & UNIV_BM_LINT_SYSFAIL) { Interrupt_counters.sysfail++; } if (stat & UNIV_BM_LINT_SW_INT) { Interrupt_counters.sw_int++; } if (stat & UNIV_BM_LINT_SW_IACK) { Interrupt_counters.sw_iack++; } if (stat & UNIV_BM_LINT_VOWN) { Interrupt_counters.vown++; } return IRQ_HANDLED; } //----------------------------------------------------------------------------- // Function : uni_generate_irq // Description: //----------------------------------------------------------------------------- int uni_generate_irq(virqInfo_t * vmeIrq) { int timeout; int looptimeout; timeout = vmeIrq->waitTime; if (timeout == 0) { timeout++; // Wait at least 1 tick... } looptimeout = HZ / 20; // try for 1/20 second vmeIrq->timeOutFlag = 0; // Validate & setup vector register. if (vmeIrq->vector & 1) { // Universe can only generate even vectors return (-EINVAL); } writel(vmeIrq->vector << 24, vmechip_baseaddr + STATID); // Assert VMEbus IRQ writel(1 << (vmeIrq->level + 24), vmechip_baseaddr + VINT_EN); // Wait for syscon to do iack while (readl(vmechip_baseaddr + VINT_STAT) & (1 << (vmeIrq->level + 24))) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(looptimeout); timeout = timeout - looptimeout; if (timeout <= 0) { vmeIrq->timeOutFlag = 1; break; } } // Clear VMEbus IRQ bit writel(0, vmechip_baseaddr + VINT_EN); return (0); } //----------------------------------------------------------------------------- // Function : uni_set_arbiter // Description: //----------------------------------------------------------------------------- int uni_set_arbiter(vmeArbiterCfg_t * vmeArb) { int temp_ctl = 0; int vbto = 0; temp_ctl = readl(vmechip_baseaddr + MISC_CTL); temp_ctl &= 0x00FFFFFF; if (vmeArb->globalTimeoutTimer == 0xFFFFFFFF) { vbto = 7; } else if (vmeArb->globalTimeoutTimer > 1024) { return (-EINVAL); } else if (vmeArb->globalTimeoutTimer == 0) { vbto = 0; } else { vbto = 1; while ((16 * (1 << (vbto - 1))) < vmeArb->globalTimeoutTimer) { vbto += 1; } } temp_ctl |= (vbto << 28); if (vmeArb->arbiterMode == VME_PRIORITY_MODE) { temp_ctl |= 1 << 26; } if (vmeArb->arbiterTimeoutFlag) { temp_ctl |= 2 << 24; } writel(temp_ctl, vmechip_baseaddr + MISC_CTL); return (0); } //----------------------------------------------------------------------------- // Function : uni_get_arbiter // Description: //----------------------------------------------------------------------------- int uni_get_arbiter(vmeArbiterCfg_t * vmeArb) { int temp_ctl = 0; int vbto = 0; temp_ctl = readl(vmechip_baseaddr + MISC_CTL); vbto = (temp_ctl >> 28) & 0xF; if (vbto != 0) { vmeArb->globalTimeoutTimer = (16 * (1 << (vbto - 1))); } if (temp_ctl & (1 << 26)) { vmeArb->arbiterMode = VME_PRIORITY_MODE; } else { vmeArb->arbiterMode = VME_R_ROBIN_MODE; } if (temp_ctl & (3 << 24)) { vmeArb->arbiterTimeoutFlag = 1; } return (0); } //----------------------------------------------------------------------------- // Function : uni_set_requestor // Description: //----------------------------------------------------------------------------- int uni_set_requestor(vmeRequesterCfg_t * vmeReq) { int temp_ctl = 0; temp_ctl = readl(vmechip_baseaddr + MAST_CTL); temp_ctl &= 0xFF0FFFFF; if (vmeReq->releaseMode == 1) { temp_ctl |= (1 << 20); } if (vmeReq->fairMode == 1) { temp_ctl |= (1 << 21); } temp_ctl |= (vmeReq->requestLevel << 22); writel(temp_ctl, vmechip_baseaddr + MAST_CTL); return (0); } //----------------------------------------------------------------------------- // Function : uni_get_requestor // Description: //----------------------------------------------------------------------------- int uni_get_requestor(vmeRequesterCfg_t * vmeReq) { int temp_ctl = 0; temp_ctl = readl(vmechip_baseaddr + MAST_CTL); if (temp_ctl & (1 << 20)) { vmeReq->releaseMode = 1; } if (temp_ctl & (1 << 21)) { vmeReq->fairMode = 1; } vmeReq->requestLevel = (temp_ctl & 0xC00000) >> 22; return (0); } //----------------------------------------------------------------------------- // Function : uni_set_in_bound // Description: //----------------------------------------------------------------------------- int uni_set_in_bound(vmeInWindowCfg_t * vmeIn) { int temp_ctl = 0; // Verify input data if (vmeIn->windowNbr > 7) { return (-EINVAL); } if ((vmeIn->vmeAddrU) || (vmeIn->windowSizeU) || (vmeIn->pciAddrU)) { return (-EINVAL); } if ((vmeIn->vmeAddrL & 0xFFF) || (vmeIn->windowSizeL & 0xFFF) || (vmeIn->pciAddrL & 0xFFF)) { return (-EINVAL); } if (vmeIn->bcastRespond2esst) { return (-EINVAL); } switch (vmeIn->addrSpace) { case VME_A64: case VME_CRCSR: case VME_USER3: case VME_USER4: return (-EINVAL); case VME_A16: temp_ctl |= 0x00000; break; case VME_A24: temp_ctl |= 0x10000; break; case VME_A32: temp_ctl |= 0x20000; break; case VME_USER1: temp_ctl |= 0x60000; break; case VME_USER2: temp_ctl |= 0x70000; break; } // Disable while we are mucking around writel(0x00000000, vmechip_baseaddr + inCTL[vmeIn->windowNbr]); writel(vmeIn->vmeAddrL, vmechip_baseaddr + inBS[vmeIn->windowNbr]); writel(vmeIn->vmeAddrL + vmeIn->windowSizeL, vmechip_baseaddr + inBD[vmeIn->windowNbr]); writel(vmeIn->pciAddrL - vmeIn->vmeAddrL, vmechip_baseaddr + inTO[vmeIn->windowNbr]); // Setup CTL register. if (vmeIn->wrPostEnable) temp_ctl |= 0x40000000; if (vmeIn->prefetchEnable) temp_ctl |= 0x20000000; if (vmeIn->rmwLock) temp_ctl |= 0x00000040; if (vmeIn->data64BitCapable) temp_ctl |= 0x00000080; if (vmeIn->userAccessType & VME_USER) temp_ctl |= 0x00100000; if (vmeIn->userAccessType & VME_SUPER) temp_ctl |= 0x00200000; if (vmeIn->dataAccessType & VME_DATA) temp_ctl |= 0x00400000; if (vmeIn->dataAccessType & VME_PROG) temp_ctl |= 0x00800000; // Write ctl reg without enable writel(temp_ctl, vmechip_baseaddr + inCTL[vmeIn->windowNbr]); if (vmeIn->windowEnable) temp_ctl |= 0x80000000; writel(temp_ctl, vmechip_baseaddr + inCTL[vmeIn->windowNbr]); return (0); } //----------------------------------------------------------------------------- // Function : uni_get_in_bound // Description: //----------------------------------------------------------------------------- int uni_get_in_bound(vmeInWindowCfg_t * vmeIn) { int temp_ctl = 0; // Verify input data if (vmeIn->windowNbr > 7) { return (-EINVAL); } // Get Window mappings. vmeIn->vmeAddrL = readl(vmechip_baseaddr + inBS[vmeIn->windowNbr]); vmeIn->pciAddrL = vmeIn->vmeAddrL + readl(vmechip_baseaddr + inTO[vmeIn->windowNbr]); vmeIn->windowSizeL = readl(vmechip_baseaddr + inBD[vmeIn->windowNbr]) - vmeIn->vmeAddrL; temp_ctl = readl(vmechip_baseaddr + inCTL[vmeIn->windowNbr]); // Get Control & BUS attributes if (temp_ctl & 0x40000000) vmeIn->wrPostEnable = 1; if (temp_ctl & 0x20000000) vmeIn->prefetchEnable = 1; if (temp_ctl & 0x00000040) vmeIn->rmwLock = 1; if (temp_ctl & 0x00000080) vmeIn->data64BitCapable = 1; if (temp_ctl & 0x00100000) vmeIn->userAccessType |= VME_USER; if (temp_ctl & 0x00200000) vmeIn->userAccessType |= VME_SUPER; if (temp_ctl & 0x00400000) vmeIn->dataAccessType |= VME_DATA; if (temp_ctl & 0x00800000) vmeIn->dataAccessType |= VME_PROG; if (temp_ctl & 0x80000000) vmeIn->windowEnable = 1; switch ((temp_ctl & 0x70000) >> 16) { case 0x0: vmeIn->addrSpace = VME_A16; break; case 0x1: vmeIn->addrSpace = VME_A24; break; case 0x2: vmeIn->addrSpace = VME_A32; break; case 0x6: vmeIn->addrSpace = VME_USER1; break; case 0x7: vmeIn->addrSpace = VME_USER2; break; } return (0); } //----------------------------------------------------------------------------- // Function : uni_set_out_bound // Description: //----------------------------------------------------------------------------- int uni_set_out_bound(vmeOutWindowCfg_t * vmeOut) { int temp_ctl = 0; // Verify input data if (vmeOut->windowNbr > 7) { return (-EINVAL); } if ((vmeOut->xlatedAddrU) || (vmeOut->windowSizeU) || (vmeOut->pciBusAddrU)) { return (-EINVAL); } if ((vmeOut->xlatedAddrL & 0xFFF) || (vmeOut->windowSizeL & 0xFFF) || (vmeOut->pciBusAddrL & 0xFFF)) { return (-EINVAL); } if (vmeOut->bcastSelect2esst) { return (-EINVAL); } switch (vmeOut->addrSpace) { case VME_A64: case VME_USER3: case VME_USER4: return (-EINVAL); case VME_A16: temp_ctl |= 0x00000; break; case VME_A24: temp_ctl |= 0x10000; break; case VME_A32: temp_ctl |= 0x20000; break; case VME_CRCSR: temp_ctl |= 0x50000; break; case VME_USER1: temp_ctl |= 0x60000; break; case VME_USER2: temp_ctl |= 0x70000; break; } // Disable while we are mucking around writel(0x00000000, vmechip_baseaddr + outCTL[vmeOut->windowNbr]); writel(vmeOut->pciBusAddrL, vmechip_baseaddr + outBS[vmeOut->windowNbr]); writel(vmeOut->pciBusAddrL + vmeOut->windowSizeL, vmechip_baseaddr + outBD[vmeOut->windowNbr]); writel(vmeOut->xlatedAddrL - vmeOut->pciBusAddrL, vmechip_baseaddr + outTO[vmeOut->windowNbr]); // Sanity check. if (vmeOut->pciBusAddrL != readl(vmechip_baseaddr + outBS[vmeOut->windowNbr])) { printk(KERN_ERR "ca91c042: out window: %x, failed to configure\n", vmeOut->windowNbr); return (-EINVAL); } if (vmeOut->pciBusAddrL + vmeOut->windowSizeL != readl(vmechip_baseaddr + outBD[vmeOut->windowNbr])) { printk(KERN_ERR "ca91c042: out window: %x, failed to configure\n", vmeOut->windowNbr); return (-EINVAL); } if (vmeOut->xlatedAddrL - vmeOut->pciBusAddrL != readl(vmechip_baseaddr + outTO[vmeOut->windowNbr])) { printk(KERN_ERR "ca91c042: out window: %x, failed to configure\n", vmeOut->windowNbr); return (-EINVAL); } // Setup CTL register. if (vmeOut->wrPostEnable) temp_ctl |= 0x40000000; if (vmeOut->userAccessType & VME_SUPER) temp_ctl |= 0x001000; if (vmeOut->dataAccessType & VME_PROG) temp_ctl |= 0x004000; if (vmeOut->maxDataWidth == VME_D16) temp_ctl |= 0x00400000; if (vmeOut->maxDataWidth == VME_D32) temp_ctl |= 0x00800000; if (vmeOut->maxDataWidth == VME_D64) temp_ctl |= 0x00C00000; if (vmeOut->xferProtocol & (VME_BLT | VME_MBLT)) temp_ctl |= 0x00000100; // Write ctl reg without enable writel(temp_ctl, vmechip_baseaddr + outCTL[vmeOut->windowNbr]); if (vmeOut->windowEnable) temp_ctl |= 0x80000000; writel(temp_ctl, vmechip_baseaddr + outCTL[vmeOut->windowNbr]); return (0); } //----------------------------------------------------------------------------- // Function : uni_get_out_bound // Description: //----------------------------------------------------------------------------- int uni_get_out_bound(vmeOutWindowCfg_t * vmeOut) { int temp_ctl = 0; // Verify input data if (vmeOut->windowNbr > 7) { return (-EINVAL); } // Get Window mappings. vmeOut->pciBusAddrL = readl(vmechip_baseaddr + outBS[vmeOut->windowNbr]); vmeOut->xlatedAddrL = vmeOut->pciBusAddrL + readl(vmechip_baseaddr + outTO[vmeOut->windowNbr]); vmeOut->windowSizeL = readl(vmechip_baseaddr + outBD[vmeOut->windowNbr]) - vmeOut->pciBusAddrL; temp_ctl = readl(vmechip_baseaddr + outCTL[vmeOut->windowNbr]); // Get Control & BUS attributes if (temp_ctl & 0x40000000) vmeOut->wrPostEnable = 1; if (temp_ctl & 0x001000) vmeOut->userAccessType = VME_SUPER; else vmeOut->userAccessType = VME_USER; if (temp_ctl & 0x004000) vmeOut->dataAccessType = VME_PROG; else vmeOut->dataAccessType = VME_DATA; if (temp_ctl & 0x80000000) vmeOut->windowEnable = 1; switch ((temp_ctl & 0x00C00000) >> 22) { case 0: vmeOut->maxDataWidth = VME_D8; break; case 1: vmeOut->maxDataWidth = VME_D16; break; case 2: vmeOut->maxDataWidth = VME_D32; break; case 3: vmeOut->maxDataWidth = VME_D64; break; } if (temp_ctl & 0x00000100) vmeOut->xferProtocol = VME_BLT; else vmeOut->xferProtocol = VME_SCT; switch ((temp_ctl & 0x70000) >> 16) { case 0x0: vmeOut->addrSpace = VME_A16; break; case 0x1: vmeOut->addrSpace = VME_A24; break; case 0x2: vmeOut->addrSpace = VME_A32; break; case 0x5: vmeOut->addrSpace = VME_CRCSR; break; case 0x6: vmeOut->addrSpace = VME_USER1; break; case 0x7: vmeOut->addrSpace = VME_USER2; break; } return (0); } //----------------------------------------------------------------------------- // Function : uni_setup_lm // Description: //----------------------------------------------------------------------------- int uni_setup_lm(vmeLmCfg_t * vmeLm) { int temp_ctl = 0; if (vmeLm->addrU) { return (-EINVAL); } switch (vmeLm->addrSpace) { case VME_A64: case VME_USER3: case VME_USER4: return (-EINVAL); case VME_A16: temp_ctl |= 0x00000; break; case VME_A24: temp_ctl |= 0x10000; break; case VME_A32: temp_ctl |= 0x20000; break; case VME_CRCSR: temp_ctl |= 0x50000; break; case VME_USER1: temp_ctl |= 0x60000; break; case VME_USER2: temp_ctl |= 0x70000; break; } // Disable while we are mucking around writel(0x00000000, vmechip_baseaddr + LM_CTL); writel(vmeLm->addr, vmechip_baseaddr + LM_BS); // Setup CTL register. if (vmeLm->userAccessType & VME_SUPER) temp_ctl |= 0x00200000; if (vmeLm->userAccessType & VME_USER) temp_ctl |= 0x00100000; if (vmeLm->dataAccessType & VME_PROG) temp_ctl |= 0x00800000; if (vmeLm->dataAccessType & VME_DATA) temp_ctl |= 0x00400000; uni_lm_event = 0; // Write ctl reg and enable writel(0x80000000 | temp_ctl, vmechip_baseaddr + LM_CTL); temp_ctl = readl(vmechip_baseaddr + LM_CTL); return (0); } //----------------------------------------------------------------------------- // Function : uni_wait_lm // Description: //----------------------------------------------------------------------------- int uni_wait_lm(vmeLmCfg_t * vmeLm) { unsigned long flags; unsigned int tmp; spin_lock_irqsave(&lm_lock, flags); tmp = uni_lm_event; spin_unlock_irqrestore(&lm_lock, flags); if (tmp == 0) { if (vmeLm->lmWait < 10) vmeLm->lmWait = 10; interruptible_sleep_on_timeout(&lm_queue, vmeLm->lmWait); } writel(0x00000000, vmechip_baseaddr + LM_CTL); vmeLm->lmEvents = uni_lm_event; return (0); } #define SWIZZLE(X) ( ((X & 0xFF000000) >> 24) | ((X & 0x00FF0000) >> 8) | ((X & 0x0000FF00) << 8) | ((X & 0x000000FF) << 24)) //----------------------------------------------------------------------------- // Function : uni_do_rmw // Description: //----------------------------------------------------------------------------- int uni_do_rmw(vmeRmwCfg_t * vmeRmw) { int temp_ctl = 0; int tempBS = 0; int tempBD = 0; int tempTO = 0; int vmeBS = 0; int vmeBD = 0; int *rmw_pci_data_ptr = NULL; int *vaDataPtr = NULL; int i; vmeOutWindowCfg_t vmeOut; if (vmeRmw->maxAttempts < 1) { return (-EINVAL); } if (vmeRmw->targetAddrU) { return (-EINVAL); } // Find the PCI address that maps to the desired VME address for (i = 0; i < 8; i++) { temp_ctl = readl(vmechip_baseaddr + outCTL[i]); if ((temp_ctl & 0x80000000) == 0) { continue; } memset(&vmeOut, 0, sizeof(vmeOut)); vmeOut.windowNbr = i; uni_get_out_bound(&vmeOut); if (vmeOut.addrSpace != vmeRmw->addrSpace) { continue; } tempBS = readl(vmechip_baseaddr + outBS[i]); tempBD = readl(vmechip_baseaddr + outBD[i]); tempTO = readl(vmechip_baseaddr + outTO[i]); vmeBS = tempBS + tempTO; vmeBD = tempBD + tempTO; if ((vmeRmw->targetAddr >= vmeBS) && (vmeRmw->targetAddr < vmeBD)) { rmw_pci_data_ptr = (int *)(tempBS + (vmeRmw->targetAddr - vmeBS)); vaDataPtr = (int *)(out_image_va[i] + (vmeRmw->targetAddr - vmeBS)); break; } } // If no window - fail. if (rmw_pci_data_ptr == NULL) { return (-EINVAL); } // Setup the RMW registers. writel(0, vmechip_baseaddr + SCYC_CTL); writel(SWIZZLE(vmeRmw->enableMask), vmechip_baseaddr + SCYC_EN); writel(SWIZZLE(vmeRmw->compareData), vmechip_baseaddr + SCYC_CMP); writel(SWIZZLE(vmeRmw->swapData), vmechip_baseaddr + SCYC_SWP); writel((int)rmw_pci_data_ptr, vmechip_baseaddr + SCYC_ADDR); writel(1, vmechip_baseaddr + SCYC_CTL); // Run the RMW cycle until either success or max attempts. vmeRmw->numAttempts = 1; while (vmeRmw->numAttempts <= vmeRmw->maxAttempts) { if ((readl(vaDataPtr) & vmeRmw->enableMask) == (vmeRmw->swapData & vmeRmw->enableMask)) { writel(0, vmechip_baseaddr + SCYC_CTL); break; } vmeRmw->numAttempts++; } // If no success, set num Attempts to be greater than max attempts if (vmeRmw->numAttempts > vmeRmw->maxAttempts) { vmeRmw->numAttempts = vmeRmw->maxAttempts + 1; } return (0); } //----------------------------------------------------------------------------- // Function : uniSetupDctlReg // Description: //----------------------------------------------------------------------------- int uniSetupDctlReg(vmeDmaPacket_t * vmeDma, int *dctlregreturn) { unsigned int dctlreg = 0x80; struct vmeAttr *vmeAttr; if (vmeDma->srcBus == VME_DMA_VME) { dctlreg = 0; vmeAttr = &vmeDma->srcVmeAttr; } else { dctlreg = 0x80000000; vmeAttr = &vmeDma->dstVmeAttr; } switch (vmeAttr->maxDataWidth) { case VME_D8: break; case VME_D16: dctlreg |= 0x00400000; break; case VME_D32: dctlreg |= 0x00800000; break; case VME_D64: dctlreg |= 0x00C00000; break; } switch (vmeAttr->addrSpace) { case VME_A16: break; case VME_A24: dctlreg |= 0x00010000; break; case VME_A32: dctlreg |= 0x00020000; break; case VME_USER1: dctlreg |= 0x00060000; break; case VME_USER2: dctlreg |= 0x00070000; break; case VME_A64: // not supported in Universe DMA case VME_CRCSR: case VME_USER3: case VME_USER4: return (-EINVAL); break; } if (vmeAttr->userAccessType == VME_PROG) { dctlreg |= 0x00004000; } if (vmeAttr->dataAccessType == VME_SUPER) { dctlreg |= 0x00001000; } if (vmeAttr->xferProtocol != VME_SCT) { dctlreg |= 0x00000100; } *dctlregreturn = dctlreg; return (0); } //----------------------------------------------------------------------------- // Function : uni_start_dma // Description: //----------------------------------------------------------------------------- unsigned int uni_start_dma(int channel, unsigned int dgcsreg, TDMA_Cmd_Packet * vmeLL) { unsigned int val; // Setup registers as needed for direct or chained. if (dgcsreg & 0x8000000) { writel(0, vmechip_baseaddr + DTBC); writel((unsigned int)vmeLL, vmechip_baseaddr + DCPP); } else { #if 0 printk("Starting: DGCS = %08x\n", dgcsreg); printk("Starting: DVA = %08x\n", readl(&vmeLL->dva)); printk("Starting: DLV = %08x\n", readl(&vmeLL->dlv)); printk("Starting: DTBC = %08x\n", readl(&vmeLL->dtbc)); printk("Starting: DCTL = %08x\n", readl(&vmeLL->dctl)); #endif // Write registers writel(readl(&vmeLL->dva), vmechip_baseaddr + DVA); writel(readl(&vmeLL->dlv), vmechip_baseaddr + DLA); writel(readl(&vmeLL->dtbc), vmechip_baseaddr + DTBC); writel(readl(&vmeLL->dctl), vmechip_baseaddr + DCTL); writel(0, vmechip_baseaddr + DCPP); } // Start the operation writel(dgcsreg, vmechip_baseaddr + DGCS); val = get_tbl(); writel(dgcsreg | 0x8000000F, vmechip_baseaddr + DGCS); return (val); } //----------------------------------------------------------------------------- // Function : uni_setup_dma // Description: //----------------------------------------------------------------------------- TDMA_Cmd_Packet *uni_setup_dma(vmeDmaPacket_t * vmeDma) { vmeDmaPacket_t *vmeCur; int maxPerPage; int currentLLcount; TDMA_Cmd_Packet *startLL; TDMA_Cmd_Packet *currentLL; TDMA_Cmd_Packet *nextLL; unsigned int dctlreg = 0; maxPerPage = PAGESIZE / sizeof(TDMA_Cmd_Packet) - 1; startLL = (TDMA_Cmd_Packet *) __get_free_pages(GFP_KERNEL, 0); if (startLL == 0) { return (startLL); } // First allocate pages for descriptors and create linked list vmeCur = vmeDma; currentLL = startLL; currentLLcount = 0; while (vmeCur != 0) { if (vmeCur->pNextPacket != 0) { currentLL->dcpp = (unsigned int)(currentLL + 1); currentLLcount++; if (currentLLcount >= maxPerPage) { currentLL->dcpp = __get_free_pages(GFP_KERNEL, 0); currentLLcount = 0; } currentLL = (TDMA_Cmd_Packet *) currentLL->dcpp; } else { currentLL->dcpp = (unsigned int)0; } vmeCur = vmeCur->pNextPacket; } // Next fill in information for each descriptor vmeCur = vmeDma; currentLL = startLL; while (vmeCur != 0) { if (vmeCur->srcBus == VME_DMA_VME) { writel(vmeCur->srcAddr, ¤tLL->dva); writel(vmeCur->dstAddr, ¤tLL->dlv); } else { writel(vmeCur->srcAddr, ¤tLL->dlv); writel(vmeCur->dstAddr, ¤tLL->dva); } uniSetupDctlReg(vmeCur, &dctlreg); writel(dctlreg, ¤tLL->dctl); writel(vmeCur->byteCount, ¤tLL->dtbc); currentLL = (TDMA_Cmd_Packet *) currentLL->dcpp; vmeCur = vmeCur->pNextPacket; } // Convert Links to PCI addresses. currentLL = startLL; while (currentLL != 0) { nextLL = (TDMA_Cmd_Packet *) currentLL->dcpp; if (nextLL == 0) { writel(1, ¤tLL->dcpp); } else { writel((unsigned int)virt_to_bus(nextLL), ¤tLL->dcpp); } currentLL = nextLL; } // Return pointer to descriptors list return (startLL); } //----------------------------------------------------------------------------- // Function : uni_free_dma // Description: //----------------------------------------------------------------------------- int uni_free_dma(TDMA_Cmd_Packet * startLL) { TDMA_Cmd_Packet *currentLL; TDMA_Cmd_Packet *prevLL; TDMA_Cmd_Packet *nextLL; unsigned int dcppreg; // Convert Links to virtual addresses. currentLL = startLL; while (currentLL != 0) { dcppreg = readl(¤tLL->dcpp); dcppreg &= ~6; if (dcppreg & 1) { currentLL->dcpp = 0; } else { currentLL->dcpp = (unsigned int)bus_to_virt(dcppreg); } currentLL = (TDMA_Cmd_Packet *) currentLL->dcpp; } // Free all pages associated with the descriptors. currentLL = startLL; prevLL = currentLL; while (currentLL != 0) { nextLL = (TDMA_Cmd_Packet *) currentLL->dcpp; if (currentLL + 1 != nextLL) { free_pages((int)prevLL, 0); prevLL = nextLL; } currentLL = nextLL; } // Return pointer to descriptors list return (0); } //----------------------------------------------------------------------------- // Function : uni_do_dma // Description: //----------------------------------------------------------------------------- int uni_do_dma(vmeDmaPacket_t * vmeDma) { unsigned int dgcsreg = 0; unsigned int dctlreg = 0; int val; int channel, x; vmeDmaPacket_t *curDma; TDMA_Cmd_Packet *dmaLL; // Sanity check the VME chain. channel = vmeDma->channel_number; if (channel > 0) { return (-EINVAL); } curDma = vmeDma; while (curDma != 0) { if (curDma->byteCount == 0) { return (-EINVAL); } if (curDma->byteCount >= 0x1000000) { return (-EINVAL); } if ((curDma->srcAddr & 7) != (curDma->dstAddr & 7)) { return (-EINVAL); } switch (curDma->srcBus) { case VME_DMA_PCI: if (curDma->dstBus != VME_DMA_VME) { return (-EINVAL); } break; case VME_DMA_VME: if (curDma->dstBus != VME_DMA_PCI) { return (-EINVAL); } break; default: return (-EINVAL); break; } if (uniSetupDctlReg(curDma, &dctlreg) < 0) { return (-EINVAL); } curDma = curDma->pNextPacket; if (curDma == vmeDma) { // Endless Loop! return (-EINVAL); } } // calculate control register if (vmeDma->pNextPacket != 0) { dgcsreg = 0x8000000; } else { dgcsreg = 0; } for (x = 0; x < 8; x++) { // vme block size if ((256 << x) >= vmeDma->maxVmeBlockSize) { break; } } if (x == 8) x = 7; dgcsreg |= (x << 20); if (vmeDma->vmeBackOffTimer) { for (x = 1; x < 8; x++) { // vme timer if ((16 << (x - 1)) >= vmeDma->vmeBackOffTimer) { break; } } if (x == 8) x = 7; dgcsreg |= (x << 16); } // Setup the dma chain dmaLL = uni_setup_dma(vmeDma); // Start the DMA if (dgcsreg & 0x8000000) { vmeDma->vmeDmaStartTick = uni_start_dma(channel, dgcsreg, (TDMA_Cmd_Packet *) virt_to_phys(dmaLL)); } else { vmeDma->vmeDmaStartTick = uni_start_dma(channel, dgcsreg, dmaLL); } wait_event_interruptible(dma_queue[0], readl(vmechip_baseaddr + DGCS) & 0x800); val = readl(vmechip_baseaddr + DGCS); writel(val | 0xF00, vmechip_baseaddr + DGCS); vmeDma->vmeDmaStatus = 0; vmeDma->vmeDmaStopTick = uni_dma_irq_time; if (vmeDma->vmeDmaStopTick < vmeDma->vmeDmaStartTick) { vmeDma->vmeDmaElapsedTime = (0xFFFFFFFF - vmeDma->vmeDmaStartTick) + vmeDma->vmeDmaStopTick; } else { vmeDma->vmeDmaElapsedTime = vmeDma->vmeDmaStopTick - vmeDma->vmeDmaStartTick; } vmeDma->vmeDmaElapsedTime -= vmechip_irq_overhead_ticks; vmeDma->vmeDmaElapsedTime /= (tb_speed / 1000000); if (!(val & 0x00000800)) { vmeDma->vmeDmaStatus = val & 0x700; printk(KERN_ERR "ca91c042: DMA Error in DMA_uni_irqhandler DGCS=%08X\n", val); val = readl(vmechip_baseaddr + DCPP); printk(KERN_ERR "ca91c042: DCPP=%08X\n", val); val = readl(vmechip_baseaddr + DCTL); printk(KERN_ERR "ca91c042: DCTL=%08X\n", val); val = readl(vmechip_baseaddr + DTBC); printk(KERN_ERR "ca91c042: DTBC=%08X\n", val); val = readl(vmechip_baseaddr + DLA); printk(KERN_ERR "ca91c042: DLA=%08X\n", val); val = readl(vmechip_baseaddr + DVA); printk(KERN_ERR "ca91c042: DVA=%08X\n", val); } // Free the dma chain uni_free_dma(dmaLL); return (0); } //----------------------------------------------------------------------------- // Function : uni_shutdown // Description: Put VME bridge in quiescent state. //----------------------------------------------------------------------------- void uni_shutdown(void) { writel(0, vmechip_baseaddr + LINT_EN); // Turn off Ints // Turn off the windows writel(0x00800000, vmechip_baseaddr + LSI0_CTL); writel(0x00800000, vmechip_baseaddr + LSI1_CTL); writel(0x00800000, vmechip_baseaddr + LSI2_CTL); writel(0x00800000, vmechip_baseaddr + LSI3_CTL); writel(0x00F00000, vmechip_baseaddr + VSI0_CTL); writel(0x00F00000, vmechip_baseaddr + VSI1_CTL); writel(0x00F00000, vmechip_baseaddr + VSI2_CTL); writel(0x00F00000, vmechip_baseaddr + VSI3_CTL); if (vmechip_revision >= 2) { writel(0x00800000, vmechip_baseaddr + LSI4_CTL); writel(0x00800000, vmechip_baseaddr + LSI5_CTL); writel(0x00800000, vmechip_baseaddr + LSI6_CTL); writel(0x00800000, vmechip_baseaddr + LSI7_CTL); writel(0x00F00000, vmechip_baseaddr + VSI4_CTL); writel(0x00F00000, vmechip_baseaddr + VSI5_CTL); writel(0x00F00000, vmechip_baseaddr + VSI6_CTL); writel(0x00F00000, vmechip_baseaddr + VSI7_CTL); } } //----------------------------------------------------------------------------- // Function : uni_init() // Description: //----------------------------------------------------------------------------- int uni_init(void) { int result; unsigned int tmp; unsigned int crcsr_addr; unsigned int irqOverHeadStart; int overHeadTicks; uni_shutdown(); // Write to Misc Register // Set VME Bus Time-out // Arbitration Mode // DTACK Enable tmp = readl(vmechip_baseaddr + MISC_CTL) & 0x0832BFFF; tmp |= 0x76040000; writel(tmp, vmechip_baseaddr + MISC_CTL); if (tmp & 0x20000) { vme_syscon = 1; } else { vme_syscon = 0; } // Clear DMA status log writel(0x00000F00, vmechip_baseaddr + DGCS); // Clear and enable error log writel(0x00800000, vmechip_baseaddr + L_CMDERR); // Turn off location monitor writel(0x00000000, vmechip_baseaddr + LM_CTL); // Initialize crcsr map if (vme_slotnum != -1) { writel(vme_slotnum << 27, vmechip_baseaddr + VCSR_BS); } crcsr_addr = readl(vmechip_baseaddr + VCSR_BS) >> 8; writel((unsigned int)vmechip_interboard_datap - crcsr_addr, vmechip_baseaddr + VCSR_TO); if (vme_slotnum != -1) { writel(0x80000000, vmechip_baseaddr + VCSR_CTL); } // Turn off interrupts writel(0x00000000, vmechip_baseaddr + LINT_EN); // Disable interrupts in the Universe first writel(0x00FFFFFF, vmechip_baseaddr + LINT_STAT); // Clear Any Pending Interrupts writel(0x00000000, vmechip_baseaddr + VINT_EN); // Disable interrupts in the Universe first result = request_irq(vmechip_irq, uni_irqhandler, IRQF_SHARED | IRQF_DISABLED, "VMEBus (ca91c042)", vmechip_baseaddr); if (result) { printk(KERN_ERR "ca91c042: can't get assigned pci irq vector %02X\n", vmechip_irq); return (0); } else { writel(0x0000, vmechip_baseaddr + LINT_MAP0); // Map all ints to 0 writel(0x0000, vmechip_baseaddr + LINT_MAP1); // Map all ints to 0 writel(0x0000, vmechip_baseaddr + LINT_MAP2); // Map all ints to 0 } // Enable DMA, mailbox, VIRQ & LM Interrupts if (vme_syscon) tmp = 0x00FF07FE; else tmp = 0x00FF0700; writel(tmp, vmechip_baseaddr + LINT_EN); // Do a quick sanity test of the bridge if (readl(vmechip_baseaddr + LINT_EN) != tmp) { return (0); } if (readl(vmechip_baseaddr + PCI_CLASS_REVISION) != 0x06800002) { return (0); } for (tmp = 1; tmp < 0x80000000; tmp = tmp << 1) { writel(tmp, vmechip_baseaddr + SCYC_EN); writel(~tmp, vmechip_baseaddr + SCYC_CMP); if (readl(vmechip_baseaddr + SCYC_EN) != tmp) { return (0); } if (readl(vmechip_baseaddr + SCYC_CMP) != ~tmp) { return (0); } } // do a mail box interrupt to calibrate the interrupt overhead. irqOverHeadStart = get_tbl(); writel(0, vmechip_baseaddr + MBOX1); for (tmp = 0; tmp < 10; tmp++) { } irqOverHeadStart = get_tbl(); writel(0, vmechip_baseaddr + MBOX1); for (tmp = 0; tmp < 10; tmp++) { } overHeadTicks = uni_irq_time - irqOverHeadStart; if (overHeadTicks > 0) { vmechip_irq_overhead_ticks = overHeadTicks; } else { vmechip_irq_overhead_ticks = 1; } return (1); }