2005-04-16 15:20:36 -07:00
/*
* 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 )
/* 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 ) ;
/* Put some defaults to these undefined bus options */
buf = reg_read ( ohci , OHCI1394_BusOptions ) ;
buf | = 0x60000000 ; /* Enable CMC and ISC */
2005-07-09 20:01:23 -04:00
if ( hpsb_disable_irm )
buf & = ~ 0x80000000 ;
else
2005-04-16 15:20:36 -07:00
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 ) ;
2005-07-09 20:01:23 -04:00
i = get_phy_reg ( ohci , 4 ) | PHY_04_LCTRL ;
if ( hpsb_disable_irm )
i & = ~ PHY_04_CONTENDER ;
else
i | = PHY_04_CONTENDER ;
set_phy_reg ( ohci , 4 , i ) ;
2005-04-16 15:20:36 -07:00
/* 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 ) ;
2005-04-21 14:09:42 -07:00
/* Initialize IR Legacy DMA channel mask */
2005-04-16 15:20:36 -07:00
ohci - > ir_legacy_channels = 0 ;
/*
* 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] "
2005-12-13 11:05:00 -05:00
" MMIO=[%lx-%lx] Max Packet=[%d] IR/IT contexts=[%d/%d] " ,
2005-04-16 15:20:36 -07:00
( ( ( ( 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 ,
2005-12-13 11:05:00 -05:00
ohci - > max_packet_size ,
ohci - > nb_iso_rcv_ctx , ohci - > nb_iso_xmit_ctx ) ;
2005-04-16 15:20:36 -07:00
/* 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 ;
2005-04-21 14:09:42 -07:00
struct dma_rcv_ctx * d = & ohci - > ir_legacy_context ;
int ir_legacy_active ;
2005-04-16 15:20:36 -07:00
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 ;
}
2005-04-21 14:09:42 -07:00
ir_legacy_active = ohci - > ir_legacy_channels ;
2005-04-16 15:20:36 -07:00
ohci - > ISO_channel_usage | = mask ;
ohci - > ir_legacy_channels | = mask ;
2005-04-21 14:09:42 -07:00
spin_unlock_irqrestore ( & ohci - > IR_channel_lock , flags ) ;
if ( ! ir_legacy_active ) {
if ( ohci1394_register_iso_tasklet ( ohci ,
& ohci - > ir_legacy_tasklet ) < 0 ) {
PRINT ( KERN_ERR , " No IR DMA context available " ) ;
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 ;
initialize_dma_rcv_ctx ( & ohci - > ir_legacy_context , 1 ) ;
2005-07-14 00:33:45 -07:00
if ( printk_ratelimit ( ) )
2005-09-30 11:59:19 -07:00
DBGMSG ( " IR legacy activated " ) ;
2005-04-21 14:09:42 -07:00
}
spin_lock_irqsave ( & ohci - > IR_channel_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ) ;
2005-04-21 14:09:42 -07:00
if ( ohci - > ir_legacy_channels = = 0 ) {
stop_dma_rcv_ctx ( & ohci - > ir_legacy_context ) ;
DBGMSG ( " ISO legacy receive context stopped " ) ;
}
2005-04-16 15:20:36 -07:00
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 ) ;
2005-04-21 14:09:42 -07:00
if ( ohci1394_register_iso_tasklet ( recv - > ohci , & recv - > task ) < 0 ) {
ret = - EBUSY ;
2005-04-16 15:20:36 -07:00
goto err ;
2005-04-21 14:09:42 -07:00
}
2005-04-16 15:20:36 -07:00
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 ] ;
2005-07-09 20:01:23 -04:00
/* ignore out-of-range requests */
if ( ( block < 0 ) | | ( block > recv - > nblocks ) )
return ;
2005-04-16 15:20:36 -07:00
/* 'next' becomes the new end of the DMA chain,
so disable branch and enable interrupt */
next - > branchAddress = 0 ;
next - > control | = cpu_to_le32 ( 3 < < 20 ) ;
next - > status = cpu_to_le32 ( recv - > buf_stride ) ;
/* link prev to next */
prev - > branchAddress = cpu_to_le32 ( dma_prog_region_offset_to_bus ( & recv - > prog ,
sizeof ( struct dma_cmd ) * next_i )
| 1 ) ; /* Z=1 */
/* disable interrupt on previous DMA descriptor, except at intervals */
if ( ( prev_i % recv - > block_irq_interval ) = = 0 ) {
prev - > control | = cpu_to_le32 ( 3 < < 20 ) ; /* enable interrupt */
} else {
prev - > control & = cpu_to_le32 ( ~ ( 3 < < 20 ) ) ; /* disable interrupt */
}
wmb ( ) ;
/* wake up DMA in case it fell asleep */
reg_write ( recv - > ohci , recv - > ContextControlSet , ( 1 < < 12 ) ) ;
}
static void ohci_iso_recv_bufferfill_release ( struct ohci_iso_recv * recv ,
struct hpsb_iso_packet_info * info )
{
/* release the memory where the packet was */
2005-07-09 20:01:23 -04:00
recv - > released_bytes + = info - > total_len ;
2005-04-16 15:20:36 -07:00
/* 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 ;
2005-07-09 20:01:23 -04:00
unsigned short len , cycle , total_len ;
2005-04-16 15:20:36 -07:00
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 ;
2005-07-09 20:01:23 -04:00
total_len = len + 8 ; /* 8 bytes header+trailer in OHCI packet */
2005-04-16 15:20:36 -07:00
/* payload is padded to 4 bytes */
if ( len % 4 ) {
recv - > dma_offset + = 4 - ( len % 4 ) ;
2005-07-09 20:01:23 -04:00
total_len + = 4 - ( len % 4 ) ;
2005-04-16 15:20:36 -07:00
}
/* 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 ;
}
2005-07-09 20:01:23 -04:00
hpsb_iso_packet_received ( iso , offset , len , total_len , cycle , channel , tag , sy ) ;
2005-04-16 15:20:36 -07:00
}
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 ;
2005-07-09 20:01:23 -04:00
hpsb_iso_packet_received ( iso , offset , packet_len ,
recv - > buf_stride , cycle , channel , tag , sy ) ;
2005-04-16 15:20:36 -07:00
}
/* 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 ) ;
2005-04-21 14:09:42 -07:00
if ( ohci1394_register_iso_tasklet ( xmit - > ohci , & xmit - > task ) < 0 ) {
ret = - EBUSY ;
2005-04-16 15:20:36 -07:00
goto err ;
2005-04-21 14:09:42 -07:00
}
2005-04-16 15:20:36 -07:00
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 ;
2005-10-19 21:23:46 -07:00
unsigned long flags ;
2005-04-16 15:20:36 -07:00
2005-10-19 21:23:46 -07:00
spin_lock_irqsave ( & ohci - > iso_tasklet_list_lock , flags ) ;
2005-04-16 15:20:36 -07:00
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 ) ;
}
2005-10-19 21:23:46 -07:00
spin_unlock_irqrestore ( & ohci - > iso_tasklet_list_lock , flags ) ;
2005-04-16 15:20:36 -07:00
}
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 ,
2005-04-21 14:09:42 -07:00
( cond_le32_to_cpu ( d - > spb [ 0 ] , ohci - > no_swap_incoming ) > > 10 ) & 0x3f ) ;
2005-04-16 15:20:36 -07:00
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= "
2005-04-21 14:09:42 -07:00
" %d ack=0x%X spd=%d dataLength=%d ctx=%d " ,
2005-04-16 15:20:36 -07:00
( 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= "
2005-04-21 14:09:42 -07:00
" %d ack=0x%X spd=%d data=0x%08X ctx=%d " ,
2005-04-16 15:20:36 -07:00
( 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 ) ;
}
2005-05-16 21:54:01 -07:00
kfree ( d - > spb ) ;
2005-04-16 15:20:36 -07:00
/* 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 ;
2005-11-07 06:31:45 -05:00
d - > buf_cpu = kzalloc ( d - > num_desc * sizeof ( * d - > buf_cpu ) , GFP_ATOMIC ) ;
d - > buf_bus = kzalloc ( d - > num_desc * sizeof ( * d - > buf_bus ) , GFP_ATOMIC ) ;
2005-04-16 15:20:36 -07:00
if ( d - > buf_cpu = = NULL | | d - > buf_bus = = NULL ) {
PRINT ( KERN_ERR , " Failed to allocate dma buffer " ) ;
free_dma_rcv_ctx ( d ) ;
return - ENOMEM ;
}
2005-11-07 06:31:45 -05:00
d - > prg_cpu = kzalloc ( d - > num_desc * sizeof ( * d - > prg_cpu ) , GFP_ATOMIC ) ;
d - > prg_bus = kzalloc ( d - > num_desc * sizeof ( * d - > prg_bus ) , GFP_ATOMIC ) ;
2005-04-16 15:20:36 -07:00
if ( d - > prg_cpu = = NULL | | d - > prg_bus = = NULL ) {
PRINT ( KERN_ERR , " Failed to allocate dma prg " ) ;
free_dma_rcv_ctx ( d ) ;
return - ENOMEM ;
}
d - > spb = kmalloc ( d - > split_buf_size , GFP_ATOMIC ) ;
if ( d - > spb = = NULL ) {
PRINT ( KERN_ERR , " Failed to allocate 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 ) ;
} 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 ;
2005-11-07 06:31:45 -05:00
d - > prg_cpu = kzalloc ( d - > num_desc * sizeof ( * d - > prg_cpu ) , GFP_KERNEL ) ;
d - > prg_bus = kzalloc ( d - > num_desc * sizeof ( * d - > prg_bus ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
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 ;
}
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 )
{
struct hpsb_host * host ;
struct ti_ohci * ohci ; /* shortcut to currently handled device */
unsigned long ohci_base ;
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 2 kb , 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 50 msec to make sure we have
* full link enabled . */
reg_write ( ohci , OHCI1394_HCControlSet , OHCI1394_HCControl_LPS ) ;
/* Disable and clear interrupts */
reg_write ( ohci , OHCI1394_IntEventClear , 0xffffffff ) ;
reg_write ( ohci , OHCI1394_IntMaskClear , 0xffffffff ) ;
mdelay ( 50 ) ;
/* Determine the number of available IR and IT contexts. */
ohci - > nb_iso_rcv_ctx =
get_nb_iso_ctx ( ohci , OHCI1394_IsoRecvIntMaskSet ) ;
ohci - > nb_iso_xmit_ctx =
get_nb_iso_ctx ( ohci , OHCI1394_IsoXmitIntMaskSet ) ;
/* Set the usage bits for non-existent contexts so they can't
* be allocated */
ohci - > ir_ctx_usage = ~ 0 < < ohci - > nb_iso_rcv_ctx ;
ohci - > it_ctx_usage = ~ 0 < < ohci - > nb_iso_xmit_ctx ;
INIT_LIST_HEAD ( & ohci - > iso_tasklet_list ) ;
spin_lock_init ( & ohci - > iso_tasklet_list_lock ) ;
ohci - > ISO_channel_usage = 0 ;
spin_lock_init ( & ohci - > IR_channel_lock ) ;
/* 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 ;
2005-08-25 23:13:14 +01:00
spin_lock_init ( & ohci - > event_lock ) ;
2005-04-16 15:20:36 -07:00
2005-08-25 23:13:14 +01:00
/*
* interrupts are disabled , all right , but . . . due to SA_SHIRQ we
* might get called anyway . We ' ll see no event , of course , but
* we need to get to that " no event " , so enough should be initialized
* by that point .
*/
2005-04-16 15:20:36 -07:00
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 :
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 )
{
2005-06-27 14:36:34 -07:00
# ifdef CONFIG_PPC_PMAC
if ( _machine = = _MACH_Pmac ) {
2005-04-16 15:20:36 -07:00
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 ) ;
}
2005-06-27 14:36:34 -07:00
# endif /* CONFIG_PPC_PMAC */
2005-04-16 15:20:36 -07:00
pci_enable_device ( pdev ) ;
return 0 ;
}
static int ohci1394_pci_suspend ( struct pci_dev * pdev , pm_message_t state )
{
2005-06-27 14:36:34 -07:00
# ifdef CONFIG_PPC_PMAC
if ( _machine = = _MACH_Pmac ) {
2005-04-16 15:20:36 -07:00
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 ) ;