summaryrefslogtreecommitdiffstats
path: root/drivers/ieee1394/ohci1394.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ieee1394/ohci1394.c')
-rw-r--r--drivers/ieee1394/ohci1394.c3582
1 files changed, 3582 insertions, 0 deletions
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
new file mode 100644
index 0000000..e509e13
--- /dev/null
+++ b/drivers/ieee1394/ohci1394.c
@@ -0,0 +1,3582 @@
+/*
+ * ohci1394.c - driver for OHCI 1394 boards
+ * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
+ * Gord Peters <GordPeters@smarttech.com>
+ * 2001 Ben Collins <bcollins@debian.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Things known to be working:
+ * . Async Request Transmit
+ * . Async Response Receive
+ * . Async Request Receive
+ * . Async Response Transmit
+ * . Iso Receive
+ * . DMA mmap for iso receive
+ * . Config ROM generation
+ *
+ * Things implemented, but still in test phase:
+ * . Iso Transmit
+ * . Async Stream Packets Transmit (Receive done via Iso interface)
+ *
+ * Things not implemented:
+ * . DMA error recovery
+ *
+ * Known bugs:
+ * . devctl BUS_RESET arg confusion (reset type or root holdoff?)
+ * added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk
+ */
+
+/*
+ * Acknowledgments:
+ *
+ * Adam J Richter <adam@yggdrasil.com>
+ * . Use of pci_class to find device
+ *
+ * Emilie Chung <emilie.chung@axis.com>
+ * . Tip on Async Request Filter
+ *
+ * Pascal Drolet <pascal.drolet@informission.ca>
+ * . Various tips for optimization and functionnalities
+ *
+ * Robert Ficklin <rficklin@westengineering.com>
+ * . Loop in irq_handler
+ *
+ * James Goodwin <jamesg@Filanet.com>
+ * . Various tips on initialization, self-id reception, etc.
+ *
+ * Albrecht Dress <ad@mpifr-bonn.mpg.de>
+ * . Apple PowerBook detection
+ *
+ * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de>
+ * . Reset the board properly before leaving + misc cleanups
+ *
+ * Leon van Stuivenberg <leonvs@iae.nl>
+ * . Bug fixes
+ *
+ * Ben Collins <bcollins@debian.org>
+ * . Working big-endian support
+ * . Updated to 2.4.x module scheme (PCI aswell)
+ * . Config ROM generation
+ *
+ * Manfred Weihs <weihs@ict.tuwien.ac.at>
+ * . Reworked code for initiating bus resets
+ * (long, short, with or without hold-off)
+ *
+ * Nandu Santhi <contactnandu@users.sourceforge.net>
+ * . Added support for nVidia nForce2 onboard Firewire chipset
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <asm/byteorder.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/irq.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_PPC_PMAC
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#endif
+
+#include "csr1212.h"
+#include "ieee1394.h"
+#include "ieee1394_types.h"
+#include "hosts.h"
+#include "dma.h"
+#include "iso.h"
+#include "ieee1394_core.h"
+#include "highlevel.h"
+#include "ohci1394.h"
+
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+#define OHCI1394_DEBUG
+#endif
+
+#ifdef DBGMSG
+#undef DBGMSG
+#endif
+
+#ifdef OHCI1394_DEBUG
+#define DBGMSG(fmt, args...) \
+printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
+#else
+#define DBGMSG(fmt, args...) do {} while (0)
+#endif
+
+/* print general (card independent) information */
+#define PRINT_G(level, fmt, args...) \
+printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args)
+
+/* print card specific information */
+#define PRINT(level, fmt, args...) \
+printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
+
+/* Module Parameters */
+static int phys_dma = 1;
+module_param(phys_dma, int, 0444);
+MODULE_PARM_DESC(phys_dma, "Enable physical DMA (default = 1).");
+
+static void dma_trm_tasklet(unsigned long data);
+static void dma_trm_reset(struct dma_trm_ctx *d);
+
+static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int buf_size, int split_buf_size, int context_base);
+static void free_dma_rcv_ctx(struct dma_rcv_ctx *d);
+
+static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int context_base);
+
+static void ohci1394_pci_remove(struct pci_dev *pdev);
+
+#ifndef __LITTLE_ENDIAN
+static const size_t hdr_sizes[] = {
+ 3, /* TCODE_WRITEQ */
+ 4, /* TCODE_WRITEB */
+ 3, /* TCODE_WRITE_RESPONSE */
+ 0, /* reserved */
+ 3, /* TCODE_READQ */
+ 4, /* TCODE_READB */
+ 3, /* TCODE_READQ_RESPONSE */
+ 4, /* TCODE_READB_RESPONSE */
+ 1, /* TCODE_CYCLE_START */
+ 4, /* TCODE_LOCK_REQUEST */
+ 2, /* TCODE_ISO_DATA */
+ 4, /* TCODE_LOCK_RESPONSE */
+ /* rest is reserved or link-internal */
+};
+
+static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode)
+{
+ size_t size;
+
+ if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes)))
+ return;
+
+ size = hdr_sizes[tcode];
+ while (size--)
+ data[size] = le32_to_cpu(data[size]);
+}
+#else
+#define header_le32_to_cpu(w,x) do {} while (0)
+#endif /* !LITTLE_ENDIAN */
+
+/***********************************
+ * IEEE-1394 functionality section *
+ ***********************************/
+
+static u8 get_phy_reg(struct ti_ohci *ohci, u8 addr)
+{
+ int i;
+ unsigned long flags;
+ quadlet_t r;
+
+ spin_lock_irqsave (&ohci->phy_reg_lock, flags);
+
+ reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000)
+ break;
+
+ mdelay(1);
+ }
+
+ r = reg_read(ohci, OHCI1394_PhyControl);
+
+ if (i >= OHCI_LOOP_COUNT)
+ PRINT (KERN_ERR, "Get PHY Reg timeout [0x%08x/0x%08x/%d]",
+ r, r & 0x80000000, i);
+
+ spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
+
+ return (r & 0x00ff0000) >> 16;
+}
+
+static void set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data)
+{
+ int i;
+ unsigned long flags;
+ u32 r = 0;
+
+ spin_lock_irqsave (&ohci->phy_reg_lock, flags);
+
+ reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ r = reg_read(ohci, OHCI1394_PhyControl);
+ if (!(r & 0x00004000))
+ break;
+
+ mdelay(1);
+ }
+
+ if (i == OHCI_LOOP_COUNT)
+ PRINT (KERN_ERR, "Set PHY Reg timeout [0x%08x/0x%08x/%d]",
+ r, r & 0x00004000, i);
+
+ spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
+
+ return;
+}
+
+/* Or's our value into the current value */
+static void set_phy_reg_mask(struct ti_ohci *ohci, u8 addr, u8 data)
+{
+ u8 old;
+
+ old = get_phy_reg (ohci, addr);
+ old |= data;
+ set_phy_reg (ohci, addr, old);
+
+ return;
+}
+
+static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
+ int phyid, int isroot)
+{
+ quadlet_t *q = ohci->selfid_buf_cpu;
+ quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount);
+ size_t size;
+ quadlet_t q0, q1;
+
+ /* Check status of self-id reception */
+
+ if (ohci->selfid_swap)
+ q0 = le32_to_cpu(q[0]);
+ else
+ q0 = q[0];
+
+ if ((self_id_count & 0x80000000) ||
+ ((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) {
+ PRINT(KERN_ERR,
+ "Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)",
+ self_id_count, q0, ohci->self_id_errors);
+
+ /* Tip by James Goodwin <jamesg@Filanet.com>:
+ * We had an error, generate another bus reset in response. */
+ if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) {
+ set_phy_reg_mask (ohci, 1, 0x40);
+ ohci->self_id_errors++;
+ } else {
+ PRINT(KERN_ERR,
+ "Too many errors on SelfID error reception, giving up!");
+ }
+ return;
+ }
+
+ /* SelfID Ok, reset error counter. */
+ ohci->self_id_errors = 0;
+
+ size = ((self_id_count & 0x00001FFC) >> 2) - 1;
+ q++;
+
+ while (size > 0) {
+ if (ohci->selfid_swap) {
+ q0 = le32_to_cpu(q[0]);
+ q1 = le32_to_cpu(q[1]);
+ } else {
+ q0 = q[0];
+ q1 = q[1];
+ }
+
+ if (q0 == ~q1) {
+ DBGMSG ("SelfID packet 0x%x received", q0);
+ hpsb_selfid_received(host, cpu_to_be32(q0));
+ if (((q0 & 0x3f000000) >> 24) == phyid)
+ DBGMSG ("SelfID for this node is 0x%08x", q0);
+ } else {
+ PRINT(KERN_ERR,
+ "SelfID is inconsistent [0x%08x/0x%08x]", q0, q1);
+ }
+ q += 2;
+ size -= 2;
+ }
+
+ DBGMSG("SelfID complete");
+
+ return;
+}
+
+static void ohci_soft_reset(struct ti_ohci *ohci) {
+ int i;
+
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset))
+ break;
+ mdelay(1);
+ }
+ DBGMSG ("Soft reset finished");
+}
+
+
+/* Generate the dma receive prgs and start the context */
+static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ int i;
+
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ for (i=0; i<d->num_desc; i++) {
+ u32 c;
+
+ c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH;
+ if (generate_irq)
+ c |= DMA_CTL_IRQ;
+
+ d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size);
+
+ /* End of descriptor list? */
+ if (i + 1 < d->num_desc) {
+ d->prg_cpu[i]->branchAddress =
+ cpu_to_le32((d->prg_bus[i+1] & 0xfffffff0) | 0x1);
+ } else {
+ d->prg_cpu[i]->branchAddress =
+ cpu_to_le32((d->prg_bus[0] & 0xfffffff0));
+ }
+
+ d->prg_cpu[i]->address = cpu_to_le32(d->buf_bus[i]);
+ d->prg_cpu[i]->status = cpu_to_le32(d->buf_size);
+ }
+
+ d->buf_ind = 0;
+ d->buf_offset = 0;
+
+ if (d->type == DMA_CTX_ISO) {
+ /* Clear contextControl */
+ reg_write(ohci, d->ctrlClear, 0xffffffff);
+
+ /* Set bufferFill, isochHeader, multichannel for IR context */
+ reg_write(ohci, d->ctrlSet, 0xd0000000);
+
+ /* Set the context match register to match on all tags */
+ reg_write(ohci, d->ctxtMatch, 0xf0000000);
+
+ /* Clear the multi channel mask high and low registers */
+ reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
+
+ /* Set up isoRecvIntMask to generate interrupts */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx);
+ }
+
+ /* Tell the controller where the first AR program is */
+ reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1);
+
+ /* Run context */
+ reg_write(ohci, d->ctrlSet, 0x00008000);
+
+ DBGMSG("Receive DMA ctx=%d initialized", d->ctx);
+}
+
+/* Initialize the dma transmit context */
+static void initialize_dma_trm_ctx(struct dma_trm_ctx *d)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+
+ /* Stop the context */
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ d->prg_ind = 0;
+ d->sent_ind = 0;
+ d->free_prgs = d->num_desc;
+ d->branchAddrPtr = NULL;
+ INIT_LIST_HEAD(&d->fifo_list);
+ INIT_LIST_HEAD(&d->pending_list);
+
+ if (d->type == DMA_CTX_ISO) {
+ /* enable interrupts */
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx);
+ }
+
+ DBGMSG("Transmit DMA ctx=%d initialized", d->ctx);
+}
+
+/* Count the number of available iso contexts */
+static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
+{
+ int i,ctx=0;
+ u32 tmp;
+
+ reg_write(ohci, reg, 0xffffffff);
+ tmp = reg_read(ohci, reg);
+
+ DBGMSG("Iso contexts reg: %08x implemented: %08x", reg, tmp);
+
+ /* Count the number of contexts */
+ for (i=0; i<32; i++) {
+ if (tmp & 1) ctx++;
+ tmp >>= 1;
+ }
+ return ctx;
+}
+
+/* Global initialization */
+static void ohci_initialize(struct ti_ohci *ohci)
+{
+ quadlet_t buf;
+ int num_ports, i;
+
+ spin_lock_init(&ohci->phy_reg_lock);
+
+ /* Put some defaults to these undefined bus options */
+ buf = reg_read(ohci, OHCI1394_BusOptions);
+ buf |= 0x60000000; /* Enable CMC and ISC */
+ if (hpsb_disable_irm)
+ buf &= ~0x80000000;
+ else
+ buf |= 0x80000000; /* Enable IRMC */
+ buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */
+ buf &= ~0x18000000; /* Disable PMC and BMC */
+ reg_write(ohci, OHCI1394_BusOptions, buf);
+
+ /* Set the bus number */
+ reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
+
+ /* Enable posted writes */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_postedWriteEnable);
+
+ /* Clear link control register */
+ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
+
+ /* Enable cycle timer and cycle master and set the IRM
+ * contender bit in our self ID packets if appropriate. */
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_CycleTimerEnable |
+ OHCI1394_LinkControl_CycleMaster);
+ i = get_phy_reg(ohci, 4) | PHY_04_LCTRL;
+ if (hpsb_disable_irm)
+ i &= ~PHY_04_CONTENDER;
+ else
+ i |= PHY_04_CONTENDER;
+ set_phy_reg(ohci, 4, i);
+
+ /* Set up self-id dma buffer */
+ reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus);
+
+ /* enable self-id */
+ reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID);
+
+ /* Set the Config ROM mapping register */
+ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus);
+
+ /* Now get our max packet size */
+ ohci->max_packet_size =
+ 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1);
+
+ /* Clear the interrupt mask */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
+
+ /* Clear the interrupt mask */
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
+
+ /* Initialize AR dma */
+ initialize_dma_rcv_ctx(&ohci->ar_req_context, 0);
+ initialize_dma_rcv_ctx(&ohci->ar_resp_context, 0);
+
+ /* Initialize AT dma */
+ initialize_dma_trm_ctx(&ohci->at_req_context);
+ initialize_dma_trm_ctx(&ohci->at_resp_context);
+
+ /* Accept AR requests from all nodes */
+ reg_write(ohci, OHCI1394_AsReqFilterHiSet, 0x80000000);
+
+ /* Set the address range of the physical response unit.
+ * Most controllers do not implement it as a writable register though.
+ * They will keep a hardwired offset of 0x00010000 and show 0x0 as
+ * register content.
+ * To actually enable physical responses is the job of our interrupt
+ * handler which programs the physical request filter. */
+ reg_write(ohci, OHCI1394_PhyUpperBound,
+ OHCI1394_PHYS_UPPER_BOUND_PROGRAMMED >> 16);
+
+ DBGMSG("physUpperBoundOffset=%08x",
+ reg_read(ohci, OHCI1394_PhyUpperBound));
+
+ /* Specify AT retries */
+ reg_write(ohci, OHCI1394_ATRetries,
+ OHCI1394_MAX_AT_REQ_RETRIES |
+ (OHCI1394_MAX_AT_RESP_RETRIES<<4) |
+ (OHCI1394_MAX_PHYS_RESP_RETRIES<<8));
+
+ /* We don't want hardware swapping */
+ reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap);
+
+ /* Enable interrupts */
+ reg_write(ohci, OHCI1394_IntMaskSet,
+ OHCI1394_unrecoverableError |
+ OHCI1394_masterIntEnable |
+ OHCI1394_busReset |
+ OHCI1394_selfIDComplete |
+ OHCI1394_RSPkt |
+ OHCI1394_RQPkt |
+ OHCI1394_respTxComplete |
+ OHCI1394_reqTxComplete |
+ OHCI1394_isochRx |
+ OHCI1394_isochTx |
+ OHCI1394_postedWriteErr |
+ OHCI1394_cycleTooLong |
+ OHCI1394_cycleInconsistent);
+
+ /* Enable link */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
+
+ buf = reg_read(ohci, OHCI1394_Version);
+ PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%d] "
+ "MMIO=[%llx-%llx] Max Packet=[%d] IR/IT contexts=[%d/%d]",
+ ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
+ ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
+ (unsigned long long)pci_resource_start(ohci->dev, 0),
+ (unsigned long long)pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
+ ohci->max_packet_size,
+ ohci->nb_iso_rcv_ctx, ohci->nb_iso_xmit_ctx);
+
+ /* Check all of our ports to make sure that if anything is
+ * connected, we enable that port. */
+ num_ports = get_phy_reg(ohci, 2) & 0xf;
+ for (i = 0; i < num_ports; i++) {
+ unsigned int status;
+
+ set_phy_reg(ohci, 7, i);
+ status = get_phy_reg(ohci, 8);
+
+ if (status & 0x20)
+ set_phy_reg(ohci, 8, status & ~1);
+ }
+
+ /* Serial EEPROM Sanity check. */
+ if ((ohci->max_packet_size < 512) ||
+ (ohci->max_packet_size > 4096)) {
+ /* Serial EEPROM contents are suspect, set a sane max packet
+ * size and print the raw contents for bug reports if verbose
+ * debug is enabled. */
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ int i;
+#endif
+
+ PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, "
+ "attempting to set max_packet_size to 512 bytes");
+ reg_write(ohci, OHCI1394_BusOptions,
+ (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002);
+ ohci->max_packet_size = 512;
+#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
+ PRINT(KERN_DEBUG, " EEPROM Present: %d",
+ (reg_read(ohci, OHCI1394_Version) >> 24) & 0x1);
+ reg_write(ohci, OHCI1394_GUID_ROM, 0x80000000);
+
+ for (i = 0;
+ ((i < 1000) &&
+ (reg_read(ohci, OHCI1394_GUID_ROM) & 0x80000000)); i++)
+ udelay(10);
+
+ for (i = 0; i < 0x20; i++) {
+ reg_write(ohci, OHCI1394_GUID_ROM, 0x02000000);
+ PRINT(KERN_DEBUG, " EEPROM %02x: %02x", i,
+ (reg_read(ohci, OHCI1394_GUID_ROM) >> 16) & 0xff);
+ }
+#endif
+ }
+}
+
+/*
+ * Insert a packet in the DMA fifo and generate the DMA prg
+ * FIXME: rewrite the program in order to accept packets crossing
+ * page boundaries.
+ * check also that a single dma descriptor doesn't cross a
+ * page boundary.
+ */
+static void insert_packet(struct ti_ohci *ohci,
+ struct dma_trm_ctx *d, struct hpsb_packet *packet)
+{
+ u32 cycleTimer;
+ int idx = d->prg_ind;
+
+ DBGMSG("Inserting packet for node " NODE_BUS_FMT
+ ", tlabel=%d, tcode=0x%x, speed=%d",
+ NODE_BUS_ARGS(ohci->host, packet->node_id), packet->tlabel,
+ packet->tcode, packet->speed_code);
+
+ d->prg_cpu[idx]->begin.address = 0;
+ d->prg_cpu[idx]->begin.branchAddress = 0;
+
+ if (d->type == DMA_CTX_ASYNC_RESP) {
+ /*
+ * For response packets, we need to put a timeout value in
+ * the 16 lower bits of the status... let's try 1 sec timeout
+ */
+ cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+ d->prg_cpu[idx]->begin.status = cpu_to_le32(
+ (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) |
+ ((cycleTimer&0x01fff000)>>12));
+
+ DBGMSG("cycleTimer: %08x timeStamp: %08x",
+ cycleTimer, d->prg_cpu[idx]->begin.status);
+ } else
+ d->prg_cpu[idx]->begin.status = 0;
+
+ if ( (packet->type == hpsb_async) || (packet->type == hpsb_raw) ) {
+
+ if (packet->type == hpsb_raw) {
+ d->prg_cpu[idx]->data[0] = cpu_to_le32(OHCI1394_TCODE_PHY<<4);
+ d->prg_cpu[idx]->data[1] = cpu_to_le32(packet->header[0]);
+ d->prg_cpu[idx]->data[2] = cpu_to_le32(packet->header[1]);
+ } else {
+ d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
+ (packet->header[0] & 0xFFFF);
+
+ if (packet->tcode == TCODE_ISO_DATA) {
+ /* Sending an async stream packet */
+ d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
+ } else {
+ /* Sending a normal async request or response */
+ d->prg_cpu[idx]->data[1] =
+ (packet->header[1] & 0xFFFF) |
+ (packet->header[0] & 0xFFFF0000);
+ d->prg_cpu[idx]->data[2] = packet->header[2];
+ d->prg_cpu[idx]->data[3] = packet->header[3];
+ }
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
+ }
+
+ if (packet->data_size) { /* block transmit */
+ if (packet->tcode == TCODE_STREAM_DATA){
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+ DMA_CTL_IMMEDIATE | 0x8);
+ } else {
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+ DMA_CTL_IMMEDIATE | 0x10);
+ }
+ d->prg_cpu[idx]->end.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ packet->data_size);
+ /*
+ * Check that the packet data buffer
+ * does not cross a page boundary.
+ *
+ * XXX Fix this some day. eth1394 seems to trigger
+ * it, but ignoring it doesn't seem to cause a
+ * problem.
+ */
+#if 0
+ if (cross_bound((unsigned long)packet->data,
+ packet->data_size)>0) {
+ /* FIXME: do something about it */
+ PRINT(KERN_ERR,
+ "%s: packet data addr: %p size %Zd bytes "
+ "cross page boundary", __func__,
+ packet->data, packet->data_size);
+ }
+#endif
+ d->prg_cpu[idx]->end.address = cpu_to_le32(
+ pci_map_single(ohci->dev, packet->data,
+ packet->data_size,
+ PCI_DMA_TODEVICE));
+
+ d->prg_cpu[idx]->end.branchAddress = 0;
+ d->prg_cpu[idx]->end.status = 0;
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) =
+ cpu_to_le32(d->prg_bus[idx] | 0x3);
+ d->branchAddrPtr =
+ &(d->prg_cpu[idx]->end.branchAddress);
+ } else { /* quadlet transmit */
+ if (packet->type == hpsb_raw)
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_IMMEDIATE |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ (packet->header_size + 4));
+ else
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_IMMEDIATE |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ packet->header_size);
+
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) =
+ cpu_to_le32(d->prg_bus[idx] | 0x2);
+ d->branchAddrPtr =
+ &(d->prg_cpu[idx]->begin.branchAddress);
+ }
+
+ } else { /* iso packet */
+ d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
+ (packet->header[0] & 0xFFFF);
+ d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
+
+ d->prg_cpu[idx]->begin.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_MORE |
+ DMA_CTL_IMMEDIATE | 0x8);
+ d->prg_cpu[idx]->end.control =
+ cpu_to_le32(DMA_CTL_OUTPUT_LAST |
+ DMA_CTL_UPDATE |
+ DMA_CTL_IRQ |
+ DMA_CTL_BRANCH |
+ packet->data_size);
+ d->prg_cpu[idx]->end.address = cpu_to_le32(
+ pci_map_single(ohci->dev, packet->data,
+ packet->data_size, PCI_DMA_TODEVICE));
+
+ d->prg_cpu[idx]->end.branchAddress = 0;
+ d->prg_cpu[idx]->end.status = 0;
+ DBGMSG("Iso xmit context info: header[%08x %08x]\n"
+ " begin=%08x %08x %08x %08x\n"
+ " %08x %08x %08x %08x\n"
+ " end =%08x %08x %08x %08x",
+ d->prg_cpu[idx]->data[0], d->prg_cpu[idx]->data[1],
+ d->prg_cpu[idx]->begin.control,
+ d->prg_cpu[idx]->begin.address,
+ d->prg_cpu[idx]->begin.branchAddress,
+ d->prg_cpu[idx]->begin.status,
+ d->prg_cpu[idx]->data[0],
+ d->prg_cpu[idx]->data[1],
+ d->prg_cpu[idx]->data[2],
+ d->prg_cpu[idx]->data[3],
+ d->prg_cpu[idx]->end.control,
+ d->prg_cpu[idx]->end.address,
+ d->prg_cpu[idx]->end.branchAddress,
+ d->prg_cpu[idx]->end.status);
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) = cpu_to_le32(d->prg_bus[idx] | 0x3);
+ d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress);
+ }
+ d->free_prgs--;
+
+ /* queue the packet in the appropriate context queue */
+ list_add_tail(&packet->driver_list, &d->fifo_list);
+ d->prg_ind = (d->prg_ind + 1) % d->num_desc;
+}
+
+/*
+ * This function fills the FIFO with the (eventual) pending packets
+ * and runs or wakes up the DMA prg if necessary.
+ *
+ * The function MUST be called with the d->lock held.
+ */
+static void dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d)
+{
+ struct hpsb_packet *packet, *ptmp;
+ int idx = d->prg_ind;
+ int z = 0;
+
+ /* insert the packets into the dma fifo */
+ list_for_each_entry_safe(packet, ptmp, &d->pending_list, driver_list) {
+ if (!d->free_prgs)
+ break;
+
+ /* For the first packet only */
+ if (!z)
+ z = (packet->data_size) ? 3 : 2;
+
+ /* Insert the packet */
+ list_del_init(&packet->driver_list);
+ insert_packet(ohci, d, packet);
+ }
+
+ /* Nothing must have been done, either no free_prgs or no packets */
+ if (z == 0)
+ return;
+
+ /* Is the context running ? (should be unless it is
+ the first packet to be sent in this context) */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) {
+ u32 nodeId = reg_read(ohci, OHCI1394_NodeID);
+
+ DBGMSG("Starting transmit DMA ctx=%d",d->ctx);
+ reg_write(ohci, d->cmdPtr, d->prg_bus[idx] | z);
+
+ /* Check that the node id is valid, and not 63 */
+ if (!(nodeId & 0x80000000) || (nodeId & 0x3f) == 63)
+ PRINT(KERN_ERR, "Running dma failed because Node ID is not valid");
+ else
+ reg_write(ohci, d->ctrlSet, 0x8000);
+ } else {
+ /* Wake up the dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400))
+ DBGMSG("Waking transmit DMA ctx=%d",d->ctx);
+
+ /* do this always, to avoid race condition */
+ reg_write(ohci, d->ctrlSet, 0x1000);
+ }
+
+ return;
+}
+
+/* Transmission of an async or iso packet */
+static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
+{
+ struct ti_ohci *ohci = host->hostdata;
+ struct dma_trm_ctx *d;
+ unsigned long flags;
+
+ if (packet->data_size > ohci->max_packet_size) {
+ PRINT(KERN_ERR,
+ "Transmit packet size %Zd is too big",
+ packet->data_size);
+ return -EOVERFLOW;
+ }
+
+ if (packet->type == hpsb_raw)
+ d = &ohci->at_req_context;
+ else if ((packet->tcode & 0x02) && (packet->tcode != TCODE_ISO_DATA))
+ d = &ohci->at_resp_context;
+ else
+ d = &ohci->at_req_context;
+
+ spin_lock_irqsave(&d->lock,flags);
+
+ list_add_tail(&packet->driver_list, &d->pending_list);
+
+ dma_trm_flush(ohci, d);
+
+ spin_unlock_irqrestore(&d->lock,flags);
+
+ return 0;
+}
+
+static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg)
+{
+ struct ti_ohci *ohci = host->hostdata;
+ int retval = 0, phy_reg;
+
+ switch (cmd) {
+ case RESET_BUS:
+ switch (arg) {
+ case SHORT_RESET:
+ phy_reg = get_phy_reg(ohci, 5);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
+ break;
+ case LONG_RESET:
+ phy_reg = get_phy_reg(ohci, 1);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 1, phy_reg); /* set IBR */
+ break;
+ case SHORT_RESET_NO_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ if (phy_reg & 0x80) {
+ phy_reg &= ~0x80;
+ set_phy_reg(ohci, 1, phy_reg); /* clear RHB */
+ }
+
+ phy_reg = get_phy_reg(ohci, 5);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
+ break;
+ case LONG_RESET_NO_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ phy_reg &= ~0x80;
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 1, phy_reg); /* clear RHB, set IBR */
+ break;
+ case SHORT_RESET_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ if (!(phy_reg & 0x80)) {
+ phy_reg |= 0x80;
+ set_phy_reg(ohci, 1, phy_reg); /* set RHB */
+ }
+
+ phy_reg = get_phy_reg(ohci, 5);
+ phy_reg |= 0x40;
+ set_phy_reg(ohci, 5, phy_reg); /* set ISBR */
+ break;
+ case LONG_RESET_FORCE_ROOT:
+ phy_reg = get_phy_reg(ohci, 1);
+ phy_reg |= 0xc0;
+ set_phy_reg(ohci, 1, phy_reg); /* set RHB and IBR */
+ break;
+ default:
+ retval = -1;
+ }
+ break;
+
+ case GET_CYCLE_COUNTER:
+ retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+ break;
+
+ case SET_CYCLE_COUNTER:
+ reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg);
+ break;
+
+ case SET_BUS_ID:
+ PRINT(KERN_ERR, "devctl command SET_BUS_ID err");
+ break;
+
+ case ACT_CYCLE_MASTER:
+ if (arg) {
+ /* check if we are root and other nodes are present */
+ u32 nodeId = reg_read(ohci, OHCI1394_NodeID);
+ if ((nodeId & (1<<30)) && (nodeId & 0x3f)) {
+ /*
+ * enable cycleTimer, cycleMaster
+ */
+ DBGMSG("Cycle master enabled");
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_CycleTimerEnable |
+ OHCI1394_LinkControl_CycleMaster);
+ }
+ } else {
+ /* disable cycleTimer, cycleMaster, cycleSource */
+ reg_write(ohci, OHCI1394_LinkControlClear,
+ OHCI1394_LinkControl_CycleTimerEnable |
+ OHCI1394_LinkControl_CycleMaster |
+ OHCI1394_LinkControl_CycleSource);
+ }
+ break;
+
+ case CANCEL_REQUESTS:
+ DBGMSG("Cancel request received");
+ dma_trm_reset(&ohci->at_req_context);
+ dma_trm_reset(&ohci->at_resp_context);
+ break;
+
+ default:
+ PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet",
+ cmd);
+ break;
+ }
+ return retval;
+}
+
+/***********************************
+ * rawiso ISO reception *
+ ***********************************/
+
+/*
+ We use either buffer-fill or packet-per-buffer DMA mode. The DMA
+ buffer is split into "blocks" (regions described by one DMA
+ descriptor). Each block must be one page or less in size, and
+ must not cross a page boundary.
+
+ There is one little wrinkle with buffer-fill mode: a packet that
+ starts in the final block may wrap around into the first block. But
+ the user API expects all packets to be contiguous. Our solution is
+ to keep the very last page of the DMA buffer in reserve - if a
+ packet spans the gap, we copy its tail into this page.
+*/
+
+struct ohci_iso_recv {
+ struct ti_ohci *ohci;
+
+ struct ohci1394_iso_tasklet task;
+ int task_active;
+
+ enum { BUFFER_FILL_MODE = 0,
+ PACKET_PER_BUFFER_MODE = 1 } dma_mode;
+
+ /* memory and PCI mapping for the DMA descriptors */
+ struct dma_prog_region prog;
+ struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */
+
+ /* how many DMA blocks fit in the buffer */
+ unsigned int nblocks;
+
+ /* stride of DMA blocks */
+ unsigned int buf_stride;
+
+ /* number of blocks to batch between interrupts */
+ int block_irq_interval;
+
+ /* block that DMA will finish next */
+ int block_dma;
+
+ /* (buffer-fill only) block that the reader will release next */
+ int block_reader;
+
+ /* (buffer-fill only) bytes of buffer the reader has released,
+ less than one block */
+ int released_bytes;
+
+ /* (buffer-fill only) buffer offset at which the next packet will appear */
+ int dma_offset;
+
+ /* OHCI DMA context control registers */
+ u32 ContextControlSet;
+ u32 ContextControlClear;
+ u32 CommandPtr;
+ u32 ContextMatch;
+};
+
+static void ohci_iso_recv_task(unsigned long data);
+static void ohci_iso_recv_stop(struct hpsb_iso *iso);
+static void ohci_iso_recv_shutdown(struct hpsb_iso *iso);
+static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync);
+static void ohci_iso_recv_program(struct hpsb_iso *iso);
+
+static int ohci_iso_recv_init(struct hpsb_iso *iso)
+{
+ struct ti_ohci *ohci = iso->host->hostdata;
+ struct ohci_iso_recv *recv;
+ int ctx;
+ int ret = -ENOMEM;
+
+ recv = kmalloc(sizeof(*recv), GFP_KERNEL);
+ if (!recv)
+ return -ENOMEM;
+
+ iso->hostdata = recv;
+ recv->ohci = ohci;
+ recv->task_active = 0;
+ dma_prog_region_init(&recv->prog);
+ recv->block = NULL;
+
+ /* use buffer-fill mode, unless irq_interval is 1
+ (note: multichannel requires buffer-fill) */
+
+ if (((iso->irq_interval == 1 && iso->dma_mode == HPSB_ISO_DMA_OLD_ABI) ||
+ iso->dma_mode == HPSB_ISO_DMA_PACKET_PER_BUFFER) && iso->channel != -1) {
+ recv->dma_mode = PACKET_PER_BUFFER_MODE;
+ } else {
+ recv->dma_mode = BUFFER_FILL_MODE;
+ }
+
+ /* set nblocks, buf_stride, block_irq_interval */
+
+ if (recv->dma_mode == BUFFER_FILL_MODE) {
+ recv->buf_stride = PAGE_SIZE;
+
+ /* one block per page of data in the DMA buffer, minus the final guard page */
+ recv->nblocks = iso->buf_size/PAGE_SIZE - 1;
+ if (recv->nblocks < 3) {
+ DBGMSG("ohci_iso_recv_init: DMA buffer too small");
+ goto err;
+ }
+
+ /* iso->irq_interval is in packets - translate that to blocks */
+ if (iso->irq_interval == 1)
+ recv->block_irq_interval = 1;
+ else
+ recv->block_irq_interval = iso->irq_interval *
+ ((recv->nblocks+1)/iso->buf_packets);
+ if (recv->block_irq_interval*4 > recv->nblocks)
+ recv->block_irq_interval = recv->nblocks/4;
+ if (recv->block_irq_interval < 1)
+ recv->block_irq_interval = 1;
+
+ } else {
+ int max_packet_size;
+
+ recv->nblocks = iso->buf_packets;
+ recv->block_irq_interval = iso->irq_interval;
+ if (recv->block_irq_interval * 4 > iso->buf_packets)
+ recv->block_irq_interval = iso->buf_packets / 4;
+ if (recv->block_irq_interval < 1)
+ recv->block_irq_interval = 1;
+
+ /* choose a buffer stride */
+ /* must be a power of 2, and <= PAGE_SIZE */
+
+ max_packet_size = iso->buf_size / iso->buf_packets;
+
+ for (recv->buf_stride = 8; recv->buf_stride < max_packet_size;
+ recv->buf_stride *= 2);
+
+ if (recv->buf_stride*iso->buf_packets > iso->buf_size ||
+ recv->buf_stride > PAGE_SIZE) {
+ /* this shouldn't happen, but anyway... */
+ DBGMSG("ohci_iso_recv_init: problem choosing a buffer stride");
+ goto err;
+ }
+ }
+
+ recv->block_reader = 0;
+ recv->released_bytes = 0;
+ recv->block_dma = 0;
+ recv->dma_offset = 0;
+
+ /* size of DMA program = one descriptor per block */
+ if (dma_prog_region_alloc(&recv->prog,
+ sizeof(struct dma_cmd) * recv->nblocks,
+ recv->ohci->dev))
+ goto err;
+
+ recv->block = (struct dma_cmd*) recv->prog.kvirt;
+
+ ohci1394_init_iso_tasklet(&recv->task,
+ iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE :
+ OHCI_ISO_RECEIVE,
+ ohci_iso_recv_task, (unsigned long) iso);
+
+ if (ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ recv->task_active = 1;
+
+ /* recv context registers are spaced 32 bytes apart */
+ ctx = recv->task.context;
+ recv->ContextControlSet = OHCI1394_IsoRcvContextControlSet + 32 * ctx;
+ recv->ContextControlClear = OHCI1394_IsoRcvContextControlClear + 32 * ctx;
+ recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx;
+ recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx;
+
+ if (iso->channel == -1) {
+ /* clear multi-channel selection mask */
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF);
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF);
+ }
+
+ /* write the DMA program */
+ ohci_iso_recv_program(iso);
+
+ DBGMSG("ohci_iso_recv_init: %s mode, DMA buffer is %lu pages"
+ " (%u bytes), using %u blocks, buf_stride %u, block_irq_interval %d",
+ recv->dma_mode == BUFFER_FILL_MODE ?
+ "buffer-fill" : "packet-per-buffer",
+ iso->buf_size/PAGE_SIZE, iso->buf_size,
+ recv->nblocks, recv->buf_stride, recv->block_irq_interval);
+
+ return 0;
+
+err:
+ ohci_iso_recv_shutdown(iso);
+ return ret;
+}
+
+static void ohci_iso_recv_stop(struct hpsb_iso *iso)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+
+ /* disable interrupts */
+ reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context);
+
+ /* halt DMA */
+ ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL);
+}
+
+static void ohci_iso_recv_shutdown(struct hpsb_iso *iso)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+
+ if (recv->task_active) {
+ ohci_iso_recv_stop(iso);
+ ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task);
+ recv->task_active = 0;
+ }
+
+ dma_prog_region_free(&recv->prog);
+ kfree(recv);
+ iso->hostdata = NULL;
+}
+
+/* set up a "gapped" ring buffer DMA program */
+static void ohci_iso_recv_program(struct hpsb_iso *iso)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ int blk;
+
+ /* address of 'branch' field in previous DMA descriptor */
+ u32 *prev_branch = NULL;
+
+ for (blk = 0; blk < recv->nblocks; blk++) {
+ u32 control;
+
+ /* the DMA descriptor */
+ struct dma_cmd *cmd = &recv->block[blk];
+
+ /* offset of the DMA descriptor relative to the DMA prog buffer */
+ unsigned long prog_offset = blk * sizeof(struct dma_cmd);
+
+ /* offset of this packet's data within the DMA buffer */
+ unsigned long buf_offset = blk * recv->buf_stride;
+
+ if (recv->dma_mode == BUFFER_FILL_MODE) {
+ control = 2 << 28; /* INPUT_MORE */
+ } else {
+ control = 3 << 28; /* INPUT_LAST */
+ }
+
+ control |= 8 << 24; /* s = 1, update xferStatus and resCount */
+
+ /* interrupt on last block, and at intervals */
+ if (blk == recv->nblocks-1 || (blk % recv->block_irq_interval) == 0) {
+ control |= 3 << 20; /* want interrupt */
+ }
+
+ control |= 3 << 18; /* enable branch to address */
+ control |= recv->buf_stride;
+
+ cmd->control = cpu_to_le32(control);
+ cmd->address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, buf_offset));
+ cmd->branchAddress = 0; /* filled in on next loop */
+ cmd->status = cpu_to_le32(recv->buf_stride);
+
+ /* link the previous descriptor to this one */
+ if (prev_branch) {
+ *prev_branch = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog, prog_offset) | 1);
+ }
+
+ prev_branch = &cmd->branchAddress;
+ }
+
+ /* the final descriptor's branch address and Z should be left at 0 */
+}
+
+/* listen or unlisten to a specific channel (multi-channel mode only) */
+static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ int reg, i;
+
+ if (channel < 32) {
+ reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear;
+ i = channel;
+ } else {
+ reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear;
+ i = channel - 32;
+ }
+
+ reg_write(recv->ohci, reg, (1 << i));
+
+ /* issue a dummy read to force all PCI writes to be posted immediately */
+ mb();
+ reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ int i;
+
+ for (i = 0; i < 64; i++) {
+ if (mask & (1ULL << i)) {
+ if (i < 32)
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i));
+ else
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32)));
+ } else {
+ if (i < 32)
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i));
+ else
+ reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32)));
+ }
+ }
+
+ /* issue a dummy read to force all PCI writes to be posted immediately */
+ mb();
+ reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+}
+
+static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ struct ti_ohci *ohci = recv->ohci;
+ u32 command, contextMatch;
+
+ reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* always keep ISO headers */
+ command = (1 << 30);
+
+ if (recv->dma_mode == BUFFER_FILL_MODE)
+ command |= (1 << 31);
+
+ reg_write(recv->ohci, recv->ContextControlSet, command);
+
+ /* match on specified tags */
+ contextMatch = tag_mask << 28;
+
+ if (iso->channel == -1) {
+ /* enable multichannel reception */
+ reg_write(recv->ohci, recv->ContextControlSet, (1 << 28));
+ } else {
+ /* listen on channel */
+ contextMatch |= iso->channel;
+ }
+
+ if (cycle != -1) {
+ u32 seconds;
+
+ /* enable cycleMatch */
+ reg_write(recv->ohci, recv->ContextControlSet, (1 << 29));
+
+ /* set starting cycle */
+ cycle &= 0x1FFF;
+
+ /* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
+ just snarf them from the current time */
+ seconds = reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
+
+ /* advance one second to give some extra time for DMA to start */
+ seconds += 1;
+
+ cycle |= (seconds & 3) << 13;
+
+ contextMatch |= cycle << 12;
+ }
+
+ if (sync != -1) {
+ /* set sync flag on first DMA descriptor */
+ struct dma_cmd *cmd = &recv->block[recv->block_dma];
+ cmd->control |= cpu_to_le32(DMA_CTL_WAIT);
+
+ /* match sync field */
+ contextMatch |= (sync&0xf)<<8;
+ }
+
+ reg_write(recv->ohci, recv->ContextMatch, contextMatch);
+
+ /* address of first descriptor block */
+ command = dma_prog_region_offset_to_bus(&recv->prog,
+ recv->block_dma * sizeof(struct dma_cmd));
+ command |= 1; /* Z=1 */
+
+ reg_write(recv->ohci, recv->CommandPtr, command);
+
+ /* enable interrupts */
+ reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << recv->task.context);
+
+ wmb();
+
+ /* run */
+ reg_write(recv->ohci, recv->ContextControlSet, 0x8000);
+
+ /* issue a dummy read of the cycle timer register to force
+ all PCI writes to be posted immediately */
+ mb();
+ reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer);
+
+ /* check RUN */
+ if (!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) {
+ PRINT(KERN_ERR,
+ "Error starting IR DMA (ContextControl 0x%08x)\n",
+ reg_read(recv->ohci, recv->ContextControlSet));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ohci_iso_recv_release_block(struct ohci_iso_recv *recv, int block)
+{
+ /* re-use the DMA descriptor for the block */
+ /* by linking the previous descriptor to it */
+
+ int next_i = block;
+ int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1);
+
+ struct dma_cmd *next = &recv->block[next_i];
+ struct dma_cmd *prev = &recv->block[prev_i];
+
+ /* ignore out-of-range requests */
+ if ((block < 0) || (block > recv->nblocks))
+ return;
+
+ /* 'next' becomes the new end of the DMA chain,
+ so disable branch and enable interrupt */
+ next->branchAddress = 0;
+ next->control |= cpu_to_le32(3 << 20);
+ next->status = cpu_to_le32(recv->buf_stride);
+
+ /* link prev to next */
+ prev->branchAddress = cpu_to_le32(dma_prog_region_offset_to_bus(&recv->prog,
+ sizeof(struct dma_cmd) * next_i)
+ | 1); /* Z=1 */
+
+ /* disable interrupt on previous DMA descriptor, except at intervals */
+ if ((prev_i % recv->block_irq_interval) == 0) {
+ prev->control |= cpu_to_le32(3 << 20); /* enable interrupt */
+ } else {
+ prev->control &= cpu_to_le32(~(3<<20)); /* disable interrupt */
+ }
+ wmb();
+
+ /* wake up DMA in case it fell asleep */
+ reg_write(recv->ohci, recv->ContextControlSet, (1 << 12));
+}
+
+static void ohci_iso_recv_bufferfill_release(struct ohci_iso_recv *recv,
+ struct hpsb_iso_packet_info *info)
+{
+ /* release the memory where the packet was */
+ recv->released_bytes += info->total_len;
+
+ /* have we released enough memory for one block? */
+ while (recv->released_bytes > recv->buf_stride) {
+ ohci_iso_recv_release_block(recv, recv->block_reader);
+ recv->block_reader = (recv->block_reader + 1) % recv->nblocks;
+ recv->released_bytes -= recv->buf_stride;
+ }
+}
+
+static inline void ohci_iso_recv_release(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
+{
+ struct ohci_iso_recv *recv = iso->hostdata;
+ if (recv->dma_mode == BUFFER_FILL_MODE) {
+ ohci_iso_recv_bufferfill_release(recv, info);
+ } else {
+ ohci_iso_recv_release_block(recv, info - iso->infos);
+ }
+}
+
+/* parse all packets from blocks that have been fully received */
+static void ohci_iso_recv_bufferfill_parse(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
+{
+ int wake = 0;
+ int runaway = 0;
+ struct ti_ohci *ohci = recv->ohci;
+
+ while (1) {
+ /* we expect the next parsable packet to begin at recv->dma_offset */
+ /* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */
+
+ unsigned int offset;
+ unsigned short len, cycle, total_len;
+ unsigned char channel, tag, sy;
+
+ unsigned char *p = iso->data_buf.kvirt;
+
+ unsigned int this_block = recv->dma_offset/recv->buf_stride;
+
+ /* don't loop indefinitely */
+ if (runaway++ > 100000) {
+ atomic_inc(&iso->overflows);
+ PRINT(KERN_ERR,
+ "IR DMA error - Runaway during buffer parsing!\n");
+ break;
+ }
+
+ /* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */
+ if (this_block == recv->block_dma)
+ break;
+
+ wake = 1;
+
+ /* parse data length, tag, channel, and sy */
+
+ /* note: we keep our own local copies of 'len' and 'offset'
+ so the user can't mess with them by poking in the mmap area */
+
+ len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8);
+
+ if (len > 4096) {
+ PRINT(KERN_ERR,
+ "IR DMA error - bogus 'len' value %u\n", len);
+ }
+
+ channel = p[recv->dma_offset+1] & 0x3F;
+ tag = p[recv->dma_offset+1] >> 6;
+ sy = p[recv->dma_offset+0] & 0xF;
+
+ /* advance to data payload */
+ recv->dma_offset += 4;
+
+ /* check for wrap-around */
+ if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+ recv->dma_offset -= recv->buf_stride*recv->nblocks;
+ }
+
+ /* dma_offset now points to the first byte of the data payload */
+ offset = recv->dma_offset;
+
+ /* advance to xferStatus/timeStamp */
+ recv->dma_offset += len;
+
+ total_len = len + 8; /* 8 bytes header+trailer in OHCI packet */
+ /* payload is padded to 4 bytes */
+ if (len % 4) {
+ recv->dma_offset += 4 - (len%4);
+ total_len += 4 - (len%4);
+ }
+
+ /* check for wrap-around */
+ if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+ /* uh oh, the packet data wraps from the last
+ to the first DMA block - make the packet
+ contiguous by copying its "tail" into the
+ guard page */
+
+ int guard_off = recv->buf_stride*recv->nblocks;
+ int tail_len = len - (guard_off - offset);
+
+ if (tail_len > 0 && tail_len < recv->buf_stride) {
+ memcpy(iso->data_buf.kvirt + guard_off,
+ iso->data_buf.kvirt,
+ tail_len);
+ }
+
+ recv->dma_offset -= recv->buf_stride*recv->nblocks;
+ }
+
+ /* parse timestamp */
+ cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8);
+ cycle &= 0x1FFF;
+
+ /* advance to next packet */
+ recv->dma_offset += 4;
+
+ /* check for wrap-around */
+ if (recv->dma_offset >= recv->buf_stride*recv->nblocks) {
+ recv->dma_offset -= recv->buf_stride*recv->nblocks;
+ }
+
+ hpsb_iso_packet_received(iso, offset, len, total_len, cycle, channel, tag, sy);
+ }
+
+ if (wake)
+ hpsb_iso_wake(iso);
+}
+
+static void ohci_iso_recv_bufferfill_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
+{
+ int loop;
+ struct ti_ohci *ohci = recv->ohci;
+
+ /* loop over all blocks */
+ for (loop = 0; loop < recv->nblocks; loop++) {
+
+ /* check block_dma to see if it's done */
+ struct dma_cmd *im = &recv->block[recv->block_dma];
+
+ /* check the DMA descriptor for new writes to xferStatus */
+ u16 xferstatus = le32_to_cpu(im->status) >> 16;
+
+ /* rescount is the number of bytes *remaining to be written* in the block */
+ u16 rescount = le32_to_cpu(im->status) & 0xFFFF;
+
+ unsigned char event = xferstatus & 0x1F;
+
+ if (!event) {
+ /* nothing has happened to this block yet */
+ break;
+ }
+
+ if (event != 0x11) {
+ atomic_inc(&iso->overflows);
+ PRINT(KERN_ERR,
+ "IR DMA error - OHCI error code 0x%02x\n", event);
+ }
+
+ if (rescount != 0) {
+ /* the card is still writing to this block;
+ we can't touch it until it's done */
+ break;
+ }
+
+ /* OK, the block is finished... */
+
+ /* sync our view of the block */
+ dma_region_sync_for_cpu(&iso->data_buf, recv->block_dma*recv->buf_stride, recv->buf_stride);
+
+ /* reset the DMA descriptor */
+ im->status = recv->buf_stride;
+
+ /* advance block_dma */
+ recv->block_dma = (recv->block_dma + 1) % recv->nblocks;
+
+ if ((recv->block_dma+1) % recv->nblocks == recv->block_reader) {
+ atomic_inc(&iso->overflows);
+ DBGMSG("ISO reception overflow - "
+ "ran out of DMA blocks");
+ }
+ }
+
+ /* parse any packets that have arrived */
+ ohci_iso_recv_bufferfill_parse(iso, recv);
+}
+
+static void ohci_iso_recv_packetperbuf_task(struct hpsb_iso *iso, struct ohci_iso_recv *recv)
+{
+ int count;
+ int wake = 0;
+ struct ti_ohci *ohci = recv->ohci;
+
+ /* loop over the entire buffer */
+ for (count = 0; count < recv->nblocks; count++) {
+ u32 packet_len = 0;
+
+ /* pointer to the DMA descriptor */
+ struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + iso->pkt_dma;
+
+ /* check the DMA descriptor for new writes to xferStatus */
+ u16 xferstatus = le32_to_cpu(il->status) >> 16;
+ u16 rescount = le32_to_cpu(il->status) & 0xFFFF;
+
+ unsigned char event = xferstatus & 0x1F;
+
+ if (!event) {
+ /* this packet hasn't come in yet; we are done for now */
+ goto out;
+ }
+
+ if (event == 0x11) {
+ /* packet received successfully! */
+
+ /* rescount is the number of bytes *remaining* in the packet buffer,
+ after the packet was written */
+ packet_len = recv->buf_stride - rescount;
+
+ } else if (event == 0x02) {
+ PRINT(KERN_ERR, "IR DMA error - packet too long for buffer\n");
+ } else if (event) {
+ PRINT(KERN_ERR, "IR DMA error - OHCI error code 0x%02x\n", event);
+ }
+
+ /* sync our view of the buffer */
+ dma_region_sync_for_cpu(&iso->data_buf, iso->pkt_dma * recv->buf_stride, recv->buf_stride);
+
+ /* record the per-packet info */
+ {
+ /* iso header is 8 bytes ahead of the data payload */
+ unsigned char *hdr;
+
+ unsigned int offset;
+ unsigned short cycle;
+ unsigned char channel, tag, sy;
+
+ offset = iso->pkt_dma * recv->buf_stride;
+ hdr = iso->data_buf.kvirt + offset;
+
+ /* skip iso header */
+ offset += 8;
+ packet_len -= 8;
+
+ cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF;
+ channel = hdr[5] & 0x3F;
+ tag = hdr[5] >> 6;
+ sy = hdr[4] & 0xF;
+
+ hpsb_iso_packet_received(iso, offset, packet_len,
+ recv->buf_stride, cycle, channel, tag, sy);
+ }
+
+ /* reset the DMA descriptor */
+ il->status = recv->buf_stride;
+
+ wake = 1;
+ recv->block_dma = iso->pkt_dma;
+ }
+
+out:
+ if (wake)
+ hpsb_iso_wake(iso);
+}
+
+static void ohci_iso_recv_task(unsigned long data)
+{
+ struct hpsb_iso *iso = (struct hpsb_iso*) data;
+ struct ohci_iso_recv *recv = iso->hostdata;
+
+ if (recv->dma_mode == BUFFER_FILL_MODE)
+ ohci_iso_recv_bufferfill_task(iso, recv);
+ else
+ ohci_iso_recv_packetperbuf_task(iso, recv);
+}
+
+/***********************************
+ * rawiso ISO transmission *
+ ***********************************/
+
+struct ohci_iso_xmit {
+ struct ti_ohci *ohci;
+ struct dma_prog_region prog;
+ struct ohci1394_iso_tasklet task;
+ int task_active;
+ int last_cycle;
+ atomic_t skips;
+
+ u32 ContextControlSet;
+ u32 ContextControlClear;
+ u32 CommandPtr;
+};
+
+/* transmission DMA program:
+ one OUTPUT_MORE_IMMEDIATE for the IT header
+ one OUTPUT_LAST for the buffer data */
+
+struct iso_xmit_cmd {
+ struct dma_cmd output_more_immediate;
+ u8 iso_hdr[8];
+ u32 unused[2];
+ struct dma_cmd output_last;
+};
+
+static int ohci_iso_xmit_init(struct hpsb_iso *iso);
+static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle);
+static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso);
+static void ohci_iso_xmit_task(unsigned long data);
+
+static int ohci_iso_xmit_init(struct hpsb_iso *iso)
+{
+ struct ohci_iso_xmit *xmit;
+ unsigned int prog_size;
+ int ctx;
+ int ret = -ENOMEM;
+
+ xmit = kmalloc(sizeof(*xmit), GFP_KERNEL);
+ if (!xmit)
+ return -ENOMEM;
+
+ iso->hostdata = xmit;
+ xmit->ohci = iso->host->hostdata;
+ xmit->task_active = 0;
+ xmit->last_cycle = -1;
+ atomic_set(&iso->skips, 0);
+
+ dma_prog_region_init(&xmit->prog);
+
+ prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets;
+
+ if (dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev))
+ goto err;
+
+ ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT,
+ ohci_iso_xmit_task, (unsigned long) iso);
+
+ if (ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ xmit->task_active = 1;
+
+ /* xmit context registers are spaced 16 bytes apart */
+ ctx = xmit->task.context;
+ xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx;
+ xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx;
+ xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx;
+
+ return 0;
+
+err:
+ ohci_iso_xmit_shutdown(iso);
+ return ret;
+}
+
+static void ohci_iso_xmit_stop(struct hpsb_iso *iso)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+
+ /* disable interrupts */
+ reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context);
+
+ /* halt DMA */
+ if (ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) {
+ /* XXX the DMA context will lock up if you try to send too much data! */
+ PRINT(KERN_ERR,
+ "you probably exceeded the OHCI card's bandwidth limit - "
+ "reload the module and reduce xmit bandwidth");
+ }
+}
+
+static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+
+ if (xmit->task_active) {
+ ohci_iso_xmit_stop(iso);
+ ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task);
+ xmit->task_active = 0;
+ }
+
+ dma_prog_region_free(&xmit->prog);
+ kfree(xmit);
+ iso->hostdata = NULL;
+}
+
+static void ohci_iso_xmit_task(unsigned long data)
+{
+ struct hpsb_iso *iso = (struct hpsb_iso*) data;
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+ int wake = 0;
+ int count;
+
+ /* check the whole buffer if necessary, starting at pkt_dma */
+ for (count = 0; count < iso->buf_packets; count++) {
+ int cycle;
+
+ /* DMA descriptor */
+ struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma);
+
+ /* check for new writes to xferStatus */
+ u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16;
+ u8 event = xferstatus & 0x1F;
+
+ if (!event) {
+ /* packet hasn't been sent yet; we are done for now */
+ break;
+ }
+
+ if (event != 0x11)
+ PRINT(KERN_ERR,
+ "IT DMA error - OHCI error code 0x%02x\n", event);
+
+ /* at least one packet went out, so wake up the writer */
+ wake = 1;
+
+ /* parse cycle */
+ cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF;
+
+ if (xmit->last_cycle > -1) {
+ int cycle_diff = cycle - xmit->last_cycle;
+ int skip;
+
+ /* unwrap */
+ if (cycle_diff < 0) {
+ cycle_diff += 8000;
+ if (cycle_diff < 0)
+ PRINT(KERN_ERR, "bogus cycle diff %d\n",
+ cycle_diff);
+ }
+
+ skip = cycle_diff - 1;
+ if (skip > 0) {
+ DBGMSG("skipped %d cycles without packet loss", skip);
+ atomic_add(skip, &iso->skips);
+ }
+ }
+ xmit->last_cycle = cycle;
+
+ /* tell the subsystem the packet has gone out */
+ hpsb_iso_packet_sent(iso, cycle, event != 0x11);
+
+ /* reset the DMA descriptor for next time */
+ cmd->output_last.status = 0;
+ }
+
+ if (wake)
+ hpsb_iso_wake(iso);
+}
+
+static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+
+ int next_i, prev_i;
+ struct iso_xmit_cmd *next, *prev;
+
+ unsigned int offset;
+ unsigned short len;
+ unsigned char tag, sy;
+
+ /* check that the packet doesn't cross a page boundary
+ (we could allow this if we added OUTPUT_MORE descriptor support) */
+ if (cross_bound(info->offset, info->len)) {
+ PRINT(KERN_ERR,
+ "rawiso xmit: packet %u crosses a page boundary",
+ iso->first_packet);
+ return -EINVAL;
+ }
+
+ offset = info->offset;
+ len = info->len;
+ tag = info->tag;
+ sy = info->sy;
+
+ /* sync up the card's view of the buffer */
+ dma_region_sync_for_device(&iso->data_buf, offset, len);
+
+ /* append first_packet to the DMA chain */
+ /* by linking the previous descriptor to it */
+ /* (next will become the new end of the DMA chain) */
+
+ next_i = iso->first_packet;
+ prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1);
+
+ next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i);
+ prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i);
+
+ /* set up the OUTPUT_MORE_IMMEDIATE descriptor */
+ memset(next, 0, sizeof(struct iso_xmit_cmd));
+ next->output_more_immediate.control = cpu_to_le32(0x02000008);
+
+ /* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */
+
+ /* tcode = 0xA, and sy */
+ next->iso_hdr[0] = 0xA0 | (sy & 0xF);
+
+ /* tag and channel number */
+ next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F);
+
+ /* transmission speed */
+ next->iso_hdr[2] = iso->speed & 0x7;
+
+ /* payload size */
+ next->iso_hdr[6] = len & 0xFF;
+ next->iso_hdr[7] = len >> 8;
+
+ /* set up the OUTPUT_LAST */
+ next->output_last.control = cpu_to_le32(1 << 28);
+ next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */
+ next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */
+ next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */
+ next->output_last.control |= cpu_to_le32(len);
+
+ /* payload bus address */
+ next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset));
+
+ /* leave branchAddress at zero for now */
+
+ /* re-write the previous DMA descriptor to chain to this one */
+
+ /* set prev branch address to point to next (Z=3) */
+ prev->output_last.branchAddress = cpu_to_le32(
+ dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3);
+
+ /*
+ * Link the skip address to this descriptor itself. This causes a
+ * context to skip a cycle whenever lost cycles or FIFO overruns occur,
+ * without dropping the data at that point the application should then
+ * decide whether this is an error condition or not. Some protocols
+ * can deal with this by dropping some rate-matching padding packets.
+ */
+ next->output_more_immediate.branchAddress =
+ prev->output_last.branchAddress;
+
+ /* disable interrupt, unless required by the IRQ interval */
+ if (prev_i % iso->irq_interval) {
+ prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */
+ } else {
+ prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */
+ }
+
+ wmb();
+
+ /* wake DMA in case it is sleeping */
+ reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12);
+
+ /* issue a dummy read of the cycle timer to force all PCI
+ writes to be posted immediately */
+ mb();
+ reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer);
+
+ return 0;
+}
+
+static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle)
+{
+ struct ohci_iso_xmit *xmit = iso->hostdata;
+ struct ti_ohci *ohci = xmit->ohci;
+
+ /* clear out the control register */
+ reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF);
+ wmb();
+
+ /* address and length of first descriptor block (Z=3) */
+ reg_write(xmit->ohci, xmit->CommandPtr,
+ dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3);
+
+ /* cycle match */
+ if (cycle != -1) {
+ u32 start = cycle & 0x1FFF;
+
+ /* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
+ just snarf them from the current time */
+ u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
+
+ /* advance one second to give some extra time for DMA to start */
+ seconds += 1;
+
+ start |= (seconds & 3) << 13;
+
+ reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16));
+ }
+
+ /* enable interrupts */
+ reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context);
+
+ /* run */
+ reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000);
+ mb();
+
+ /* wait 100 usec to give the card time to go active */
+ udelay(100);
+
+ /* check the RUN bit */
+ if (!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) {
+ PRINT(KERN_ERR, "Error starting IT DMA (ContextControl 0x%08x)\n",
+ reg_read(xmit->ohci, xmit->ContextControlSet));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg)
+{
+
+ switch(cmd) {
+ case XMIT_INIT:
+ return ohci_iso_xmit_init(iso);
+ case XMIT_START:
+ return ohci_iso_xmit_start(iso, arg);
+ case XMIT_STOP:
+ ohci_iso_xmit_stop(iso);
+ return 0;
+ case XMIT_QUEUE:
+ return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg);
+ case XMIT_SHUTDOWN:
+ ohci_iso_xmit_shutdown(iso);
+ return 0;
+
+ case RECV_INIT:
+ return ohci_iso_recv_init(iso);
+ case RECV_START: {
+ int *args = (int*) arg;
+ return ohci_iso_recv_start(iso, args[0], args[1], args[2]);
+ }
+ case RECV_STOP:
+ ohci_iso_recv_stop(iso);
+ return 0;
+ case RECV_RELEASE:
+ ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg);
+ return 0;
+ case RECV_FLUSH:
+ ohci_iso_recv_task((unsigned long) iso);
+ return 0;
+ case RECV_SHUTDOWN:
+ ohci_iso_recv_shutdown(iso);
+ return 0;
+ case RECV_LISTEN_CHANNEL:
+ ohci_iso_recv_change_channel(iso, arg, 1);
+ return 0;
+ case RECV_UNLISTEN_CHANNEL:
+ ohci_iso_recv_change_channel(iso, arg, 0);
+ return 0;
+ case RECV_SET_CHANNEL_MASK:
+ ohci_iso_recv_set_channel_mask(iso, *((u64*) arg));
+ return 0;
+
+ default:
+ PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet",
+ cmd);
+ break;
+ }
+ return -EINVAL;
+}
+
+/***************************************
+ * IEEE-1394 functionality section END *
+ ***************************************/
+
+
+/********************************************************
+ * Global stuff (interrupt handler, init/shutdown code) *
+ ********************************************************/
+
+static void dma_trm_reset(struct dma_trm_ctx *d)
+{
+ unsigned long flags;
+ LIST_HEAD(packet_list);
+ struct ti_ohci *ohci = d->ohci;
+ struct hpsb_packet *packet, *ptmp;
+
+ ohci1394_stop_context(ohci, d->ctrlClear, NULL);
+
+ /* Lock the context, reset it and release it. Move the packets
+ * that were pending in the context to packet_list and free
+ * them after releasing the lock. */
+
+ spin_lock_irqsave(&d->lock, flags);
+
+ list_splice_init(&d->fifo_list, &packet_list);
+ list_splice_init(&d->pending_list, &packet_list);
+
+ d->branchAddrPtr = NULL;
+ d->sent_ind = d->prg_ind;
+ d->free_prgs = d->num_desc;
+
+ spin_unlock_irqrestore(&d->lock, flags);
+
+ if (list_empty(&packet_list))
+ return;
+
+ PRINT(KERN_INFO, "AT dma reset ctx=%d, aborting transmission", d->ctx);
+
+ /* Now process subsystem callbacks for the packets from this
+ * context. */
+ list_for_each_entry_safe(packet, ptmp, &packet_list, driver_list) {
+ list_del_init(&packet->driver_list);
+ hpsb_packet_sent(ohci->host, packet, ACKX_ABORTED);
+ }
+}
+
+static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci,
+ quadlet_t rx_event,
+ quadlet_t tx_event)
+{
+ struct ohci1394_iso_tasklet *t;
+ unsigned long mask;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
+
+ list_for_each_entry(t, &ohci->iso_tasklet_list, link) {
+ mask = 1 << t->context;
+
+ if (t->type == OHCI_ISO_TRANSMIT) {
+ if (tx_event & mask)
+ tasklet_schedule(&t->tasklet);
+ } else {
+ /* OHCI_ISO_RECEIVE or OHCI_ISO_MULTICHANNEL_RECEIVE */
+ if (rx_event & mask)
+ tasklet_schedule(&t->tasklet);
+ }
+ }
+
+ spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
+}
+
+static irqreturn_t ohci_irq_handler(int irq, void *dev_id)
+{
+ quadlet_t event, node_id;
+ struct ti_ohci *ohci = (struct ti_ohci *)dev_id;
+ struct hpsb_host *host = ohci->host;
+ int phyid = -1, isroot = 0;
+ unsigned long flags;
+
+ /* Read and clear the interrupt event register. Don't clear
+ * the busReset event, though. This is done when we get the
+ * selfIDComplete interrupt. */
+ spin_lock_irqsave(&ohci->event_lock, flags);
+ event = reg_read(ohci, OHCI1394_IntEventClear);
+ reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset);
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+
+ if (!event)
+ return IRQ_NONE;
+
+ /* If event is ~(u32)0 cardbus card was ejected. In this case
+ * we just return, and clean up in the ohci1394_pci_remove
+ * function. */
+ if (event == ~(u32) 0) {
+ DBGMSG("Device removed.");
+ return IRQ_NONE;
+ }
+
+ DBGMSG("IntEvent: %08x", event);
+
+ if (event & OHCI1394_unrecoverableError) {
+ int ctx;
+ PRINT(KERN_ERR, "Unrecoverable error!");
+
+ if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Req Tx Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsReqTrContextControlSet),
+ reg_read(ohci, OHCI1394_AsReqTrCommandPtr));
+
+ if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Rsp Tx Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsRspTrContextControlSet),
+ reg_read(ohci, OHCI1394_AsRspTrCommandPtr));
+
+ if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Req Rcv Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsReqRcvContextControlSet),
+ reg_read(ohci, OHCI1394_AsReqRcvCommandPtr));
+
+ if (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x800)
+ PRINT(KERN_ERR, "Async Rsp Rcv Context died: "
+ "ctrl[%08x] cmdptr[%08x]",
+ reg_read(ohci, OHCI1394_AsRspRcvContextControlSet),
+ reg_read(ohci, OHCI1394_AsRspRcvCommandPtr));
+
+ for (ctx = 0; ctx < ohci->nb_iso_xmit_ctx; ctx++) {
+ if (reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)) & 0x800)
+ PRINT(KERN_ERR, "Iso Xmit %d Context died: "
+ "ctrl[%08x] cmdptr[%08x]", ctx,
+ reg_read(ohci, OHCI1394_IsoXmitContextControlSet + (16 * ctx)),
+ reg_read(ohci, OHCI1394_IsoXmitCommandPtr + (16 * ctx)));
+ }
+
+ for (ctx = 0; ctx < ohci->nb_iso_rcv_ctx; ctx++) {
+ if (reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)) & 0x800)
+ PRINT(KERN_ERR, "Iso Recv %d Context died: "
+ "ctrl[%08x] cmdptr[%08x] match[%08x]", ctx,
+ reg_read(ohci, OHCI1394_IsoRcvContextControlSet + (32 * ctx)),
+ reg_read(ohci, OHCI1394_IsoRcvCommandPtr + (32 * ctx)),
+ reg_read(ohci, OHCI1394_IsoRcvContextMatch + (32 * ctx)));
+ }
+
+ event &= ~OHCI1394_unrecoverableError;
+ }
+ if (event & OHCI1394_postedWriteErr) {
+ PRINT(KERN_ERR, "physical posted write error");
+ /* no recovery strategy yet, had to involve protocol drivers */
+ event &= ~OHCI1394_postedWriteErr;
+ }
+ if (event & OHCI1394_cycleTooLong) {
+ if(printk_ratelimit())
+ PRINT(KERN_WARNING, "isochronous cycle too long");
+ else
+ DBGMSG("OHCI1394_cycleTooLong");
+ reg_write(ohci, OHCI1394_LinkControlSet,
+ OHCI1394_LinkControl_CycleMaster);
+ event &= ~OHCI1394_cycleTooLong;
+ }
+ if (event & OHCI1394_cycleInconsistent) {
+ /* We subscribe to the cycleInconsistent event only to
+ * clear the corresponding event bit... otherwise,
+ * isochronous cycleMatch DMA won't work. */
+ DBGMSG("OHCI1394_cycleInconsistent");
+ event &= ~OHCI1394_cycleInconsistent;
+ }
+ if (event & OHCI1394_busReset) {
+ /* The busReset event bit can't be cleared during the
+ * selfID phase, so we disable busReset interrupts, to
+ * avoid burying the cpu in interrupt requests. */
+ spin_lock_irqsave(&ohci->event_lock, flags);
+ reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
+
+ if (ohci->check_busreset) {
+ int loop_count = 0;
+
+ udelay(10);
+
+ while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
+ reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+ udelay(10);
+ spin_lock_irqsave(&ohci->event_lock, flags);
+
+ /* The loop counter check is to prevent the driver
+ * from remaining in this state forever. For the
+ * initial bus reset, the loop continues for ever
+ * and the system hangs, until some device is plugged-in
+ * or out manually into a port! The forced reset seems
+ * to solve this problem. This mainly effects nForce2. */
+ if (loop_count > 10000) {
+ ohci_devctl(host, RESET_BUS, LONG_RESET);
+ DBGMSG("Detected bus-reset loop. Forced a bus reset!");
+ loop_count = 0;
+ }
+
+ loop_count++;
+ }
+ }
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+ if (!host->in_bus_reset) {
+ DBGMSG("irq_handler: Bus reset requested");
+
+ /* Subsystem call */
+ hpsb_bus_reset(ohci->host);
+ }
+ event &= ~OHCI1394_busReset;
+ }
+ if (event & OHCI1394_reqTxComplete) {
+ struct dma_trm_ctx *d = &ohci->at_req_context;
+ DBGMSG("Got reqTxComplete interrupt "
+ "status=0x%08X", reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear,
+ "reqTxComplete");
+ else
+ dma_trm_tasklet((unsigned long)d);
+ //tasklet_schedule(&d->task);
+ event &= ~OHCI1394_reqTxComplete;
+ }
+ if (event & OHCI1394_respTxComplete) {
+ struct dma_trm_ctx *d = &ohci->at_resp_context;
+ DBGMSG("Got respTxComplete interrupt "
+ "status=0x%08X", reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear,
+ "respTxComplete");
+ else
+ tasklet_schedule(&d->task);
+ event &= ~OHCI1394_respTxComplete;
+ }
+ if (event & OHCI1394_RQPkt) {
+ struct dma_rcv_ctx *d = &ohci->ar_req_context;
+ DBGMSG("Got RQPkt interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt");
+ else
+ tasklet_schedule(&d->task);
+ event &= ~OHCI1394_RQPkt;
+ }
+ if (event & OHCI1394_RSPkt) {
+ struct dma_rcv_ctx *d = &ohci->ar_resp_context;
+ DBGMSG("Got RSPkt interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt");
+ else
+ tasklet_schedule(&d->task);
+ event &= ~OHCI1394_RSPkt;
+ }
+ if (event & OHCI1394_isochRx) {
+ quadlet_t rx_event;
+
+ rx_event = reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, rx_event);
+ ohci_schedule_iso_tasklets(ohci, rx_event, 0);
+ event &= ~OHCI1394_isochRx;
+ }
+ if (event & OHCI1394_isochTx) {
+ quadlet_t tx_event;
+
+ tx_event = reg_read(ohci, OHCI1394_IsoXmitIntEventSet);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, tx_event);
+ ohci_schedule_iso_tasklets(ohci, 0, tx_event);
+ event &= ~OHCI1394_isochTx;
+ }
+ if (event & OHCI1394_selfIDComplete) {
+ if (host->in_bus_reset) {
+ node_id = reg_read(ohci, OHCI1394_NodeID);
+
+ if (!(node_id & 0x80000000)) {
+ PRINT(KERN_ERR,
+ "SelfID received, but NodeID invalid "
+ "(probably new bus reset occurred): %08X",
+ node_id);
+ goto selfid_not_valid;
+ }
+
+ phyid = node_id & 0x0000003f;
+ isroot = (node_id & 0x40000000) != 0;
+
+ DBGMSG("SelfID interrupt received "
+ "(phyid %d, %s)", phyid,
+ (isroot ? "root" : "not root"));
+
+ handle_selfid(ohci, host, phyid, isroot);
+
+ /* Clear the bus reset event and re-enable the
+ * busReset interrupt. */
+ spin_lock_irqsave(&ohci->event_lock, flags);
+ reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+ reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_busReset);
+ spin_unlock_irqrestore(&ohci->event_lock, flags);
+
+ /* Turn on phys dma reception.
+ *
+ * TODO: Enable some sort of filtering management.
+ */
+ if (phys_dma) {
+ reg_write(ohci, OHCI1394_PhyReqFilterHiSet,
+ 0xffffffff);
+ reg_write(ohci, OHCI1394_PhyReqFilterLoSet,
+ 0xffffffff);
+ }
+
+ DBGMSG("PhyReqFilter=%08x%08x",
+ reg_read(ohci, OHCI1394_PhyReqFilterHiSet),
+ reg_read(ohci, OHCI1394_PhyReqFilterLoSet));
+
+ hpsb_selfid_complete(host, phyid, isroot);
+ } else
+ PRINT(KERN_ERR,
+ "SelfID received outside of bus reset sequence");
+
+selfid_not_valid:
+ event &= ~OHCI1394_selfIDComplete;
+ }
+
+ /* Make sure we handle everything, just in case we accidentally
+ * enabled an interrupt that we didn't write a handler for. */
+ if (event)
+ PRINT(KERN_ERR, "Unhandled interrupt(s) 0x%08x",
+ event);
+
+ return IRQ_HANDLED;
+}
+
+/* Put the buffer back into the dma context */
+static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ DBGMSG("Inserting dma buf ctx=%d idx=%d", d->ctx, idx);
+
+ d->prg_cpu[idx]->status = cpu_to_le32(d->buf_size);
+ d->prg_cpu[idx]->branchAddress &= le32_to_cpu(0xfffffff0);
+ idx = (idx + d->num_desc - 1 ) % d->num_desc;
+ d->prg_cpu[idx]->branchAddress |= le32_to_cpu(0x00000001);
+
+ /* To avoid a race, ensure 1394 interface hardware sees the inserted
+ * context program descriptors before it sees the wakeup bit set. */
+ wmb();
+
+ /* wake up the dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
+ PRINT(KERN_INFO,
+ "Waking dma ctx=%d ... processing is probably too slow",
+ d->ctx);
+ }
+
+ /* do this always, to avoid race condition */
+ reg_write(ohci, d->ctrlSet, 0x1000);
+}
+
+#define cond_le32_to_cpu(data, noswap) \
+ (noswap ? data : le32_to_cpu(data))
+
+static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0,
+ -1, 0, -1, 0, -1, -1, 16, -1};
+
+/*
+ * Determine the length of a packet in the buffer
+ * Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca>
+ */
+static inline int packet_length(struct dma_rcv_ctx *d, int idx,
+ quadlet_t *buf_ptr, int offset,
+ unsigned char tcode, int noswap)
+{
+ int length = -1;
+
+ if (d->type == DMA_CTX_ASYNC_REQ || d->type == DMA_CTX_ASYNC_RESP) {
+ length = TCODE_SIZE[tcode];
+ if (length == 0) {
+ if (offset + 12 >= d->buf_size) {
+ length = (cond_le32_to_cpu(d->buf_cpu[(idx + 1) % d->num_desc]
+ [3 - ((d->buf_size - offset) >> 2)], noswap) >> 16);
+ } else {
+ length = (cond_le32_to_cpu(buf_ptr[3], noswap) >> 16);
+ }
+ length += 20;
+ }
+ } else if (d->type == DMA_CTX_ISO) {
+ /* Assumption: buffer fill mode with header/trailer */
+ length = (cond_le32_to_cpu(buf_ptr[0], noswap) >> 16) + 8;
+ }
+
+ if (length > 0 && length % 4)
+ length += 4 - (length % 4);
+
+ return length;
+}
+
+/* Tasklet that processes dma receive buffers */
+static void dma_rcv_tasklet (unsigned long data)
+{
+ struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data;
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ unsigned int split_left, idx, offset, rescount;
+ unsigned char tcode;
+ int length, bytes_left, ack;
+ unsigned long flags;
+ quadlet_t *buf_ptr;
+ char *split_ptr;
+ char msg[256];
+
+ spin_lock_irqsave(&d->lock, flags);
+
+ idx = d->buf_ind;
+ offset = d->buf_offset;
+ buf_ptr = d->buf_cpu[idx] + offset/4;
+
+ rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
+ bytes_left = d->buf_size - rescount - offset;
+
+ while (bytes_left > 0) {
+ tcode = (cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming) >> 4) & 0xf;
+
+ /* packet_length() will return < 4 for an error */
+ length = packet_length(d, idx, buf_ptr, offset, tcode, ohci->no_swap_incoming);
+
+ if (length < 4) { /* something is wrong */
+ sprintf(msg,"Unexpected tcode 0x%x(0x%08x) in AR ctx=%d, length=%d",
+ tcode, cond_le32_to_cpu(buf_ptr[0], ohci->no_swap_incoming),
+ d->ctx, length);
+ ohci1394_stop_context(ohci, d->ctrlClear, msg);
+ spin_unlock_irqrestore(&d->lock, flags);
+ return;
+ }
+
+ /* The first case is where we have a packet that crosses
+ * over more than one descriptor. The next case is where
+ * it's all in the first descriptor. */
+ if ((offset + length) > d->buf_size) {
+ DBGMSG("Split packet rcv'd");
+ if (length > d->split_buf_size) {
+ ohci1394_stop_context(ohci, d->ctrlClear,
+ "Split packet size exceeded");
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+ spin_unlock_irqrestore(&d->lock, flags);
+ return;
+ }
+
+ if (le32_to_cpu(d->prg_cpu[(idx+1)%d->num_desc]->status)
+ == d->buf_size) {
+ /* Other part of packet not written yet.
+ * this should never happen I think
+ * anyway we'll get it on the next call. */
+ PRINT(KERN_INFO,
+ "Got only half a packet!");
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+ spin_unlock_irqrestore(&d->lock, flags);
+ return;
+ }
+
+ split_left = length;
+ split_ptr = (char *)d->spb;
+ memcpy(split_ptr,buf_ptr,d->buf_size-offset);
+ split_left -= d->buf_size-offset;
+ split_ptr += d->buf_size-offset;
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf_cpu[idx];
+ offset=0;
+
+ while (split_left >= d->buf_size) {
+ memcpy(split_ptr,buf_ptr,d->buf_size);
+ split_ptr += d->buf_size;
+ split_left -= d->buf_size;
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf_cpu[idx];
+ }
+
+ if (split_left > 0) {
+ memcpy(split_ptr, buf_ptr, split_left);
+ offset = split_left;
+ buf_ptr += offset/4;
+ }
+ } else {
+ DBGMSG("Single packet rcv'd");
+ memcpy(d->spb, buf_ptr, length);
+ offset += length;
+ buf_ptr += length/4;
+ if (offset==d->buf_size) {
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf_cpu[idx];
+ offset=0;
+ }
+ }
+
+ /* We get one phy packet to the async descriptor for each
+ * bus reset. We always ignore it. */
+ if (tcode != OHCI1394_TCODE_PHY) {
+ if (!ohci->no_swap_incoming)
+ header_le32_to_cpu(d->spb, tcode);
+ DBGMSG("Packet received from node"
+ " %d ack=0x%02X spd=%d tcode=0x%X"
+ " length=%d ctx=%d tlabel=%d",
+ (d->spb[1]>>16)&0x3f,
+ (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f,
+ (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3,
+ tcode, length, d->ctx,
+ (d->spb[0]>>10)&0x3f);
+
+ ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f)
+ == 0x11) ? 1 : 0;
+
+ hpsb_packet_received(ohci->host, d->spb,
+ length-4, ack);
+ }
+#ifdef OHCI1394_DEBUG
+ else
+ PRINT (KERN_DEBUG, "Got phy packet ctx=%d ... discarded",
+ d->ctx);
+#endif
+
+ rescount = le32_to_cpu(d->prg_cpu[idx]->status) & 0xffff;
+
+ bytes_left = d->buf_size - rescount - offset;
+
+ }
+
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+
+ spin_unlock_irqrestore(&d->lock, flags);
+}
+
+/* Bottom half that processes sent packets */
+static void dma_trm_tasklet (unsigned long data)
+{
+ struct dma_trm_ctx *d = (struct dma_trm_ctx*)data;
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ struct hpsb_packet *packet, *ptmp;
+ unsigned long flags;
+ u32 status, ack;
+ size_t datasize;
+
+ spin_lock_irqsave(&d->lock, flags);
+
+ list_for_each_entry_safe(packet, ptmp, &d->fifo_list, driver_list) {
+ datasize = packet->data_size;
+ if (datasize && packet->type != hpsb_raw)
+ status = le32_to_cpu(
+ d->prg_cpu[d->sent_ind]->end.status) >> 16;
+ else
+ status = le32_to_cpu(
+ d->prg_cpu[d->sent_ind]->begin.status) >> 16;
+
+ if (status == 0)
+ /* this packet hasn't been sent yet*/
+ break;
+
+#ifdef OHCI1394_DEBUG
+ if (datasize)
+ if (((le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf) == 0xa)
+ DBGMSG("Stream packet sent to channel %d tcode=0x%X "
+ "ack=0x%X spd=%d dataLength=%d ctx=%d",
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>8)&0x3f,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf,
+ status&0x1f, (status>>5)&0x3,
+ le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16,
+ d->ctx);
+ else
+ DBGMSG("Packet sent to node %d tcode=0x%X tLabel="
+ "%d ack=0x%X spd=%d dataLength=%d ctx=%d",
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])>>16)&0x3f,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>4)&0xf,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])>>10)&0x3f,
+ status&0x1f, (status>>5)&0x3,
+ le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3])>>16,
+ d->ctx);
+ else
+ DBGMSG("Packet sent to node %d tcode=0x%X tLabel="
+ "%d ack=0x%X spd=%d data=0x%08X ctx=%d",
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[1])
+ >>16)&0x3f,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])
+ >>4)&0xf,
+ (le32_to_cpu(d->prg_cpu[d->sent_ind]->data[0])
+ >>10)&0x3f,
+ status&0x1f, (status>>5)&0x3,
+ le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3]),
+ d->ctx);
+#endif
+
+ if (status & 0x10) {
+ ack = status & 0xf;
+ } else {
+ switch (status & 0x1f) {
+ case EVT_NO_STATUS: /* that should never happen */
+ case EVT_RESERVED_A: /* that should never happen */
+ case EVT_LONG_PACKET: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_MISSING_ACK:
+ ack = ACKX_TIMEOUT;
+ break;
+ case EVT_UNDERRUN:
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_OVERRUN: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_DESCRIPTOR_READ:
+ case EVT_DATA_READ:
+ case EVT_DATA_WRITE:
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_BUS_RESET: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_TIMEOUT:
+ ack = ACKX_TIMEOUT;
+ break;
+ case EVT_TCODE_ERR:
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_RESERVED_B: /* that should never happen */
+ case EVT_RESERVED_C: /* that should never happen */
+ PRINT(KERN_WARNING, "Received OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ break;
+ case EVT_UNKNOWN:
+ case EVT_FLUSHED:
+ ack = ACKX_SEND_ERROR;
+ break;
+ default:
+ PRINT(KERN_ERR, "Unhandled OHCI evt_* error 0x%x", status & 0x1f);
+ ack = ACKX_SEND_ERROR;
+ BUG();
+ }
+ }
+
+ list_del_init(&packet->driver_list);
+ hpsb_packet_sent(ohci->host, packet, ack);
+
+ if (datasize)
+ pci_unmap_single(ohci->dev,
+ cpu_to_le32(d->prg_cpu[d->sent_ind]->end.address),
+ datasize, PCI_DMA_TODEVICE);
+
+ d->sent_ind = (d->sent_ind+1)%d->num_desc;
+ d->free_prgs++;
+ }
+
+ dma_trm_flush(ohci, d);
+
+ spin_unlock_irqrestore(&d->lock, flags);
+}
+
+static void free_dma_rcv_ctx(struct dma_rcv_ctx *d)
+{
+ int i;
+ struct ti_ohci *ohci = d->ohci;
+
+ if (ohci == NULL)
+ return;
+
+ DBGMSG("Freeing dma_rcv_ctx %d", d->ctx);
+
+ if (d->buf_cpu) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->buf_cpu[i] && d->buf_bus[i])
+ pci_free_consistent(
+ ohci->dev, d->buf_size,
+ d->buf_cpu[i], d->buf_bus[i]);
+ kfree(d->buf_cpu);
+ kfree(d->buf_bus);
+ }
+ if (d->prg_cpu) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->prg_cpu[i] && d->prg_bus[i])
+ pci_pool_free(d->prg_pool, d->prg_cpu[i],
+ d->prg_bus[i]);
+ pci_pool_destroy(d->prg_pool);
+ kfree(d->prg_cpu);
+ kfree(d->prg_bus);
+ }
+ kfree(d->spb);
+
+ /* Mark this context as freed. */
+ d->ohci = NULL;
+}
+
+static int
+alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int buf_size, int split_buf_size, int context_base)
+{
+ int i, len;
+ static int num_allocs;
+ static char pool_name[20];
+
+ d->ohci = ohci;
+ d->type = type;
+ d->ctx = ctx;
+
+ d->num_desc = num_desc;
+ d->buf_size = buf_size;
+ d->split_buf_size = split_buf_size;
+
+ d->ctrlSet = 0;
+ d->ctrlClear = 0;
+ d->cmdPtr = 0;
+
+ d->buf_cpu = kzalloc(d->num_desc * sizeof(*d->buf_cpu), GFP_ATOMIC);
+ d->buf_bus = kzalloc(d->num_desc * sizeof(*d->buf_bus), GFP_ATOMIC);
+
+ if (d->buf_cpu == NULL || d->buf_bus == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "DMA buffer");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_ATOMIC);
+ d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_ATOMIC);
+
+ if (d->prg_cpu == NULL || d->prg_bus == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "DMA prg");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC);
+
+ if (d->spb == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "split buffer");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ len = sprintf(pool_name, "ohci1394_rcv_prg");
+ sprintf(pool_name+len, "%d", num_allocs);
+ d->prg_pool = pci_pool_create(pool_name, ohci->dev,
+ sizeof(struct dma_cmd), 4, 0);
+ if(d->prg_pool == NULL)
+ {
+ PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name);
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+ num_allocs++;
+
+ for (i=0; i<d->num_desc; i++) {
+ d->buf_cpu[i] = pci_alloc_consistent(ohci->dev,
+ d->buf_size,
+ d->buf_bus+i);
+
+ if (d->buf_cpu[i] != NULL) {
+ memset(d->buf_cpu[i], 0, d->buf_size);
+ } else {
+ PRINT(KERN_ERR,
+ "Failed to allocate %s", "DMA buffer");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+
+ d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i);
+
+ if (d->prg_cpu[i] != NULL) {
+ memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd));
+ } else {
+ PRINT(KERN_ERR,
+ "Failed to allocate %s", "DMA prg");
+ free_dma_rcv_ctx(d);
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_init(&d->lock);
+
+ d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+ d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+ d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
+
+ tasklet_init(&d->task, dma_rcv_tasklet, (unsigned long) d);
+ return 0;
+}
+
+static void free_dma_trm_ctx(struct dma_trm_ctx *d)
+{
+ int i;
+ struct ti_ohci *ohci = d->ohci;
+
+ if (ohci == NULL)
+ return;
+
+ DBGMSG("Freeing dma_trm_ctx %d", d->ctx);
+
+ if (d->prg_cpu) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->prg_cpu[i] && d->prg_bus[i])
+ pci_pool_free(d->prg_pool, d->prg_cpu[i],
+ d->prg_bus[i]);
+ pci_pool_destroy(d->prg_pool);
+ kfree(d->prg_cpu);
+ kfree(d->prg_bus);
+ }
+
+ /* Mark this context as freed. */
+ d->ohci = NULL;
+}
+
+static int
+alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
+ enum context_type type, int ctx, int num_desc,
+ int context_base)
+{
+ int i, len;
+ static char pool_name[20];
+ static int num_allocs=0;
+
+ d->ohci = ohci;
+ d->type = type;
+ d->ctx = ctx;
+ d->num_desc = num_desc;
+ d->ctrlSet = 0;
+ d->ctrlClear = 0;
+ d->cmdPtr = 0;
+
+ d->prg_cpu = kzalloc(d->num_desc * sizeof(*d->prg_cpu), GFP_KERNEL);
+ d->prg_bus = kzalloc(d->num_desc * sizeof(*d->prg_bus), GFP_KERNEL);
+
+ if (d->prg_cpu == NULL || d->prg_bus == NULL) {
+ PRINT(KERN_ERR, "Failed to allocate %s", "AT DMA prg");
+ free_dma_trm_ctx(d);
+ return -ENOMEM;
+ }
+
+ len = sprintf(pool_name, "ohci1394_trm_prg");
+ sprintf(pool_name+len, "%d", num_allocs);
+ d->prg_pool = pci_pool_create(pool_name, ohci->dev,
+ sizeof(struct at_dma_prg), 4, 0);
+ if (d->prg_pool == NULL) {
+ PRINT(KERN_ERR, "pci_pool_create failed for %s", pool_name);
+ free_dma_trm_ctx(d);
+ return -ENOMEM;
+ }
+ num_allocs++;
+
+ for (i = 0; i < d->num_desc; i++) {
+ d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, GFP_KERNEL, d->prg_bus+i);
+
+ if (d->prg_cpu[i] != NULL) {
+ memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg));
+ } else {
+ PRINT(KERN_ERR,
+ "Failed to allocate %s", "AT DMA prg");
+ free_dma_trm_ctx(d);
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_init(&d->lock);
+
+ /* initialize tasklet */
+ d->ctrlSet = context_base + OHCI1394_ContextControlSet;
+ d->ctrlClear = context_base + OHCI1394_ContextControlClear;
+ d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
+ tasklet_init(&d->task, dma_trm_tasklet, (unsigned long)d);
+ return 0;
+}
+
+static void ohci_set_hw_config_rom(struct hpsb_host *host, quadlet_t *config_rom)
+{
+ struct ti_ohci *ohci = host->hostdata;
+
+ reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(config_rom[0]));
+ reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(config_rom[2]));
+
+ memcpy(ohci->csr_config_rom_cpu, config_rom, OHCI_CONFIG_ROM_LEN);
+}
+
+
+static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
+ quadlet_t data, quadlet_t compare)
+{
+ struct ti_ohci *ohci = host->hostdata;
+ int i;
+
+ reg_write(ohci, OHCI1394_CSRData, data);
+ reg_write(ohci, OHCI1394_CSRCompareData, compare);
+ reg_write(ohci, OHCI1394_CSRControl, reg & 0x3);
+
+ for (i = 0; i < OHCI_LOOP_COUNT; i++) {
+ if (reg_read(ohci, OHCI1394_CSRControl) & 0x80000000)
+ break;
+
+ mdelay(1);
+ }
+
+ return reg_read(ohci, OHCI1394_CSRData);
+}
+
+static struct hpsb_host_driver ohci1394_driver = {
+ .owner = THIS_MODULE,
+ .name = OHCI1394_DRIVER_NAME,
+ .set_hw_config_rom = ohci_set_hw_config_rom,
+ .transmit_packet = ohci_transmit,
+ .devctl = ohci_devctl,
+ .isoctl = ohci_isoctl,
+ .hw_csr_reg = ohci_hw_csr_reg,
+};
+
+/***********************************
+ * PCI Driver Interface functions *
+ ***********************************/
+
+#ifdef CONFIG_PPC_PMAC
+static void ohci1394_pmac_on(struct pci_dev *dev)
+{
+ if (machine_is(powermac)) {
+ struct device_node *ofn = pci_device_to_OF_node(dev);
+
+ if (ofn) {
+ pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 1);
+ pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 1);
+ }
+ }
+}
+
+static void ohci1394_pmac_off(struct pci_dev *dev)
+{
+ if (machine_is(powermac)) {
+ struct device_node *ofn = pci_device_to_OF_node(dev);
+
+ if (ofn) {
+ pmac_call_feature(PMAC_FTR_1394_ENABLE, ofn, 0, 0);
+ pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, ofn, 0, 0);
+ }
+ }
+}
+#else
+#define ohci1394_pmac_on(dev)
+#define ohci1394_pmac_off(dev)
+#endif /* CONFIG_PPC_PMAC */
+
+static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ struct hpsb_host *host;
+ struct ti_ohci *ohci; /* shortcut to currently handled device */
+ resource_size_t ohci_base;
+ int err = -ENOMEM;
+
+ ohci1394_pmac_on(dev);
+ if (pci_enable_device(dev)) {
+ PRINT_G(KERN_ERR, "Failed to enable OHCI hardware");
+ err = -ENXIO;
+ goto err;
+ }
+ pci_set_master(dev);
+
+ host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev);
+ if (!host) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "host structure");
+ goto err;
+ }
+ ohci = host->hostdata;
+ ohci->dev = dev;
+ ohci->host = host;
+ ohci->init_state = OHCI_INIT_ALLOC_HOST;
+ host->pdev = dev;
+ pci_set_drvdata(dev, ohci);
+
+ /* We don't want hardware swapping */
+ pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0);
+
+ /* Some oddball Apple controllers do not order the selfid
+ * properly, so we make up for it here. */
+#ifndef __LITTLE_ENDIAN
+ /* XXX: Need a better way to check this. I'm wondering if we can
+ * read the values of the OHCI1394_PCI_HCI_Control and the
+ * noByteSwapData registers to see if they were not cleared to
+ * zero. Should this work? Obviously it's not defined what these
+ * registers will read when they aren't supported. Bleh! */
+ if (dev->vendor == PCI_VENDOR_ID_APPLE &&
+ dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
+ ohci->no_swap_incoming = 1;
+ ohci->selfid_swap = 0;
+ } else
+ ohci->selfid_swap = 1;
+#endif
+
+
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_FW
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_FW 0x006e
+#endif
+
+ /* These chipsets require a bit of extra care when checking after
+ * a busreset. */
+ if ((dev->vendor == PCI_VENDOR_ID_APPLE &&
+ dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) ||
+ (dev->vendor == PCI_VENDOR_ID_NVIDIA &&
+ dev->device == PCI_DEVICE_ID_NVIDIA_NFORCE2_FW))
+ ohci->check_busreset = 1;
+
+ /* We hardwire the MMIO length, since some CardBus adaptors
+ * fail to report the right length. Anyway, the ohci spec
+ * clearly says it's 2kb, so this shouldn't be a problem. */
+ ohci_base = pci_resource_start(dev, 0);
+ if (pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE)
+ PRINT(KERN_WARNING, "PCI resource length of 0x%llx too small!",
+ (unsigned long long)pci_resource_len(dev, 0));
+
+ if (!request_mem_region(ohci_base, OHCI1394_REGISTER_SIZE,
+ OHCI1394_DRIVER_NAME)) {
+ PRINT_G(KERN_ERR, "MMIO resource (0x%llx - 0x%llx) unavailable",
+ (unsigned long long)ohci_base,
+ (unsigned long long)ohci_base + OHCI1394_REGISTER_SIZE);
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_MEM_REGION;
+
+ ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE);
+ if (ohci->registers == NULL) {
+ PRINT_G(KERN_ERR, "Failed to remap registers");
+ err = -ENXIO;
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_IOMAPPING;
+ DBGMSG("Remapped memory spaces reg 0x%p", ohci->registers);
+
+ /* csr_config rom allocation */
+ ohci->csr_config_rom_cpu =
+ pci_alloc_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN,
+ &ohci->csr_config_rom_bus);
+ if (ohci->csr_config_rom_cpu == NULL) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "buffer config rom");
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_CONFIG_ROM_BUFFER;
+
+ /* self-id dma buffer allocation */
+ ohci->selfid_buf_cpu =
+ pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE,
+ &ohci->selfid_buf_bus);
+ if (ohci->selfid_buf_cpu == NULL) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "self-ID buffer");
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER;
+
+ if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff)
+ PRINT(KERN_INFO, "SelfID buffer %p is not aligned on "
+ "8Kb boundary... may cause problems on some CXD3222 chip",
+ ohci->selfid_buf_cpu);
+
+ /* No self-id errors at startup */
+ ohci->self_id_errors = 0;
+
+ ohci->init_state = OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE;
+ /* AR DMA request context allocation */
+ if (alloc_dma_rcv_ctx(ohci, &ohci->ar_req_context,
+ DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
+ AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
+ OHCI1394_AsReqRcvContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Req context");
+ goto err;
+ }
+ /* AR DMA response context allocation */
+ if (alloc_dma_rcv_ctx(ohci, &ohci->ar_resp_context,
+ DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
+ AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
+ OHCI1394_AsRspRcvContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AR Resp context");
+ goto err;
+ }
+ /* AT DMA request context */
+ if (alloc_dma_trm_ctx(ohci, &ohci->at_req_context,
+ DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
+ OHCI1394_AsReqTrContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Req context");
+ goto err;
+ }
+ /* AT DMA response context */
+ if (alloc_dma_trm_ctx(ohci, &ohci->at_resp_context,
+ DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
+ OHCI1394_AsRspTrContextBase) < 0) {
+ PRINT_G(KERN_ERR, "Failed to allocate %s", "AT Resp context");
+ goto err;
+ }
+ /* Start off with a soft reset, to clear everything to a sane
+ * state. */
+ ohci_soft_reset(ohci);
+
+ /* Now enable LPS, which we need in order to start accessing
+ * most of the registers. In fact, on some cards (ALI M5251),
+ * accessing registers in the SClk domain without LPS enabled
+ * will lock up the machine. Wait 50msec to make sure we have
+ * full link enabled. */
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
+
+ /* Disable and clear interrupts */
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+
+ mdelay(50);
+
+ /* Determine the number of available IR and IT contexts. */
+ ohci->nb_iso_rcv_ctx =
+ get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
+ ohci->nb_iso_xmit_ctx =
+ get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
+
+ /* Set the usage bits for non-existent contexts so they can't
+ * be allocated */
+ ohci->ir_ctx_usage = ~0 << ohci->nb_iso_rcv_ctx;
+ ohci->it_ctx_usage = ~0 << ohci->nb_iso_xmit_ctx;
+
+ INIT_LIST_HEAD(&ohci->iso_tasklet_list);
+ spin_lock_init(&ohci->iso_tasklet_list_lock);
+ ohci->ISO_channel_usage = 0;
+ spin_lock_init(&ohci->IR_channel_lock);
+
+ spin_lock_init(&ohci->event_lock);
+
+ /*
+ * interrupts are disabled, all right, but... due to IRQF_SHARED we
+ * might get called anyway. We'll see no event, of course, but
+ * we need to get to that "no event", so enough should be initialized
+ * by that point.
+ */
+ if (request_irq(dev->irq, ohci_irq_handler, IRQF_SHARED,
+ OHCI1394_DRIVER_NAME, ohci)) {
+ PRINT_G(KERN_ERR, "Failed to allocate interrupt %d", dev->irq);
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_HAVE_IRQ;
+ ohci_initialize(ohci);
+
+ /* Set certain csr values */
+ host->csr.guid_hi = reg_read(ohci, OHCI1394_GUIDHi);
+ host->csr.guid_lo = reg_read(ohci, OHCI1394_GUIDLo);
+ host->csr.cyc_clk_acc = 100; /* how do we determine clk accuracy? */
+ host->csr.max_rec = (reg_read(ohci, OHCI1394_BusOptions) >> 12) & 0xf;
+ host->csr.lnk_spd = reg_read(ohci, OHCI1394_BusOptions) & 0x7;
+
+ if (phys_dma) {
+ host->low_addr_space =
+ (u64) reg_read(ohci, OHCI1394_PhyUpperBound) << 16;
+ if (!host->low_addr_space)
+ host->low_addr_space = OHCI1394_PHYS_UPPER_BOUND_FIXED;
+ }
+ host->middle_addr_space = OHCI1394_MIDDLE_ADDRESS_SPACE;
+
+ /* Tell the highlevel this host is ready */
+ if (hpsb_add_host(host)) {
+ PRINT_G(KERN_ERR, "Failed to register host with highlevel");
+ goto err;
+ }
+ ohci->init_state = OHCI_INIT_DONE;
+
+ return 0;
+err:
+ ohci1394_pci_remove(dev);
+ return err;
+}
+
+static void ohci1394_pci_remove(struct pci_dev *dev)
+{
+ struct ti_ohci *ohci;
+ struct device *device;
+
+ ohci = pci_get_drvdata(dev);
+ if (!ohci)
+ goto out;
+
+ device = get_device(&ohci->host->device);
+
+ switch (ohci->init_state) {
+ case OHCI_INIT_DONE:
+ hpsb_remove_host(ohci->host);
+
+ /* Clear out BUS Options */
+ reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
+ reg_write(ohci, OHCI1394_BusOptions,
+ (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) |
+ 0x00ff0000);
+ memset(ohci->csr_config_rom_cpu, 0, OHCI_CONFIG_ROM_LEN);
+
+ case OHCI_INIT_HAVE_IRQ:
+ /* Clear interrupt registers */
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
+
+ /* Disable IRM Contender */
+ set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4));
+
+ /* Clear link control register */
+ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
+
+ /* Let all other nodes know to ignore us */
+ ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
+
+ /* Soft reset before we start - this disables
+ * interrupts and clears linkEnable and LPS. */
+ ohci_soft_reset(ohci);
+ free_irq(dev->irq, ohci);
+
+ case OHCI_INIT_HAVE_TXRX_BUFFERS__MAYBE:
+ /* The ohci_soft_reset() stops all DMA contexts, so we
+ * dont need to do this. */
+ free_dma_rcv_ctx(&ohci->ar_req_context);
+ free_dma_rcv_ctx(&ohci->ar_resp_context);
+ free_dma_trm_ctx(&ohci->at_req_context);
+ free_dma_trm_ctx(&ohci->at_resp_context);
+
+ case OHCI_INIT_HAVE_SELFID_BUFFER:
+ pci_free_consistent(dev, OHCI1394_SI_DMA_BUF_SIZE,
+ ohci->selfid_buf_cpu,
+ ohci->selfid_buf_bus);
+
+ case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER:
+ pci_free_consistent(dev, OHCI_CONFIG_ROM_LEN,
+ ohci->csr_config_rom_cpu,
+ ohci->csr_config_rom_bus);
+
+ case OHCI_INIT_HAVE_IOMAPPING:
+ iounmap(ohci->registers);
+
+ case OHCI_INIT_HAVE_MEM_REGION:
+ release_mem_region(pci_resource_start(dev, 0),
+ OHCI1394_REGISTER_SIZE);
+
+ case OHCI_INIT_ALLOC_HOST:
+ pci_set_drvdata(dev, NULL);
+ }
+
+ if (device)
+ put_device(device);
+out:
+ ohci1394_pmac_off(dev);
+}
+
+#ifdef CONFIG_PM
+static int ohci1394_pci_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ int err;
+ struct ti_ohci *ohci = pci_get_drvdata(dev);
+
+ if (!ohci) {
+ printk(KERN_ERR "%s: tried to suspend nonexisting host\n",
+ OHCI1394_DRIVER_NAME);
+ return -ENXIO;
+ }
+ DBGMSG("suspend called");
+
+ /* Clear the async DMA contexts and stop using the controller */
+ hpsb_bus_reset(ohci->host);
+
+ /* See ohci1394_pci_remove() for comments on this sequence */
+ reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
+ reg_write(ohci, OHCI1394_BusOptions,
+ (reg_read(ohci, OHCI1394_BusOptions) & 0x0000f007) |
+ 0x00ff0000);
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff);
+ set_phy_reg(ohci, 4, ~0xc0 & get_phy_reg(ohci, 4));
+ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
+ ohci_devctl(ohci->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
+ ohci_soft_reset(ohci);
+
+ err = pci_save_state(dev);
+ if (err) {
+ PRINT(KERN_ERR, "pci_save_state failed with %d", err);
+ return err;
+ }
+ err = pci_set_power_state(dev, pci_choose_state(dev, state));
+ if (err)
+ DBGMSG("pci_set_power_state failed with %d", err);
+ ohci1394_pmac_off(dev);
+
+ return 0;
+}
+
+static int ohci1394_pci_resume(struct pci_dev *dev)
+{
+ int err;
+ struct ti_ohci *ohci = pci_get_drvdata(dev);
+
+ if (!ohci) {
+ printk(KERN_ERR "%s: tried to resume nonexisting host\n",
+ OHCI1394_DRIVER_NAME);
+ return -ENXIO;
+ }
+ DBGMSG("resume called");
+
+ ohci1394_pmac_on(dev);
+ pci_set_power_state(dev, PCI_D0);
+ pci_restore_state(dev);
+ err = pci_enable_device(dev);
+ if (err) {
+ PRINT(KERN_ERR, "pci_enable_device failed with %d", err);
+ return err;
+ }
+
+ /* See ohci1394_pci_probe() for comments on this sequence */
+ ohci_soft_reset(ohci);
+ reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
+ reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
+ mdelay(50);
+ ohci_initialize(ohci);
+
+ hpsb_resume_host(ohci->host);
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct pci_device_id ohci1394_pci_tbl[] = {
+ {
+ .class = PCI_CLASS_SERIAL_FIREWIRE_OHCI,
+ .class_mask = PCI_ANY_ID,
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);
+
+static struct pci_driver ohci1394_pci_driver = {
+ .name = OHCI1394_DRIVER_NAME,
+ .id_table = ohci1394_pci_tbl,
+ .probe = ohci1394_pci_probe,
+ .remove = ohci1394_pci_remove,
+#ifdef CONFIG_PM
+ .resume = ohci1394_pci_resume,
+ .suspend = ohci1394_pci_suspend,
+#endif
+};
+
+/***********************************
+ * OHCI1394 Video Interface *
+ ***********************************/
+
+/* essentially the only purpose of this code is to allow another
+ module to hook into ohci's interrupt handler */
+
+/* returns zero if successful, one if DMA context is locked up */
+int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg)
+{
+ int i=0;
+
+ /* stop the channel program if it's still running */
+ reg_write(ohci, reg, 0x8000);
+
+ /* Wait until it effectively stops */
+ while (reg_read(ohci, reg) & 0x400) {
+ i++;
+ if (i>5000) {
+ PRINT(KERN_ERR,
+ "Runaway loop while stopping context: %s...", msg ? msg : "");
+ return 1;
+ }
+
+ mb();
+ udelay(10);
+ }
+ if (msg) PRINT(KERN_ERR, "%s: dma prg stopped", msg);
+ return 0;
+}
+
+void ohci1394_init_iso_tasklet(struct ohci1394_iso_tasklet *tasklet, int type,
+ void (*func)(unsigned long), unsigned long data)
+{
+ tasklet_init(&tasklet->tasklet, func, data);
+ tasklet->type = type;
+ /* We init the tasklet->link field, so we can list_del() it
+ * without worrying whether it was added to the list or not. */
+ INIT_LIST_HEAD(&tasklet->link);
+}
+
+int ohci1394_register_iso_tasklet(struct ti_ohci *ohci,
+ struct ohci1394_iso_tasklet *tasklet)
+{
+ unsigned long flags, *usage;
+ int n, i, r = -EBUSY;
+
+ if (tasklet->type == OHCI_ISO_TRANSMIT) {
+ n = ohci->nb_iso_xmit_ctx;
+ usage = &ohci->it_ctx_usage;
+ }
+ else {
+ n = ohci->nb_iso_rcv_ctx;
+ usage = &ohci->ir_ctx_usage;
+
+ /* only one receive context can be multichannel (OHCI sec 10.4.1) */
+ if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
+ if (test_and_set_bit(0, &ohci->ir_multichannel_used)) {
+ return r;
+ }
+ }
+ }
+
+ spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
+
+ for (i = 0; i < n; i++)
+ if (!test_and_set_bit(i, usage)) {
+ tasklet->context = i;
+ list_add_tail(&tasklet->link, &ohci->iso_tasklet_list);
+ r = 0;
+ break;
+ }
+
+ spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
+
+ return r;
+}
+
+void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
+ struct ohci1394_iso_tasklet *tasklet)
+{
+ unsigned long flags;
+
+ tasklet_kill(&tasklet->tasklet);
+
+ spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
+
+ if (tasklet->type == OHCI_ISO_TRANSMIT)
+ clear_bit(tasklet->context, &ohci->it_ctx_usage);
+ else {
+ clear_bit(tasklet->context, &ohci->ir_ctx_usage);
+
+ if (tasklet->type == OHCI_ISO_MULTICHANNEL_RECEIVE) {
+ clear_bit(0, &ohci->ir_multichannel_used);
+ }
+ }
+
+ list_del(&tasklet->link);
+
+ spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
+}
+
+EXPORT_SYMBOL(ohci1394_stop_context);
+EXPORT_SYMBOL(ohci1394_init_iso_tasklet);
+EXPORT_SYMBOL(ohci1394_register_iso_tasklet);
+EXPORT_SYMBOL(ohci1394_unregister_iso_tasklet);
+
+/***********************************
+ * General module initialization *
+ ***********************************/
+
+MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>");
+MODULE_DESCRIPTION("Driver for PCI OHCI IEEE-1394 controllers");
+MODULE_LICENSE("GPL");
+
+static void __exit ohci1394_cleanup (void)
+{
+ pci_unregister_driver(&ohci1394_pci_driver);
+}
+
+static int __init ohci1394_init(void)
+{
+ return pci_register_driver(&ohci1394_pci_driver);
+}
+
+module_init(ohci1394_init);
+module_exit(ohci1394_cleanup);
OpenPOWER on IntegriCloud