blob: e0d1172fc3e461982ba9fd2ac0b87a7dd3302f71 [file]
/*
* ohci1394.c - driver for OHCI 1394 boards
* Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
* Gord Peters <GordPeters@smarttech.com>
* 2001 Ben Collins <bcollins@debian.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Things known to be working:
* . Async Request Transmit
* . Async Response Receive
* . Async Request Receive
* . Async Response Transmit
* . Iso Receive
* . DMA mmap for iso receive
* . Config ROM generation
*
* Things implemented, but still in test phase:
* . Iso Transmit
*
* Things not implemented:
* . Async Stream Packets
* . DMA error recovery
*
* Known bugs:
* . Apple PowerBook detected but not working yet (still true?)
*/
/*
* Acknowledgments:
*
* Adam J Richter <adam@yggdrasil.com>
* . Use of pci_class to find device
*
* Andreas Tobler <toa@pop.agri.ch>
* . Updated proc_fs calls
*
* 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)
* . Removed procfs support since it trashes random mem
* . Config ROM generation
*/
/* Issues:
*
* - devctl BUS_RESET should treat arg as reset type
*
*/
#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/pci.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <asm/byteorder.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/tqueue.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/wrapper.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#ifdef CONFIG_ALL_PPC
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.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(card, fmt, args...) \
printk(KERN_INFO "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
#else
#define DBGMSG(card, 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)
u32 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, card, fmt, args...) \
printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
static char version[] __devinitdata =
"$Revision: 1.101 $ Ben Collins <bcollins@debian.org>";
/* Module Parameters */
MODULE_PARM(attempt_root,"i");
MODULE_PARM_DESC(attempt_root, "Attempt to make the host root.");
static int attempt_root = 0;
static void dma_trm_tasklet(unsigned long data);
static void dma_trm_reset(struct dma_trm_ctx *d);
static void __devexit ohci1394_pci_remove(struct pci_dev *pdev);
static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
quadlet_t isoRecvEvent,
quadlet_t isoXmitEvent);
#ifndef __LITTLE_ENDIAN
/* Swap a series of quads inplace. */
static __inline__ void block_swab32(quadlet_t *data, size_t size) {
while (size--)
data[size] = swab32(data[size]);
}
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, int len)
{
if (tcode > TCODE_LOCK_RESPONSE || hdr_sizes[tcode] == 0)
return;
block_swab32(data, hdr_sizes[tcode]);
}
#else
/* Don't waste cycles on same sex byte swaps */
#define packet_swab(w,x,y)
#define block_swab32(x,y)
#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, (((u16)addr << 8) & 0x00000f00) | 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, ohci->id, "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, 0x00004000 | (((u16)addr << 8) & 0x00000f00) | data);
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, ohci->id, "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;
mdelay(10);
/* 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, ohci->id,
"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, ohci->id,
"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 (ohci->id, "SelfID packet 0x%x received", q0);
hpsb_selfid_received(host, cpu_to_be32(q0));
if (((q0 & 0x3f000000) >> 24) == phyid)
DBGMSG (ohci->id, "SelfID for this node is 0x%08x", q0);
} else {
PRINT(KERN_ERR, ohci->id,
"SelfID is inconsistent [0x%08x/0x%08x]", q0, q1);
}
q += 2;
size -= 2;
}
DBGMSG(ohci->id, "SelfID complete");
hpsb_selfid_complete(host, phyid, isroot);
return;
}
static void ohci_soft_reset(struct ti_ohci *ohci) {
int i;
reg_write(ohci, OHCI1394_HCControlSet, 0x00010000);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (reg_read(ohci, OHCI1394_HCControlSet) & 0x00010000)
break;
mdelay(1);
}
/* Now reenable LPS, since that's usually what we want after a
* softreset anyway. Wait 50msec to make sure we have full link
* enabled. */
reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
mdelay(50);
DBGMSG (ohci->id, "Soft reset finished");
}
static int run_context(struct ti_ohci *ohci, int reg, char *msg)
{
u32 nodeId;
/* check that the node id is valid */
nodeId = reg_read(ohci, OHCI1394_NodeID);
if (!(nodeId&0x80000000)) {
PRINT(KERN_ERR, ohci->id,
"Running dma failed because Node ID is not valid");
return -1;
}
/* check that the node number != 63 */
if ((nodeId&0x3f)==63) {
PRINT(KERN_ERR, ohci->id,
"Running dma failed because Node ID == 63");
return -1;
}
/* Run the dma context */
reg_write(ohci, reg, 0x8000);
if (msg) PRINT(KERN_DEBUG, ohci->id, "%s", msg);
return 0;
}
/* 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;
/* Tell the controller where the first AR program is */
reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1);
/* Run AR context */
reg_write(ohci, d->ctrlSet, 0x00008000);
DBGMSG(ohci->id, "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;
d->fifo_first = NULL;
d->fifo_last = NULL;
d->pending_first = NULL;
d->pending_last = NULL;
DBGMSG(ohci->id, "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(ohci->id,"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;
}
static void ohci_init_config_rom(struct ti_ohci *ohci);
/* Global initialization */
static void ohci_initialize(struct ti_ohci *ohci)
{
quadlet_t buf;
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 */
buf &= ~0x00ff0000; /* XXX: Set cyc_clk_acc to zero for now */
buf &= ~0x98000000; /* Disable PMC, IRMC 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, 0x00040000);
/* Clear link control register */
reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
/* Enable cycle timer and cycle master */
reg_write(ohci, OHCI1394_LinkControlSet, 0x00300000);
/* Clear interrupt registers */
reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff);
reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff);
/* Set up self-id dma buffer */
reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus);
/* enable self-id dma */
reg_write(ohci, OHCI1394_LinkControlSet, 0x00000200);
/* Set the Config ROM mapping register */
reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus);
/* Initialize the Config ROM */
ohci_init_config_rom(ohci);
/* 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);
/* Set bufferFill, isochHeader, multichannel for IR context */
reg_write(ohci, OHCI1394_IsoRcvContextControlSet, 0xd0000000);
/* Set the context match register to match on all tags */
reg_write(ohci, OHCI1394_IsoRcvContextMatch, 0xf0000000);
/* 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);
/* Clear the multi channel mask high and low registers */
reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 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 dma */
initialize_dma_rcv_ctx(ohci->ir_context, 1);
/* Initialize IT dma */
initialize_dma_trm_ctx(ohci->it_context);
/* Set up isoRecvIntMask to generate interrupts for context 0
(thanks to Michael Greger for seeing that I forgot this) */
reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001);
/* Set up isoXmitIntMask to generate interrupts for context 0 */
reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 0x00000001);
/*
* 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, 0x40000000);
/* Enable interrupts */
reg_write(ohci, OHCI1394_IntMaskSet,
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, 0x00020000);
buf = reg_read(ohci, OHCI1394_Version);
PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d] "
"MMIO=[%lx-%lx] Max Packet=[%d]",
((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
pci_resource_start(ohci->dev, 0),
pci_resource_start(ohci->dev, 0) + pci_resource_len(ohci->dev, 0),
ohci->max_packet_size);
}
/*
* Insert a packet in the AT 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(ohci->id, "Inserting packet for node %d, tlabel=%d, tcode=0x%x, speed=%d",
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(ohci->id, "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] = packet->header[0];
d->prg_cpu[idx]->data[2] = packet->header[1];
} else {
d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
(packet->header[0] & 0xFFFF);
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,
packet->header_size>>2);
}
if (packet->data_size) { /* block transmit */
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.
*/
if (cross_bound((unsigned long)packet->data,
packet->data_size)>0) {
/* FIXME: do something about it */
PRINT(KERN_ERR, ohci->id,
"%s: packet data addr: %p size %Zd bytes "
"cross page boundary", __FUNCTION__,
packet->data, 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, 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, packet->header_size>>2);
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(ohci->id, "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 */
if (d->fifo_last) {
d->fifo_last->xnext = packet;
d->fifo_last = packet;
} else {
d->fifo_first = packet;
d->fifo_last = packet;
}
d->prg_ind = (d->prg_ind+1)%d->num_desc;
}
/*
* This function fills the AT FIFO with the (eventual) pending packets
* and runs or wakes up the AT DMA prg if necessary.
*
* The function MUST be called with the d->lock held.
*/
static int dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d)
{
int idx,z;
if (d->pending_first == NULL || d->free_prgs == 0)
return 0;
idx = d->prg_ind;
z = (d->pending_first->data_size) ? 3 : 2;
/* insert the packets into the at dma fifo */
while (d->free_prgs>0 && d->pending_first) {
insert_packet(ohci, d, d->pending_first);
d->pending_first = d->pending_first->xnext;
}
if (d->pending_first == NULL)
d->pending_last = NULL;
else
PRINT(KERN_INFO, ohci->id,
"Transmit DMA FIFO ctx=%d is full... waiting",d->ctx);
/* 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)) {
DBGMSG(ohci->id,"Starting transmit DMA ctx=%d",d->ctx);
reg_write(ohci, d->cmdPtr, d->prg_bus[idx]|z);
run_context(ohci, d->ctrlSet, NULL);
}
else {
/* Wake up the dma context if necessary */
if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
DBGMSG(ohci->id,"Waking transmit DMA ctx=%d",d->ctx);
reg_write(ohci, d->ctrlSet, 0x1000);
}
}
return 1;
}
/* Transmission of an async 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 char tcode;
unsigned long flags;
if (packet->data_size > ohci->max_packet_size) {
PRINT(KERN_ERR, ohci->id,
"Transmit packet size %Zd is too big",
packet->data_size);
return 0;
}
packet->xnext = NULL;
/* Decide wether we have an iso, a request, or a response packet */
tcode = (packet->header[0]>>4)&0xf;
if (tcode == TCODE_ISO_DATA) d = ohci->it_context;
else if (tcode & 0x02) d = ohci->at_resp_context;
else d = ohci->at_req_context;
spin_lock_irqsave(&d->lock,flags);
/* queue the packet for later insertion into the dma fifo */
if (d->pending_last) {
d->pending_last->xnext = packet;
d->pending_last = packet;
}
else {
d->pending_first = packet;
d->pending_last = packet;
}
dma_trm_flush(ohci, d);
spin_unlock_irqrestore(&d->lock,flags);
return 1;
}
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;
switch (cmd) {
case RESET_BUS:
DBGMSG(ohci->id, "devctl: Bus reset requested%s",
attempt_root ? " and attempting to become root" : "");
set_phy_reg_mask (ohci, 1, 0x40 | (attempt_root ? 0x80 : 0));
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, ohci->id, "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(ohci->id, "Cycle master enabled");
reg_write(ohci, OHCI1394_LinkControlSet,
0x00300000);
}
} else {
/* disable cycleTimer, cycleMaster, cycleSource */
reg_write(ohci, OHCI1394_LinkControlClear, 0x00700000);
}
break;
case CANCEL_REQUESTS:
DBGMSG(ohci->id, "Cancel request received");
dma_trm_reset(ohci->at_req_context);
dma_trm_reset(ohci->at_resp_context);
break;
case MODIFY_USAGE:
if (arg) {
MOD_INC_USE_COUNT;
} else {
MOD_DEC_USE_COUNT;
}
retval = 1;
break;
case ISO_LISTEN_CHANNEL:
{
u64 mask;
if (arg<0 || arg>63) {
PRINT(KERN_ERR, ohci->id,
"%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, ohci->id,
"%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;
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(ohci->id, "Listening enabled on channel %d", arg);
break;
}
case ISO_UNLISTEN_CHANNEL:
{
u64 mask;
if (arg<0 || arg>63) {
PRINT(KERN_ERR, ohci->id,
"%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, ohci->id,
"%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;
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(ohci->id, "Listening disabled on channel %d", arg);
break;
}
default:
PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet",
cmd);
break;
}
return retval;
}
/***************************************
* IEEE-1394 functionality section END *
***************************************/
/********************************************************
* Global stuff (interrupt handler, init/shutdown code) *
********************************************************/
static void dma_trm_reset(struct dma_trm_ctx *d)
{
struct ti_ohci *ohci;
unsigned long flags;
struct hpsb_packet *nextpacket;
if (d==NULL) {
PRINT_G(KERN_ERR, "dma_trm_reset called with NULL arg");
return;
}
ohci = (struct ti_ohci *)(d->ohci);
ohci1394_stop_context(ohci, d->ctrlClear, NULL);
spin_lock_irqsave(&d->lock,flags);
/* Is there still any packet pending in the fifo ? */
while(d->fifo_first) {
PRINT(KERN_INFO, ohci->id,
"AT dma reset ctx=%d, aborting transmission",
d->ctx);
nextpacket = d->fifo_first->xnext;
hpsb_packet_sent(ohci->host, d->fifo_first, ACKX_ABORTED);
d->fifo_first = nextpacket;
}
d->fifo_first = d->fifo_last = NULL;
/* is there still any packet pending ? */
while(d->pending_first) {
PRINT(KERN_INFO, ohci->id,
"AT dma reset ctx=%d, aborting transmission",
d->ctx);
nextpacket = d->pending_first->xnext;
hpsb_packet_sent(ohci->host, d->pending_first,
ACKX_ABORTED);
d->pending_first = nextpacket;
}
d->pending_first = d->pending_last = NULL;
d->branchAddrPtr=NULL;
d->sent_ind = d->prg_ind;
d->free_prgs = d->num_desc;
spin_unlock_irqrestore(&d->lock,flags);
}
static void 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;
DBGMSG(ohci->id, "IntEvent: %08x", event);
/* Die right here an now */
if (event & OHCI1394_unrecoverableError) {
PRINT(KERN_ERR, ohci->id, "Unrecoverable error, shutting down card!");
return;
}
if (event & OHCI1394_cycleInconsistent) {
/* We subscribe to the cycleInconsistent event only to
* clear the corresponding event bit... otherwise,
* isochronous cycleMatch DMA wont work. */
DBGMSG(ohci->id, "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->dev->vendor == PCI_VENDOR_ID_APPLE &&
ohci->dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
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);
}
}
spin_unlock_irqrestore(&ohci->event_lock, flags);
if (!host->in_bus_reset) {
DBGMSG(ohci->id, "irq_handler: Bus reset requested%s",
(attempt_root) ? " and attempting to become root"
: "");
/* Subsystem call */
hpsb_bus_reset(ohci->host);
}
event &= ~OHCI1394_busReset;
}
/* XXX: We need a way to also queue the OHCI1394_reqTxComplete,
* but for right now we simply run it upon reception, to make sure
* we get sent acks before response packets. This sucks mainly
* because it halts the interrupt handler. */
if (event & OHCI1394_reqTxComplete) {
struct dma_trm_ctx *d = ohci->at_req_context;
DBGMSG(ohci->id, "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);
event &= ~OHCI1394_reqTxComplete;
}
if (event & OHCI1394_respTxComplete) {
struct dma_trm_ctx *d = ohci->at_resp_context;
DBGMSG(ohci->id, "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(ohci->id, "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(ohci->id, "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 isoRecvIntEvent;
struct dma_rcv_ctx *d = ohci->ir_context;
isoRecvIntEvent =
reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
reg_write(ohci, OHCI1394_IsoRecvIntEventClear,
isoRecvIntEvent);
DBGMSG(ohci->id, "Got isochRx interrupt "
"status=0x%08X isoRecvIntEvent=%08x",
reg_read(ohci, d->ctrlSet), isoRecvIntEvent);
if (isoRecvIntEvent & 0x1) {
if (reg_read(ohci, d->ctrlSet) & 0x800)
ohci1394_stop_context(ohci, d->ctrlClear,
"isochRx");
else
tasklet_schedule(&d->task);
}
ohci1394_run_irq_hooks(ohci, isoRecvIntEvent, 0);
event &= ~OHCI1394_isochRx;
}
if (event & OHCI1394_isochTx) {
quadlet_t isoXmitIntEvent;
struct dma_trm_ctx *d = ohci->it_context;
isoXmitIntEvent =
reg_read(ohci, OHCI1394_IsoXmitIntEventSet);
reg_write(ohci, OHCI1394_IsoXmitIntEventClear,
isoXmitIntEvent);
DBGMSG(ohci->id, "Got isochTx interrupt "
"status=0x%08x isoXmitIntEvent=%08x",
reg_read(ohci, d->ctrlSet), isoXmitIntEvent);
ohci1394_run_irq_hooks(ohci, 0, isoXmitIntEvent);
if (isoXmitIntEvent & 0x1) {
if (reg_read(ohci, d->ctrlSet) & 0x800)
ohci1394_stop_context(ohci, d->ctrlClear, "isochTx");
else
tasklet_schedule(&d->task);
}
event &= ~OHCI1394_isochTx;
}
if (event & OHCI1394_selfIDComplete) {
if (host->in_bus_reset) {
node_id = reg_read(ohci, OHCI1394_NodeID);
/* If our nodeid is not valid, give a msec delay
* to let it settle in and try again. */
if (!(node_id & 0x80000000)) {
mdelay(1);
node_id = reg_read(ohci, OHCI1394_NodeID);
}
if (node_id & 0x80000000) { /* NodeID valid */
phyid = node_id & 0x0000003f;
isroot = (node_id & 0x40000000) != 0;
DBGMSG(ohci->id,
"SelfID interrupt received "
"(phyid %d, %s)", phyid,
(isroot ? "root" : "not root"));
handle_selfid(ohci, host,
phyid, isroot);
} else {
PRINT(KERN_ERR, ohci->id,
"SelfID interrupt received, but "
"NodeID is not valid: %08X",
node_id);
}
/* Accept Physical requests from all nodes. */
reg_write(ohci,OHCI1394_AsReqFilterHiSet,
0xffffffff);
reg_write(ohci,OHCI1394_AsReqFilterLoSet,
0xffffffff);
/* Turn on phys dma reception. We should
* probably manage the filtering somehow,
* instead of blindly turning it on. */
reg_write(ohci,OHCI1394_PhyReqFilterHiSet,
0xffffffff);
reg_write(ohci,OHCI1394_PhyReqFilterLoSet,
0xffffffff);
reg_write(ohci,OHCI1394_PhyUpperBound,
0xffff0000);
} else
PRINT(KERN_ERR, ohci->id,
"SelfID received outside of bus reset sequence");
/* Finally, we clear the busReset event and reenable
* 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);
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, ohci->id, "Unhandled interrupt(s) 0x%08x",
event);
}
/* 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(ohci->id, "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);
/* wake up the dma context if necessary */
if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
PRINT(KERN_INFO, ohci->id,
"Waking dma ctx=%d ... processing is probably too slow",
d->ctx);
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(ohci->id,"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, ohci->id,
"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(ohci->id,"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, (length - 4) >> 2);
DBGMSG(ohci->id, "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, ohci->id, "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, *nextpacket;
unsigned long flags;
u32 ack;
size_t datasize;
spin_lock_irqsave(&d->lock, flags);
if (d->fifo_first == NULL) {
#if 0
ohci1394_stop_context(ohci, d->ctrlClear,
"Packet sent ack received but queue is empty");
#endif
spin_unlock_irqrestore(&d->lock, flags);
return;
}
while (d->fifo_first) {
packet = d->fifo_first;
datasize = d->fifo_first->data_size;
if (datasize && packet->type != hpsb_raw)
ack = le32_to_cpu(
d->prg_cpu[d->sent_ind]->end.status) >> 16;
else
ack = le32_to_cpu(
d->prg_cpu[d->sent_ind]->begin.status) >> 16;
if (ack == 0)
/* this packet hasn't been sent yet*/
break;
if (!(ack & 0x10)) {
/* XXX: This is an OHCI evt_* code. We need to handle
* this specially! For right now, we just fake an
* ackx_send_error. */
PRINT(KERN_DEBUG, ohci->id, "Received OHCI evt_* error 0x%x",
ack & 0xf);
ack = (ack & 0xffe0) | ACK_BUSY_A;
}
#ifdef OHCI1394_DEBUG
if (datasize)
DBGMSG(ohci->id,
"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,
ack&0x1f, (ack>>5)&0x3,
le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3])
>>16,
d->ctx);
else
DBGMSG(ohci->id,
"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,
ack&0x1f, (ack>>5)&0x3,
le32_to_cpu(d->prg_cpu[d->sent_ind]->data[3]),
d->ctx);
#endif
nextpacket = packet->xnext;
hpsb_packet_sent(ohci->host, packet, ack & 0xf);
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++;
d->fifo_first = nextpacket;
}
if (d->fifo_first == NULL)
d->fifo_last = NULL;
dma_trm_flush(ohci, d);
spin_unlock_irqrestore(&d->lock, flags);
}
static int free_dma_rcv_ctx(struct dma_rcv_ctx **d)
{
int i;
struct ti_ohci *ohci;
if (*d==NULL) return -1;
ohci = (struct ti_ohci *)(*d)->ohci;
DBGMSG(ohci->id, "Freeing dma_rcv_ctx %d",(*d)->ctx);
ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL);
tasklet_kill(&(*d)->task);
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_free_consistent(
ohci->dev, sizeof(struct dma_cmd),
(*d)->prg_cpu[i], (*d)->prg_bus[i]);
OHCI_DMA_FREE("consistent dma_rcv prg[%d]", i);
}
kfree((*d)->prg_cpu);
kfree((*d)->prg_bus);
}
if ((*d)->spb) kfree((*d)->spb);
/* clear ISO context usage bit */
if ((*d)->type == DMA_CTX_ISO) {
clear_bit((*d)->ctx, &ohci->ir_ctx_usage);
}
kfree(*d);
*d = NULL;
return 0;
}
static struct dma_rcv_ctx *
alloc_dma_rcv_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
int buf_size, int split_buf_size, int context_base)
{
struct dma_rcv_ctx *d;
int i;
if (type == DMA_CTX_ISO) {
/* try to claim the ISO context usage bit */
if (test_and_set_bit(ctx, &ohci->ir_ctx_usage)) {
PRINT(KERN_ERR, ohci->id, "IR DMA context %d is not available", ctx);
return NULL;
}
}
d = kmalloc(sizeof(struct dma_rcv_ctx), GFP_KERNEL);
if (d == NULL) {
PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_rcv_ctx");
return NULL;
}
memset (d, 0, sizeof (struct dma_rcv_ctx));
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 = context_base + OHCI1394_ContextControlSet;
d->ctrlClear = context_base + OHCI1394_ContextControlClear;
d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL);
d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL);
if (d->buf_cpu == NULL || d->buf_bus == NULL) {
PRINT(KERN_ERR, ohci->id, "Failed to allocate dma buffer");
free_dma_rcv_ctx(&d);
return NULL;
}
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_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, ohci->id, "Failed to allocate dma prg");
free_dma_rcv_ctx(&d);
return NULL;
}
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_KERNEL);
if (d->spb == NULL) {
PRINT(KERN_ERR, ohci->id, "Failed to allocate split buffer");
free_dma_rcv_ctx(&d);
return NULL;
}
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, ohci->id,
"Failed to allocate dma buffer");
free_dma_rcv_ctx(&d);
return NULL;
}
d->prg_cpu[i] = pci_alloc_consistent(ohci->dev,
sizeof(struct dma_cmd),
d->prg_bus+i);
OHCI_DMA_ALLOC("consistent 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, ohci->id,
"Failed to allocate dma prg");
free_dma_rcv_ctx(&d);
return NULL;
}
}
spin_lock_init(&d->lock);
/* initialize tasklet */
tasklet_init (&d->task, dma_rcv_tasklet, (unsigned long)d);
return d;
}
static int free_dma_trm_ctx(struct dma_trm_ctx **d)
{
struct ti_ohci *ohci;
int i;
if (*d==NULL) return -1;
ohci = (struct ti_ohci *)(*d)->ohci;
DBGMSG(ohci->id, "Freeing dma_trm_ctx %d",(*d)->ctx);
ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL);
tasklet_kill(&(*d)->task);
if ((*d)->prg_cpu) {
for (i=0; i<(*d)->num_desc; i++)
if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) {
pci_free_consistent(
ohci->dev, sizeof(struct at_dma_prg),
(*d)->prg_cpu[i], (*d)->prg_bus[i]);
OHCI_DMA_FREE("consistent dma_trm prg[%d]", i);
}
kfree((*d)->prg_cpu);
kfree((*d)->prg_bus);
}
/* clear the ISO context usage bit */
if ((*d)->type == DMA_CTX_ISO) {
clear_bit((*d)->ctx, &ohci->it_ctx_usage);
}
kfree(*d);
*d = NULL;
return 0;
}
static struct dma_trm_ctx *
alloc_dma_trm_ctx(struct ti_ohci *ohci, enum context_type type, int ctx, int num_desc,
int context_base)
{
struct dma_trm_ctx *d;
int i;
if (type == DMA_CTX_ISO) {
/* try to claim the ISO context usage bit */
if (test_and_set_bit(ctx, &ohci->it_ctx_usage)) {
PRINT(KERN_ERR, ohci->id, "IT DMA context %d is not available", ctx);
return NULL;
}
}
d = kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL);
if (d == NULL) {
PRINT(KERN_ERR, ohci->id, "Failed to allocate dma_trm_ctx");
return NULL;
}
memset (d, 0, sizeof (struct dma_trm_ctx));
d->ohci = ohci;
d->type = type;
d->ctx = ctx;
d->num_desc = num_desc;
d->ctrlSet = context_base + OHCI1394_ContextControlSet;
d->ctrlClear = context_base + OHCI1394_ContextControlClear;
d->cmdPtr = context_base + OHCI1394_ContextCommandPtr;
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, ohci->id, "Failed to allocate at dma prg");
free_dma_trm_ctx(&d);
return NULL;
}
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));
for (i = 0; i < d->num_desc; i++) {
d->prg_cpu[i] = pci_alloc_consistent(ohci->dev,
sizeof(struct at_dma_prg),
d->prg_bus+i);
OHCI_DMA_ALLOC("consistent 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, ohci->id,
"Failed to allocate at dma prg");
free_dma_trm_ctx(&d);
return NULL;
}
}
spin_lock_init(&d->lock);
/* initialize bottom handler */
tasklet_init (&d->task, dma_trm_tasklet, (unsigned long)d);
return d;
}
static u16 ohci_crc16 (u32 *ptr, int length)
{
int shift;
u32 crc, sum, data;
crc = 0;
for (; length > 0; length--) {
data = *ptr++;
for (shift = 28; shift >= 0; shift -= 4) {
sum = ((crc >> 12) ^ (data >> shift)) & 0x000f;
crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
}
crc &= 0xffff;
}
return crc;
}
/* Config ROM macro implementation influenced by NetBSD OHCI driver */
struct config_rom_unit {
u32 *start;
u32 *refer;
int length;
int refunit;
};
struct config_rom_ptr {
u32 *data;
int unitnum;
struct config_rom_unit unitdir[10];
};
#define cf_put_1quad(cr, q) (((cr)->data++)[0] = cpu_to_be32(q))
#define cf_put_4bytes(cr, b1, b2, b3, b4) \
(((cr)->data++)[0] = cpu_to_be32(((b1) << 24) | ((b2) << 16) | ((b3) << 8) | (b4)))
#define cf_put_keyval(cr, key, val) (((cr)->data++)[0] = cpu_to_be32(((key) << 24) | (val)))
static inline void cf_put_str(struct config_rom_ptr *cr, const char *str)
{
int t;
char fourb[4];
while (str[0]) {
memset(fourb, 0, 4);
for (t = 0; t < 4 && str[t]; t++)
fourb[t] = str[t];
cf_put_4bytes(cr, fourb[0], fourb[1], fourb[2], fourb[3]);
str += strlen(str) < 4 ? strlen(str) : 4;
}
return;
}
static inline void cf_put_crc16(struct config_rom_ptr *cr, int unit)
{
*cr->unitdir[unit].start =
cpu_to_be32((cr->unitdir[unit].length << 16) |
ohci_crc16(cr->unitdir[unit].start + 1,
cr->unitdir[unit].length));
}
static inline void cf_unit_begin(struct config_rom_ptr *cr, int unit)
{
if (cr->unitdir[unit].refer != NULL) {
*cr->unitdir[unit].refer |=
cpu_to_be32 (cr->data - cr->unitdir[unit].refer);
cf_put_crc16(cr, cr->unitdir[unit].refunit);
}
cr->unitnum = unit;
cr->unitdir[unit].start = cr->data++;
}
static inline void cf_put_refer(struct config_rom_ptr *cr, char key, int unit)
{
cr->unitdir[unit].refer = cr->data;
cr->unitdir[unit].refunit = cr->unitnum;
(cr->data++)[0] = cpu_to_be32(key << 24);
}
static inline void cf_unit_end(struct config_rom_ptr *cr)
{
cr->unitdir[cr->unitnum].length = cr->data -
(cr->unitdir[cr->unitnum].start + 1);
cf_put_crc16(cr, cr->unitnum);
}
/* End of NetBSD derived code. */
static void ohci_init_config_rom(struct ti_ohci *ohci)
{
struct config_rom_ptr cr;
memset(&cr, 0, sizeof(cr));
memset(ohci->csr_config_rom_cpu, 0, sizeof (ohci->csr_config_rom_cpu));
cr.data = ohci->csr_config_rom_cpu;
/* Bus info block */
cf_unit_begin(&cr, 0);
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_BusID));
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_BusOptions));
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_GUIDHi));
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_GUIDLo));
cf_unit_end(&cr);
DBGMSG(ohci->id, "GUID: %08x:%08x", reg_read(ohci, OHCI1394_GUIDHi),
reg_read(ohci, OHCI1394_GUIDLo));
/* IEEE P1212 suggests the initial ROM header CRC should only
* cover the header itself (and not the entire ROM). Since we do
* this, then we can make our bus_info_len the same as the CRC
* length. */
ohci->csr_config_rom_cpu[0] |= cpu_to_be32(
(be32_to_cpu(ohci->csr_config_rom_cpu[0]) & 0x00ff0000) << 8);
reg_write(ohci, OHCI1394_ConfigROMhdr,
be32_to_cpu(ohci->csr_config_rom_cpu[0]));
/* Root directory */
cf_unit_begin(&cr, 1);
/* Vendor ID */
cf_put_keyval(&cr, 0x03, reg_read(ohci,OHCI1394_VendorID) & 0xFFFFFF);
cf_put_refer(&cr, 0x81, 2); /* Textual description unit */
cf_put_keyval(&cr, 0x0c, 0x0083c0); /* Node capabilities */
/* NOTE: Add other unit referers here, and append at bottom */
cf_unit_end(&cr);
/* Textual description - "Linux 1394" */
cf_unit_begin(&cr, 2);
cf_put_keyval(&cr, 0, 0);
cf_put_1quad(&cr, 0);
cf_put_str(&cr, "Linux OHCI-1394");
cf_unit_end(&cr);
ohci->csr_config_rom_length = cr.data - ohci->csr_config_rom_cpu;
}
static size_t ohci_get_rom(struct hpsb_host *host, const quadlet_t **ptr)
{
struct ti_ohci *ohci=host->hostdata;
DBGMSG(ohci->id, "request csr_rom address: %p",
ohci->csr_config_rom_cpu);
*ptr = ohci->csr_config_rom_cpu;
return ohci->csr_config_rom_length * 4;
}
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_operations ohci1394_ops = {
get_rom: ohci_get_rom,
transmit_packet: ohci_transmit,
devctl: ohci_devctl,
hw_csr_reg: ohci_hw_csr_reg,
};
static struct hpsb_host_driver *ohci1394_driver;
/***********************************
* 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 unsigned int card_id_counter = 0;
static int version_printed = 0;
struct hpsb_host *host;
struct ti_ohci *ohci; /* shortcut to currently handled device */
unsigned long ohci_base, ohci_len;
int i;
if (version_printed++ == 0)
PRINT_G(KERN_INFO, "%s", version);
if (pci_enable_device(dev))
FAIL(-ENXIO, "Failed to enable OHCI hardware %d",
card_id_counter++);
pci_set_master(dev);
host = hpsb_alloc_host(ohci1394_driver, sizeof(struct ti_ohci));
if (!host) FAIL(-ENOMEM, "Failed to allocate host structure");
ohci = host->hostdata;
ohci->id = card_id_counter++;
ohci->dev = dev;
ohci->host = 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
/* 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_base = pci_resource_start(dev, 0);
ohci_len = pci_resource_len(dev, 0);
if (!request_mem_region (ohci_base, ohci_len, OHCI1394_DRIVER_NAME))
FAIL(-ENOMEM, "MMIO resource (0x%lx@0x%lx) unavailable, aborting.",
ohci_base, ohci_len);
ohci->registers = ioremap(ohci_base, ohci_len);
if (ohci->registers == NULL)
FAIL(-ENXIO, "Failed to remap registers - card not accessible");
DBGMSG(ohci->id, "Remapped memory spaces reg 0x%p", ohci->registers);
/* Start off with a softreset, to clear everything to a sane
* state. This will also set Link Power State (LPS), which we
* need in order to start accessing most of the registers. */
ohci_soft_reset(ohci);
/* determinte the number of available IR and IT contexts right away,
because they need to be known for alloc_dma_*_ctx() */
ohci->nb_iso_rcv_ctx =
get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet);
DBGMSG(ohci->id, "%d iso receive contexts available",
ohci->nb_iso_rcv_ctx);
ohci->ir_ctx_usage = 0;
/* set the usage bits for non-existent contexts so they can't be allocated */
for(i = ohci->nb_iso_rcv_ctx; i < sizeof(ohci->ir_ctx_usage)*8; i++)
__set_bit(i, &ohci->ir_ctx_usage);
ohci->nb_iso_xmit_ctx =
get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet);
DBGMSG(ohci->id, "%d iso transmit contexts available",
ohci->nb_iso_xmit_ctx);
ohci->it_ctx_usage = 0;
/* set the usage bits for non-existent contexts so they can't be allocated */
for(i = ohci->nb_iso_xmit_ctx; i < sizeof(ohci->it_ctx_usage)*8; i++)
__set_bit(i, &ohci->it_ctx_usage);
/*
* 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");
if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff)
PRINT(KERN_INFO, ohci->id, "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;
/* AR DMA request context allocation */
ohci->ar_req_context =
alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AR_REQ_NUM_DESC,
AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
OHCI1394_AsReqRcvContextBase);
if (ohci->ar_req_context == NULL)
FAIL(-ENOMEM, "Failed to allocate AR Req context");
/* AR DMA response context allocation */
ohci->ar_resp_context =
alloc_dma_rcv_ctx(ohci, DMA_CTX_ASYNC_RESP, 0, AR_RESP_NUM_DESC,
AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
OHCI1394_AsRspRcvContextBase);
if (ohci->ar_resp_context == NULL)
FAIL(-ENOMEM, "Failed to allocate AR Resp context");
/* AT DMA request context */
ohci->at_req_context =
alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_REQ, 0, AT_REQ_NUM_DESC,
OHCI1394_AsReqTrContextBase);
if (ohci->at_req_context == NULL)
FAIL(-ENOMEM, "Failed to allocate AT Req context");
/* AT DMA response context */
ohci->at_resp_context =
alloc_dma_trm_ctx(ohci, DMA_CTX_ASYNC_RESP, 1, AT_RESP_NUM_DESC,
OHCI1394_AsRspTrContextBase);
if (ohci->at_resp_context == NULL)
FAIL(-ENOMEM, "Failed to allocate AT Resp context");
/* IR DMA context */
ohci->ir_context =
alloc_dma_rcv_ctx(ohci, DMA_CTX_ISO, 0, IR_NUM_DESC,
IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
OHCI1394_IsoRcvContextBase);
if (ohci->ir_context == NULL)
FAIL(-ENOMEM, "Failed to allocate IR context");
/* IT DMA context allocation */
ohci->it_context =
alloc_dma_trm_ctx(ohci, DMA_CTX_ISO, 0, IT_NUM_DESC,
OHCI1394_IsoXmitContextBase);
if (ohci->it_context == NULL)
FAIL(-ENOMEM, "Failed to allocate IT context");
ohci->ISO_channel_usage = 0;
spin_lock_init(&ohci->IR_channel_lock);
for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
ohci->irq_hooks[i].irq_handler = NULL;
ohci->irq_hooks[i].data = 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_initialize(ohci);
/* Tell the highlevel this host is ready */
hpsb_add_host(host);
return 0;
#undef FAIL
}
static void __devexit ohci1394_pci_remove(struct pci_dev *pdev)
{
struct ti_ohci *ohci;
quadlet_t buf;
ohci = pci_get_drvdata(pdev);
if (!ohci)
return;
if (ohci->host)
hpsb_remove_host(ohci->host);
/* Soft reset before we start */
ohci_soft_reset(ohci);
/* 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_context);
/* Free IT dma */
free_dma_trm_ctx(&ohci->it_context);
/* Disable all interrupts */
reg_write(ohci, OHCI1394_IntMaskClear, 0x80000000);
free_irq(ohci->dev->irq, ohci);
/* Free self-id buffer */
if (ohci->selfid_buf_cpu) {
pci_free_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE,
ohci->selfid_buf_cpu,
ohci->selfid_buf_bus);
OHCI_DMA_FREE("consistent selfid_buf");
}
/* Free config rom */
if (ohci->csr_config_rom_cpu) {
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");
}
/* Disable our bus options */
buf = reg_read(ohci, OHCI1394_BusOptions);
buf &= ~0xf8000000;
buf |= 0x00ff0000;
reg_write(ohci, OHCI1394_BusOptions, buf);
/* Clear LinkEnable and LPS */
reg_write(ohci, OHCI1394_HCControlClear, 0x000a0000);
if (ohci->registers)
iounmap(ohci->registers);
release_mem_region (pci_resource_start(ohci->dev, 0),
pci_resource_len(ohci->dev, 0));
#ifdef CONFIG_ALL_PPC
/* 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_ALL_PPC */
pci_set_drvdata(ohci->dev, NULL);
hpsb_unref_host(ohci->host);
}
#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = {
{
class: PCI_CLASS_FIREWIRE_OHCI,
class_mask: 0x00ffffff,
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: __devexit_p(ohci1394_pci_remove),
};
/***********************************
* OHCI1394 Video Interface *
***********************************/
/* essentially the only purpose of this code is to allow another
module to hook into ohci's interrupt handler */
void 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, ohci->id,
"Runaway loop while stopping context...");
break;
}
}
if (msg) PRINT(KERN_ERR, ohci->id, "%s: dma prg stopped", msg);
}
static inline void ohci1394_run_irq_hooks(struct ti_ohci *ohci,
quadlet_t isoRecvEvent,
quadlet_t isoXmitEvent)
{
int i;
for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
if(ohci->irq_hooks[i].irq_handler != NULL) {
ohci->irq_hooks[i].irq_handler(ohci->id, isoRecvEvent, isoXmitEvent,
ohci->irq_hooks[i].data);
}
}
}
int ohci1394_hook_irq(struct ti_ohci *ohci,
void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
void *data)
{
int i;
/* find a free slot */
for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
if(ohci->irq_hooks[i].irq_handler == NULL)
break;
}
if(i >= OHCI1394_MAX_IRQ_HOOKS)
return -EBUSY;
ohci->irq_hooks[i].irq_handler = irq_handler;
ohci->irq_hooks[i].data = data;
/* ohci1394 will never be unloaded while an IRQ hook is
in use, because the user must reference this symbol */
return 0;
}
void ohci1394_unhook_irq(struct ti_ohci *ohci,
void (*irq_handler) (int, quadlet_t, quadlet_t, void *),
void *data)
{
int i;
for(i = 0; i < OHCI1394_MAX_IRQ_HOOKS; i++) {
if( (ohci->irq_hooks[i].irq_handler == irq_handler) &&
(ohci->irq_hooks[i].data == data) )
break;
}
if(i < OHCI1394_MAX_IRQ_HOOKS) {
ohci->irq_hooks[i].irq_handler = NULL;
ohci->irq_hooks[i].data = NULL;
}
}
EXPORT_SYMBOL(ohci1394_stop_context);
EXPORT_SYMBOL(ohci1394_hook_irq);
EXPORT_SYMBOL(ohci1394_unhook_irq);
/***********************************
* 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);
hpsb_unregister_lowlevel(ohci1394_driver);
}
static int __init ohci1394_init(void)
{
int ret;
ohci1394_driver = hpsb_register_lowlevel(&ohci1394_ops,
OHCI1394_DRIVER_NAME);
if (!ohci1394_driver) {
PRINT_G(KERN_ERR, "hpsb_register_lowlevel failed");
return -ENOMEM;
}
ret = pci_module_init(&ohci1394_pci_driver);
if (ret < 0) {
PRINT_G(KERN_ERR, "pci_module_init failed");
hpsb_unregister_lowlevel(ohci1394_driver);
return ret;
}
return ret;
}
module_init(ohci1394_init);
module_exit(ohci1394_cleanup);