/drivers/ieee1394/ohci1394.c
https://bitbucket.org/evzijst/gittest · C · 3705 lines · 2512 code · 685 blank · 508 comment · 416 complexity · 1b44604c45ed287f0ac5dea7f1e3021c MD5 · raw file
Large files are truncated click here to view the full file
- /*
- * 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/config.h>
- #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/sched.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...)
- #endif
- #ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG
- #define OHCI_DMA_ALLOC(fmt, args...) \
- HPSB_ERR("%s(%s)alloc(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \
- ++global_outstanding_dmas, ## args)
- #define OHCI_DMA_FREE(fmt, args...) \
- HPSB_ERR("%s(%s)free(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \
- --global_outstanding_dmas, ## args)
- static int global_outstanding_dmas = 0;
- #else
- #define OHCI_DMA_ALLOC(fmt, args...)
- #define OHCI_DMA_FREE(fmt, args...)
- #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)
- static char version[] __devinitdata =
- "$Rev: 1250 $ Ben Collins <bcollins@debian.org>";
- /* Module Parameters */
- static int phys_dma = 1;
- module_param(phys_dma, int, 0644);
- 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 stop_dma_rcv_ctx(struct dma_rcv_ctx *d);
- 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 unsigned hdr_sizes[] =
- {
- 3, /* TCODE_WRITEQ */
- 4, /* TCODE_WRITEB */
- 3, /* TCODE_WRITE_RESPONSE */
- 0, /* ??? */
- 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 */
- };
- /* Swap headers */
- static inline void packet_swab(quadlet_t *data, int tcode)
- {
- size_t size = hdr_sizes[tcode];
- if (tcode > TCODE_LOCK_RESPONSE || hdr_sizes[tcode] == 0)
- return;
- while (size--)
- data[size] = swab32(data[size]);
- }
- #else
- /* Don't waste cycles on same sex byte swaps */
- #define packet_swab(w,x)
- #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)
- {
- char irq_buf[16];
- quadlet_t buf;
- int num_ports, i;
- spin_lock_init(&ohci->phy_reg_lock);
- spin_lock_init(&ohci->event_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; /* 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);
- set_phy_reg_mask(ohci, 4, PHY_04_LCTRL |
- (hpsb_disable_irm ? 0 : PHY_04_CONTENDER));
- /* Set up self-id dma buffer */
- reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus);
- /* enable self-id and phys */
- reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID |
- OHCI1394_LinkControl_RcvPhyPkt);
- /* 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);
-
- /* Don't accept phy packets into AR request context */
- reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
- /* 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);
-
- /* Initialize IR Legacy DMA */
- ohci->ir_legacy_channels = 0;
- initialize_dma_rcv_ctx(&ohci->ir_legacy_context, 1);
- DBGMSG("ISO receive legacy context activated");
- /*
- * Accept AT requests from all nodes. This probably
- * will have to be controlled from the subsystem
- * on a per node basis.
- */
- reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000);
- /* 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_cycleInconsistent);
- /* Enable link */
- reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
- buf = reg_read(ohci, OHCI1394_Version);
- #ifndef __sparc__
- sprintf (irq_buf, "%d", ohci->dev->irq);
- #else
- sprintf (irq_buf, "%s", __irq_itoa(ohci->dev->irq));
- #endif
- PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%s] "
- "MMIO=[%lx-%lx] Max Packet=[%d]",
- ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
- ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), irq_buf,
- pci_resource_start(ohci->dev, 0),
- pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
- ohci->max_packet_size);
- /* 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 setting 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];
- }
- packet_swab(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", __FUNCTION__,
- 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));
- OHCI_DMA_ALLOC("single, block transmit packet");
- 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;
- packet_swab(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));
- OHCI_DMA_ALLOC("single, iso transmit packet");
- 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;
- }
- /* Decide whether we have an iso, a request, or a response packet */
- if (packet->type == hpsb_raw)
- d = &ohci->at_req_context;
- else if ((packet->tcode == TCODE_ISO_DATA) && (packet->type == hpsb_iso)) {
- /* The legacy IT DMA context is initialized on first
- * use. However, the alloc cannot be run from
- * interrupt context, so we bail out if that is the
- * case. I don't see anyone sending ISO packets from
- * interrupt context anyway... */
- if (ohci->it_legacy_context.ohci == NULL) {
- if (in_interrupt()) {
- PRINT(KERN_ERR,
- "legacy IT context cannot be initialized during interrupt");
- return -EINVAL;
- }
- if (alloc_dma_trm_ctx(ohci, &ohci->it_legacy_context,
- DMA_CTX_ISO, 0, IT_NUM_DESC,
- OHCI1394_IsoXmitContextBase) < 0) {
- PRINT(KERN_ERR,
- "error initializing legacy IT context");
- return -ENOMEM;
- }
- initialize_dma_trm_ctx(&ohci->it_legacy_context);
- }
- d = &ohci->it_legacy_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;
- unsigned long flags;
- int 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;
- case ISO_LISTEN_CHANNEL:
- {
- u64 mask;
- if (arg<0 || arg>63) {
- PRINT(KERN_ERR,
- "%s: IS0 listen channel %d is out of range",
- __FUNCTION__, arg);
- return -EFAULT;
- }
- mask = (u64)0x1<<arg;
- spin_lock_irqsave(&ohci->IR_channel_lock, flags);
- if (ohci->ISO_channel_usage & mask) {
- PRINT(KERN_ERR,
- "%s: IS0 listen channel %d is already used",
- __FUNCTION__, arg);
- spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
- return -EFAULT;
- }
- ohci->ISO_channel_usage |= mask;
- ohci->ir_legacy_channels |= mask;
- if (arg>31)
- reg_write(ohci, OHCI1394_IRMultiChanMaskHiSet,
- 1<<(arg-32));
- else
- reg_write(ohci, OHCI1394_IRMultiChanMaskLoSet,
- 1<<arg);
- spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
- DBGMSG("Listening enabled on channel %d", arg);
- break;
- }
- case ISO_UNLISTEN_CHANNEL:
- {
- u64 mask;
- if (arg<0 || arg>63) {
- PRINT(KERN_ERR,
- "%s: IS0 unlisten channel %d is out of range",
- __FUNCTION__, arg);
- return -EFAULT;
- }
- mask = (u64)0x1<<arg;
- spin_lock_irqsave(&ohci->IR_channel_lock, flags);
- if (!(ohci->ISO_channel_usage & mask)) {
- PRINT(KERN_ERR,
- "%s: IS0 unlisten channel %d is not used",
- __FUNCTION__, arg);
- spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
- return -EFAULT;
- }
- ohci->ISO_channel_usage &= ~mask;
- ohci->ir_legacy_channels &= ~mask;
- if (arg>31)
- reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear,
- 1<<(arg-32));
- else
- reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear,
- 1<<arg);
- spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
- DBGMSG("Listening disabled on channel %d", arg);
- 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), SLAB_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)
- 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];
- /* '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)
- {
- int len;
- /* release the memory where the packet was */
- len = info->len;
- /* add the wasted space for padding to 4 bytes */
- if (len % 4)
- len += 4 - (len % 4);
- /* add 8 bytes for the OHCI DMA data format overhead */
- len += 8;
- recv->released_bytes += 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;
- 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;
- /* payload is padded to 4 bytes */
- if (len % 4) {
- recv->dma_offset += 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, 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 w…