patch-2.4.19 linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_ate.c
Next file: linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_config.c
Previous file: linux-2.4.19/arch/ia64/sn/io/sn2/ml_SN_intr.c
Back to the patch index
Back to the overall index
- Lines: 455
- Date:
Fri Aug 2 17:39:43 2002
- Orig file:
linux-2.4.18/arch/ia64/sn/io/sn2/pcibr/pcibr_ate.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -urN linux-2.4.18/arch/ia64/sn/io/sn2/pcibr/pcibr_ate.c linux-2.4.19/arch/ia64/sn/io/sn2/pcibr/pcibr_ate.c
@@ -0,0 +1,454 @@
+/*
+ *
+ * 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.
+ *
+ * Copyright (C) 2001-2002 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/sn/sgi.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/iograph.h>
+#include <asm/sn/invent.h>
+#include <asm/sn/hcl.h>
+#include <asm/sn/labelcl.h>
+#include <asm/sn/xtalk/xwidget.h>
+#include <asm/sn/pci/bridge.h>
+#include <asm/sn/pci/pciio.h>
+#include <asm/sn/pci/pcibr.h>
+#include <asm/sn/pci/pcibr_private.h>
+#include <asm/sn/pci/pci_defs.h>
+#include <asm/sn/prio.h>
+#include <asm/sn/xtalk/xbow.h>
+#include <asm/sn/ioc3.h>
+#include <asm/sn/eeprom.h>
+#include <asm/sn/io.h>
+#include <asm/sn/sn_private.h>
+
+#ifdef __ia64
+uint64_t atealloc(struct map *mp, size_t size);
+void atefree(struct map *mp, size_t size, uint64_t a);
+void atemapfree(struct map *mp);
+struct map *atemapalloc(uint64_t mapsiz);
+
+#define rmallocmap atemapalloc
+#define rmfreemap atemapfree
+#define rmfree atefree
+#define rmalloc atealloc
+#endif
+
+
+#ifdef LATER
+#if (PCIBR_FREEZE_TIME) || PCIBR_ATE_DEBUG
+LOCAL struct reg_desc ate_bits[] =
+{
+ {0xFFFF000000000000ull, -48, "RMF", "%x"},
+ {~(IOPGSIZE - 1) & /* may trim off some low bits */
+ 0x0000FFFFFFFFF000ull, 0, "XIO", "%x"},
+ {0x0000000000000F00ull, -8, "port", "%x"},
+ {0x0000000000000010ull, 0, "Barrier"},
+ {0x0000000000000008ull, 0, "Prefetch"},
+ {0x0000000000000004ull, 0, "Precise"},
+ {0x0000000000000002ull, 0, "Coherent"},
+ {0x0000000000000001ull, 0, "Valid"},
+ {0}
+};
+#endif
+#endif /* LATER */
+
+#ifndef LOCAL
+#define LOCAL static
+#endif
+
+/*
+ * functions
+ */
+int pcibr_init_ext_ate_ram(bridge_t *);
+int pcibr_ate_alloc(pcibr_soft_t, int);
+void pcibr_ate_free(pcibr_soft_t, int, int);
+bridge_ate_t pcibr_flags_to_ate(unsigned);
+bridge_ate_p pcibr_ate_addr(pcibr_soft_t, int);
+unsigned ate_freeze(pcibr_dmamap_t pcibr_dmamap,
+#if PCIBR_FREEZE_TIME
+ unsigned *freeze_time_ptr,
+#endif
+ unsigned *cmd_regs);
+void ate_write(bridge_ate_p ate_ptr, int ate_count, bridge_ate_t ate);
+void ate_thaw(pcibr_dmamap_t pcibr_dmamap,
+ int ate_index,
+#if PCIBR_FREEZE_TIME
+ bridge_ate_t ate,
+ int ate_total,
+ unsigned freeze_time_start,
+#endif
+ unsigned *cmd_regs,
+ unsigned s);
+
+
+/* Convert from ssram_bits in control register to number of SSRAM entries */
+#define ATE_NUM_ENTRIES(n) _ate_info[n]
+
+/* Possible choices for number of ATE entries in Bridge's SSRAM */
+LOCAL int _ate_info[] =
+{
+ 0, /* 0 entries */
+ 8 * 1024, /* 8K entries */
+ 16 * 1024, /* 16K entries */
+ 64 * 1024 /* 64K entries */
+};
+
+#define ATE_NUM_SIZES (sizeof(_ate_info) / sizeof(int))
+#define ATE_PROBE_VALUE 0x0123456789abcdefULL
+
+/*
+ * Determine the size of this bridge's external mapping SSRAM, and set
+ * the control register appropriately to reflect this size, and initialize
+ * the external SSRAM.
+ */
+int
+pcibr_init_ext_ate_ram(bridge_t *bridge)
+{
+ int largest_working_size = 0;
+ int num_entries, entry;
+ int i, j;
+ bridgereg_t old_enable, new_enable;
+ int s;
+
+ /* Probe SSRAM to determine its size. */
+ old_enable = bridge->b_int_enable;
+ new_enable = old_enable & ~BRIDGE_IMR_PCI_MST_TIMEOUT;
+ bridge->b_int_enable = new_enable;
+
+ for (i = 1; i < ATE_NUM_SIZES; i++) {
+ /* Try writing a value */
+ bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(i) - 1] = ATE_PROBE_VALUE;
+
+ /* Guard against wrap */
+ for (j = 1; j < i; j++)
+ bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(j) - 1] = 0;
+
+ /* See if value was written */
+ if (bridge->b_ext_ate_ram[ATE_NUM_ENTRIES(i) - 1] == ATE_PROBE_VALUE)
+ largest_working_size = i;
+ }
+ bridge->b_int_enable = old_enable;
+ bridge->b_wid_tflush; /* wait until Bridge PIO complete */
+
+ /*
+ * ensure that we write and read without any interruption.
+ * The read following the write is required for the Bridge war
+ */
+
+ s = splhi();
+ bridge->b_wid_control = (bridge->b_wid_control
+ & ~BRIDGE_CTRL_SSRAM_SIZE_MASK)
+ | BRIDGE_CTRL_SSRAM_SIZE(largest_working_size);
+ bridge->b_wid_control; /* inval addr bug war */
+ splx(s);
+
+ num_entries = ATE_NUM_ENTRIES(largest_working_size);
+
+#if PCIBR_ATE_DEBUG
+ if (num_entries)
+ printk("bridge at 0x%x: clearing %d external ATEs\n", bridge, num_entries);
+ else
+ printk("bridge at 0x%x: no external ATE RAM found\n", bridge);
+#endif
+
+ /* Initialize external mapping entries */
+ for (entry = 0; entry < num_entries; entry++)
+ bridge->b_ext_ate_ram[entry] = 0;
+
+ return (num_entries);
+}
+
+/*
+ * Allocate "count" contiguous Bridge Address Translation Entries
+ * on the specified bridge to be used for PCI to XTALK mappings.
+ * Indices in rm map range from 1..num_entries. Indicies returned
+ * to caller range from 0..num_entries-1.
+ *
+ * Return the start index on success, -1 on failure.
+ */
+int
+pcibr_ate_alloc(pcibr_soft_t pcibr_soft, int count)
+{
+ int index = 0;
+
+ index = (int) rmalloc(pcibr_soft->bs_int_ate_map, (size_t) count);
+
+ if (!index && pcibr_soft->bs_ext_ate_map)
+ index = (int) rmalloc(pcibr_soft->bs_ext_ate_map, (size_t) count);
+
+ /* rmalloc manages resources in the 1..n
+ * range, with 0 being failure.
+ * pcibr_ate_alloc manages resources
+ * in the 0..n-1 range, with -1 being failure.
+ */
+ return index - 1;
+}
+
+void
+pcibr_ate_free(pcibr_soft_t pcibr_soft, int index, int count)
+/* Who says there's no such thing as a free meal? :-) */
+{
+ /* note the "+1" since rmalloc handles 1..n but
+ * we start counting ATEs at zero.
+ */
+ rmfree((index < pcibr_soft->bs_int_ate_size)
+ ? pcibr_soft->bs_int_ate_map
+ : pcibr_soft->bs_ext_ate_map,
+ count, index + 1);
+}
+
+/*
+ * Convert PCI-generic software flags and Bridge-specific software flags
+ * into Bridge-specific Address Translation Entry attribute bits.
+ */
+bridge_ate_t
+pcibr_flags_to_ate(unsigned flags)
+{
+ bridge_ate_t attributes;
+
+ /* default if nothing specified:
+ * NOBARRIER
+ * NOPREFETCH
+ * NOPRECISE
+ * COHERENT
+ * Plus the valid bit
+ */
+ attributes = ATE_CO | ATE_V;
+
+ /* Generic macro flags
+ */
+ if (flags & PCIIO_DMA_DATA) { /* standard data channel */
+ attributes &= ~ATE_BAR; /* no barrier */
+ attributes |= ATE_PREF; /* prefetch on */
+ }
+ if (flags & PCIIO_DMA_CMD) { /* standard command channel */
+ attributes |= ATE_BAR; /* barrier bit on */
+ attributes &= ~ATE_PREF; /* disable prefetch */
+ }
+ /* Generic detail flags
+ */
+ if (flags & PCIIO_PREFETCH)
+ attributes |= ATE_PREF;
+ if (flags & PCIIO_NOPREFETCH)
+ attributes &= ~ATE_PREF;
+
+ /* Provider-specific flags
+ */
+ if (flags & PCIBR_BARRIER)
+ attributes |= ATE_BAR;
+ if (flags & PCIBR_NOBARRIER)
+ attributes &= ~ATE_BAR;
+
+ if (flags & PCIBR_PREFETCH)
+ attributes |= ATE_PREF;
+ if (flags & PCIBR_NOPREFETCH)
+ attributes &= ~ATE_PREF;
+
+ if (flags & PCIBR_PRECISE)
+ attributes |= ATE_PREC;
+ if (flags & PCIBR_NOPRECISE)
+ attributes &= ~ATE_PREC;
+
+ return (attributes);
+}
+
+/*
+ * Setup an Address Translation Entry as specified. Use either the Bridge
+ * internal maps or the external map RAM, as appropriate.
+ */
+bridge_ate_p
+pcibr_ate_addr(pcibr_soft_t pcibr_soft,
+ int ate_index)
+{
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ return (ate_index < pcibr_soft->bs_int_ate_size)
+ ? &(bridge->b_int_ate_ram[ate_index].wr)
+ : &(bridge->b_ext_ate_ram[ate_index]);
+}
+
+/* We are starting to get more complexity
+ * surrounding writing ATEs, so pull
+ * the writing code into this new function.
+ */
+
+#if PCIBR_FREEZE_TIME
+#define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, &freeze_time, cmd_regs)
+#else
+#define ATE_FREEZE() s = ate_freeze(pcibr_dmamap, cmd_regs)
+#endif
+
+unsigned
+ate_freeze(pcibr_dmamap_t pcibr_dmamap,
+#if PCIBR_FREEZE_TIME
+ unsigned *freeze_time_ptr,
+#endif
+ unsigned *cmd_regs)
+{
+ pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
+#ifdef LATER
+ int dma_slot = pcibr_dmamap->bd_slot;
+#endif
+ int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
+ int slot;
+
+ unsigned long s;
+ unsigned cmd_reg;
+ volatile unsigned *cmd_lwa;
+ unsigned cmd_lwd;
+
+ if (!ext_ates)
+ return 0;
+
+ /* Bridge Hardware Bug WAR #484930:
+ * Bridge can't handle updating External ATEs
+ * while DMA is occuring that uses External ATEs,
+ * even if the particular ATEs involved are disjoint.
+ */
+
+ /* need to prevent anyone else from
+ * unfreezing the grant while we
+ * are working; also need to prevent
+ * this thread from being interrupted
+ * to keep PCI grant freeze time
+ * at an absolute minimum.
+ */
+ s = pcibr_lock(pcibr_soft);
+
+#ifdef LATER
+ /* just in case pcibr_dmamap_done was not called */
+ if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_BUSY) {
+ pcibr_dmamap->bd_flags &= ~PCIBR_DMAMAP_BUSY;
+ if (pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM)
+ atomic_dec(&(pcibr_soft->bs_slot[dma_slot]. bss_ext_ates_active));
+ xtalk_dmamap_done(pcibr_dmamap->bd_xtalk);
+ }
+#endif /* LATER */
+#if PCIBR_FREEZE_TIME
+ *freeze_time_ptr = get_timestamp();
+#endif
+
+ cmd_lwa = 0;
+ for (slot = 0; slot < 8; ++slot)
+ if (atomic_read(&pcibr_soft->bs_slot[slot].bss_ext_ates_active)) {
+ cmd_reg = pcibr_soft->
+ bs_slot[slot].
+ bss_cmd_shadow;
+ if (cmd_reg & PCI_CMD_BUS_MASTER) {
+ cmd_lwa = pcibr_soft->
+ bs_slot[slot].
+ bss_cmd_pointer;
+ cmd_lwd = cmd_reg ^ PCI_CMD_BUS_MASTER;
+ cmd_lwa[0] = cmd_lwd;
+ }
+ cmd_regs[slot] = cmd_reg;
+ } else
+ cmd_regs[slot] = 0;
+
+ if (cmd_lwa) {
+ bridge_t *bridge = pcibr_soft->bs_base;
+
+ /* Read the last master bit that has been cleared. This PIO read
+ * on the PCI bus is to ensure the completion of any DMAs that
+ * are due to bus requests issued by PCI devices before the
+ * clearing of master bits.
+ */
+ cmd_lwa[0];
+
+ /* Flush all the write buffers in the bridge */
+ for (slot = 0; slot < 8; ++slot)
+ if (atomic_read(&pcibr_soft->bs_slot[slot].bss_ext_ates_active)) {
+ /* Flush the write buffer associated with this
+ * PCI device which might be using dma map RAM.
+ */
+ bridge->b_wr_req_buf[slot].reg;
+ }
+ }
+ return s;
+}
+
+#define ATE_WRITE() ate_write(ate_ptr, ate_count, ate)
+
+void
+ate_write(bridge_ate_p ate_ptr,
+ int ate_count,
+ bridge_ate_t ate)
+{
+ while (ate_count-- > 0) {
+ *ate_ptr++ = ate;
+ ate += IOPGSIZE;
+ }
+}
+
+#if PCIBR_FREEZE_TIME
+#define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, ate, ate_total, freeze_time, cmd_regs, s)
+#else
+#define ATE_THAW() ate_thaw(pcibr_dmamap, ate_index, cmd_regs, s)
+#endif
+
+void
+ate_thaw(pcibr_dmamap_t pcibr_dmamap,
+ int ate_index,
+#if PCIBR_FREEZE_TIME
+ bridge_ate_t ate,
+ int ate_total,
+ unsigned freeze_time_start,
+#endif
+ unsigned *cmd_regs,
+ unsigned s)
+{
+ pcibr_soft_t pcibr_soft = pcibr_dmamap->bd_soft;
+ int dma_slot = pcibr_dmamap->bd_slot;
+ int slot;
+ bridge_t *bridge = pcibr_soft->bs_base;
+ int ext_ates = pcibr_dmamap->bd_flags & PCIBR_DMAMAP_SSRAM;
+
+ unsigned cmd_reg;
+
+#if PCIBR_FREEZE_TIME
+ unsigned freeze_time;
+ static unsigned max_freeze_time = 0;
+ static unsigned max_ate_total;
+#endif
+
+ if (!ext_ates)
+ return;
+
+ /* restore cmd regs */
+ for (slot = 0; slot < 8; ++slot)
+ if ((cmd_reg = cmd_regs[slot]) & PCI_CMD_BUS_MASTER)
+ bridge->b_type0_cfg_dev[slot].l[PCI_CFG_COMMAND / 4] = cmd_reg;
+
+ pcibr_dmamap->bd_flags |= PCIBR_DMAMAP_BUSY;
+ atomic_inc(&(pcibr_soft->bs_slot[dma_slot]. bss_ext_ates_active));
+
+#if PCIBR_FREEZE_TIME
+ freeze_time = get_timestamp() - freeze_time_start;
+
+ if ((max_freeze_time < freeze_time) ||
+ (max_ate_total < ate_total)) {
+ if (max_freeze_time < freeze_time)
+ max_freeze_time = freeze_time;
+ if (max_ate_total < ate_total)
+ max_ate_total = ate_total;
+ pcibr_unlock(pcibr_soft, s);
+ printk("%s: pci freeze time %d usec for %d ATEs\n"
+ "\tfirst ate: %R\n",
+ pcibr_soft->bs_name,
+ freeze_time * 1000 / 1250,
+ ate_total,
+ ate, ate_bits);
+ } else
+#endif
+ pcibr_unlock(pcibr_soft, s);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)