diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/ieee1394/ohci1394.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/ieee1394/ohci1394.c')
-rw-r--r-- | drivers/ieee1394/ohci1394.c | 3705 |
1 files changed, 3705 insertions, 0 deletions
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c new file mode 100644 index 00000000000..97ff364c043 --- /dev/null +++ b/drivers/ieee1394/ohci1394.c @@ -0,0 +1,3705 @@ +/* + * 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 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, 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; + + 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), SLAB_KERNEL); + if (!xmit) + return -ENOMEM; + + iso->hostdata = xmit; + xmit->ohci = iso->host->hostdata; + xmit->task_active = 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) + 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; + + /* 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); + + /* 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(&d->fifo_list, &packet_list); + list_splice(&d->pending_list, &packet_list); + INIT_LIST_HEAD(&d->fifo_list); + INIT_LIST_HEAD(&d->pending_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; + + spin_lock(&ohci->iso_tasklet_list_lock); + + list_for_each_entry(t, &ohci->iso_tasklet_list, link) { + mask = 1 << t->context; + + if (t->type == OHCI_ISO_TRANSMIT && tx_event & mask) + tasklet_schedule(&t->tasklet); + else if (rx_event & mask) + tasklet_schedule(&t->tasklet); + } + + spin_unlock(&ohci->iso_tasklet_list_lock); + +} + +static irqreturn_t ohci_irq_handler(int irq, void *dev_id, + struct pt_regs *regs_are_unused) +{ + 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_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); + + /* Accept Physical requests from all nodes. */ + reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0xffffffff); + reg_write(ohci,OHCI1394_AsReqFilterLoSet, 0xffffffff); + + /* 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); + reg_write(ohci,OHCI1394_PhyUpperBound, 0xffff0000); + } else { + reg_write(ohci,OHCI1394_PhyReqFilterHiSet, 0x00000000); + reg_write(ohci,OHCI1394_PhyReqFilterLoSet, 0x00000000); + } + + 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) + packet_swab(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, + (cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>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=" + "0x%02X 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=" + "0x%02X 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); + OHCI_DMA_FREE("single Xmit data packet"); + } + + 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 stop_dma_rcv_ctx(struct dma_rcv_ctx *d) +{ + if (d->ctrlClear) { + ohci1394_stop_context(d->ohci, d->ctrlClear, NULL); + + if (d->type == DMA_CTX_ISO) { + /* disable interrupts */ + reg_write(d->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << d->ctx); + ohci1394_unregister_iso_tasklet(d->ohci, &d->ohci->ir_legacy_tasklet); + } else { + tasklet_kill(&d->task); + } + } +} + + +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]); + OHCI_DMA_FREE("consistent dma_rcv buf[%d]", 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]); + OHCI_DMA_FREE("consistent dma_rcv prg[%d]", i); + } + pci_pool_destroy(d->prg_pool); + OHCI_DMA_FREE("dma_rcv prg pool"); + kfree(d->prg_cpu); + kfree(d->prg_bus); + } + if (d->spb) 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 = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_ATOMIC); + d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_ATOMIC); + + if (d->buf_cpu == NULL || d->buf_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate dma buffer"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + memset(d->buf_cpu, 0, d->num_desc * sizeof(quadlet_t*)); + memset(d->buf_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + d->prg_cpu = kmalloc(d->num_desc * sizeof(struct dma_cmd*), + GFP_ATOMIC); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_ATOMIC); + + if (d->prg_cpu == NULL || d->prg_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate dma prg"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct dma_cmd*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + d->spb = kmalloc(d->split_buf_size, GFP_ATOMIC); + + if (d->spb == NULL) { + PRINT(KERN_ERR, "Failed to allocate 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++; + + OHCI_DMA_ALLOC("dma_rcv prg pool"); + + for (i=0; i<d->num_desc; i++) { + d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, + d->buf_size, + d->buf_bus+i); + OHCI_DMA_ALLOC("consistent dma_rcv buf[%d]", i); + + if (d->buf_cpu[i] != NULL) { + memset(d->buf_cpu[i], 0, d->buf_size); + } else { + PRINT(KERN_ERR, + "Failed to allocate dma buffer"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + + d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, SLAB_KERNEL, d->prg_bus+i); + OHCI_DMA_ALLOC("pool dma_rcv prg[%d]", i); + + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); + } else { + PRINT(KERN_ERR, + "Failed to allocate dma prg"); + free_dma_rcv_ctx(d); + return -ENOMEM; + } + } + + spin_lock_init(&d->lock); + + if (type == DMA_CTX_ISO) { + ohci1394_init_iso_tasklet(&ohci->ir_legacy_tasklet, + OHCI_ISO_MULTICHANNEL_RECEIVE, + dma_rcv_tasklet, (unsigned long) d); + if (ohci1394_register_iso_tasklet(ohci, + &ohci->ir_legacy_tasklet) < 0) { + PRINT(KERN_ERR, "No IR DMA context available"); + free_dma_rcv_ctx(d); + return -EBUSY; + } + + /* the IR context can be assigned to any DMA context + * by ohci1394_register_iso_tasklet */ + d->ctx = ohci->ir_legacy_tasklet.context; + d->ctrlSet = OHCI1394_IsoRcvContextControlSet + 32*d->ctx; + d->ctrlClear = OHCI1394_IsoRcvContextControlClear + 32*d->ctx; + d->cmdPtr = OHCI1394_IsoRcvCommandPtr + 32*d->ctx; + d->ctxtMatch = OHCI1394_IsoRcvContextMatch + 32*d->ctx; + } else { + 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]); + OHCI_DMA_FREE("pool dma_trm prg[%d]", i); + } + pci_pool_destroy(d->prg_pool); + OHCI_DMA_FREE("dma_trm 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 = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), + GFP_KERNEL); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); + + if (d->prg_cpu == NULL || d->prg_bus == NULL) { + PRINT(KERN_ERR, "Failed to allocate at dma prg"); + free_dma_trm_ctx(d); + return -ENOMEM; + } + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + 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++; + + OHCI_DMA_ALLOC("dma_rcv prg pool"); + + for (i = 0; i < d->num_desc; i++) { + d->prg_cpu[i] = pci_pool_alloc(d->prg_pool, SLAB_KERNEL, d->prg_bus+i); + OHCI_DMA_ALLOC("pool dma_trm prg[%d]", 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 at dma prg"); + free_dma_trm_ctx(d); + return -ENOMEM; + } + } + + spin_lock_init(&d->lock); + + /* initialize tasklet */ + if (type == DMA_CTX_ISO) { + ohci1394_init_iso_tasklet(&ohci->it_legacy_tasklet, OHCI_ISO_TRANSMIT, + dma_trm_tasklet, (unsigned long) d); + if (ohci1394_register_iso_tasklet(ohci, + &ohci->it_legacy_tasklet) < 0) { + PRINT(KERN_ERR, "No IT DMA context available"); + free_dma_trm_ctx(d); + return -EBUSY; + } + + /* IT can be assigned to any context by register_iso_tasklet */ + d->ctx = ohci->it_legacy_tasklet.context; + d->ctrlSet = OHCI1394_IsoXmitContextControlSet + 16 * d->ctx; + d->ctrlClear = OHCI1394_IsoXmitContextControlClear + 16 * d->ctx; + d->cmdPtr = OHCI1394_IsoXmitCommandPtr + 16 * d->ctx; + } else { + 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 * + ***********************************/ + +#define FAIL(err, fmt, args...) \ +do { \ + PRINT_G(KERN_ERR, fmt , ## args); \ + ohci1394_pci_remove(dev); \ + return err; \ +} while (0) + +static int __devinit ohci1394_pci_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + static int version_printed = 0; + + struct hpsb_host *host; + struct ti_ohci *ohci; /* shortcut to currently handled device */ + unsigned long ohci_base; + + if (version_printed++ == 0) + PRINT_G(KERN_INFO, "%s", version); + + if (pci_enable_device(dev)) + FAIL(-ENXIO, "Failed to enable OHCI hardware"); + pci_set_master(dev); + + host = hpsb_alloc_host(&ohci1394_driver, sizeof(struct ti_ohci), &dev->dev); + if (!host) FAIL(-ENOMEM, "Failed to allocate host structure"); + + 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, "Unexpected PCI resource length of %lx!", + pci_resource_len(dev, 0)); + + /* Seems PCMCIA handles this internally. Not sure why. Seems + * pretty bogus to force a driver to special case this. */ +#ifndef PCMCIA + if (!request_mem_region (ohci_base, OHCI1394_REGISTER_SIZE, OHCI1394_DRIVER_NAME)) + FAIL(-ENOMEM, "MMIO resource (0x%lx - 0x%lx) unavailable", + ohci_base, ohci_base + OHCI1394_REGISTER_SIZE); +#endif + ohci->init_state = OHCI_INIT_HAVE_MEM_REGION; + + ohci->registers = ioremap(ohci_base, OHCI1394_REGISTER_SIZE); + if (ohci->registers == NULL) + FAIL(-ENXIO, "Failed to remap registers - card not accessible"); + 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); + OHCI_DMA_ALLOC("consistent csr_config_rom"); + if (ohci->csr_config_rom_cpu == NULL) + FAIL(-ENOMEM, "Failed to allocate buffer config rom"); + 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); + OHCI_DMA_ALLOC("consistent selfid_buf"); + + if (ohci->selfid_buf_cpu == NULL) + FAIL(-ENOMEM, "Failed to allocate DMA buffer for self-id packets"); + 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) + FAIL(-ENOMEM, "Failed to allocate AR Req context"); + + /* 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) + FAIL(-ENOMEM, "Failed to allocate AR Resp context"); + + /* 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) + FAIL(-ENOMEM, "Failed to allocate AT Req context"); + + /* 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) + FAIL(-ENOMEM, "Failed to allocate AT Resp context"); + + /* 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); + DBGMSG("%d iso receive contexts available", + ohci->nb_iso_rcv_ctx); + + ohci->nb_iso_xmit_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); + DBGMSG("%d iso transmit contexts available", + ohci->nb_iso_xmit_ctx); + + /* 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); + + /* Allocate the IR DMA context right here so we don't have + * to do it in interrupt path - note that this doesn't + * waste much memory and avoids the jugglery required to + * allocate it in IRQ path. */ + if (alloc_dma_rcv_ctx(ohci, &ohci->ir_legacy_context, + DMA_CTX_ISO, 0, IR_NUM_DESC, + IR_BUF_SIZE, IR_SPLIT_BUF_SIZE, + OHCI1394_IsoRcvContextBase) < 0) { + FAIL(-ENOMEM, "Cannot allocate IR Legacy DMA context"); + } + + /* We hopefully don't have to pre-allocate IT DMA like we did + * for IR DMA above. Allocate it on-demand and mark inactive. */ + ohci->it_legacy_context.ohci = NULL; + + if (request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ, + OHCI1394_DRIVER_NAME, ohci)) + FAIL(-ENOMEM, "Failed to allocate shared interrupt %d", dev->irq); + + 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; + + /* Tell the highlevel this host is ready */ + if (hpsb_add_host(host)) + FAIL(-ENOMEM, "Failed to register host with highlevel"); + + ohci->init_state = OHCI_INIT_DONE; + + return 0; +#undef FAIL +} + +static void ohci1394_pci_remove(struct pci_dev *pdev) +{ + struct ti_ohci *ohci; + struct device *dev; + + ohci = pci_get_drvdata(pdev); + if (!ohci) + return; + + dev = get_device(&ohci->host->device); + + switch (ohci->init_state) { + case OHCI_INIT_DONE: + stop_dma_rcv_ctx(&ohci->ir_legacy_context); + 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(ohci->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 AR dma */ + free_dma_rcv_ctx(&ohci->ar_req_context); + free_dma_rcv_ctx(&ohci->ar_resp_context); + + /* Free AT dma */ + free_dma_trm_ctx(&ohci->at_req_context); + free_dma_trm_ctx(&ohci->at_resp_context); + + /* Free IR dma */ + free_dma_rcv_ctx(&ohci->ir_legacy_context); + + /* Free IT dma */ + free_dma_trm_ctx(&ohci->it_legacy_context); + + /* Free IR legacy dma */ + free_dma_rcv_ctx(&ohci->ir_legacy_context); + + + case OHCI_INIT_HAVE_SELFID_BUFFER: + pci_free_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, + ohci->selfid_buf_cpu, + ohci->selfid_buf_bus); + OHCI_DMA_FREE("consistent selfid_buf"); + + case OHCI_INIT_HAVE_CONFIG_ROM_BUFFER: + pci_free_consistent(ohci->dev, OHCI_CONFIG_ROM_LEN, + ohci->csr_config_rom_cpu, + ohci->csr_config_rom_bus); + OHCI_DMA_FREE("consistent csr_config_rom"); + + case OHCI_INIT_HAVE_IOMAPPING: + iounmap(ohci->registers); + + case OHCI_INIT_HAVE_MEM_REGION: +#ifndef PCMCIA + release_mem_region(pci_resource_start(ohci->dev, 0), + OHCI1394_REGISTER_SIZE); +#endif + +#ifdef CONFIG_PPC_PMAC + /* On UniNorth, power down the cable and turn off the chip + * clock when the module is removed to save power on + * laptops. Turning it back ON is done by the arch code when + * pci_enable_device() is called */ + { + struct device_node* of_node; + + of_node = pci_device_to_OF_node(ohci->dev); + if (of_node) { + pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0); + pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, of_node, 0, 0); + } + } +#endif /* CONFIG_PPC_PMAC */ + + case OHCI_INIT_ALLOC_HOST: + pci_set_drvdata(ohci->dev, NULL); + } + + if (dev) + put_device(dev); +} + + +static int ohci1394_pci_resume (struct pci_dev *pdev) +{ +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Re-enable 1394 */ + of_node = pci_device_to_OF_node (pdev); + if (of_node) + pmac_call_feature (PMAC_FTR_1394_ENABLE, of_node, 0, 1); + } +#endif + + pci_enable_device(pdev); + + return 0; +} + + +static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state) +{ +#ifdef CONFIG_PMAC_PBOOK + { + struct device_node *of_node; + + /* Disable 1394 */ + of_node = pci_device_to_OF_node (pdev); + if (of_node) + pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0); + } +#endif + + return 0; +} + + +#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10) + +static struct pci_device_id ohci1394_pci_tbl[] = { + { + .class = PCI_CLASS_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, + .resume = ohci1394_pci_resume, + .suspend = ohci1394_pci_suspend, +}; + + + +/*********************************** + * OHCI1394 Video Interface * + ***********************************/ + +/* essentially the only purpose of this code is to allow another + module to hook into ohci's interrupt handler */ + +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); |