?? ohci1394.c
字號:
/*
* ohci1394.c - driver for OHCI 1394 boards
* Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>
* Gord Peters <GordPeters@smarttech.com>
* 2001 Ben Collins <bcollins@debian.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* Things known to be working:
* . Async Request Transmit
* . Async Response Receive
* . Async Request Receive
* . Async Response Transmit
* . Iso Receive
* . DMA mmap for iso receive
* . Config ROM generation
*
* Things implemented, but still in test phase:
* . Iso Transmit
* . Async Stream Packets Transmit (Receive done via Iso interface)
*
* Things not implemented:
* . DMA error recovery
*
* Known bugs:
* . devctl BUS_RESET arg confusion (reset type or root holdoff?)
* added LONG_RESET_ROOT and SHORT_RESET_ROOT for root holdoff --kk
*/
/*
* Acknowledgments:
*
* Adam J Richter <adam@yggdrasil.com>
* . Use of pci_class to find device
*
* Emilie Chung <emilie.chung@axis.com>
* . Tip on Async Request Filter
*
* Pascal Drolet <pascal.drolet@informission.ca>
* . Various tips for optimization and functionnalities
*
* Robert Ficklin <rficklin@westengineering.com>
* . Loop in irq_handler
*
* James Goodwin <jamesg@Filanet.com>
* . Various tips on initialization, self-id reception, etc.
*
* Albrecht Dress <ad@mpifr-bonn.mpg.de>
* . Apple PowerBook detection
*
* Daniel Kobras <daniel.kobras@student.uni-tuebingen.de>
* . Reset the board properly before leaving + misc cleanups
*
* Leon van Stuivenberg <leonvs@iae.nl>
* . Bug fixes
*
* Ben Collins <bcollins@debian.org>
* . Working big-endian support
* . Updated to 2.4.x module scheme (PCI aswell)
* . Config ROM generation
*
* Manfred Weihs <weihs@ict.tuwien.ac.at>
* . Reworked code for initiating bus resets
* (long, short, with or without hold-off)
*
* Nandu Santhi <contactnandu@users.sourceforge.net>
* . Added support for nVidia nForce2 onboard Firewire chipset
*
*/
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <asm/byteorder.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/irq.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#ifdef CONFIG_PPC_PMAC
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "dma.h"
#include "iso.h"
#include "ieee1394_core.h"
#include "highlevel.h"
#include "ohci1394.h"
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
#define OHCI1394_DEBUG
#endif
#ifdef DBGMSG
#undef DBGMSG
#endif
#ifdef OHCI1394_DEBUG
#define DBGMSG(fmt, args...) \
printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
#else
#define DBGMSG(fmt, args...) do {} while (0)
#endif
/* print general (card independent) information */
#define PRINT_G(level, fmt, args...) \
printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args)
/* print card specific information */
#define PRINT(level, fmt, args...) \
printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
/* Module Parameters */
static int phys_dma = 1;
module_param(phys_dma, int, 0444);
MODULE_PARM_DESC(phys_dma, "Enable physical dma (default = 1).");
static void dma_trm_tasklet(unsigned long data);
static void dma_trm_reset(struct dma_trm_ctx *d);
static int alloc_dma_rcv_ctx(struct ti_ohci *ohci, struct dma_rcv_ctx *d,
enum context_type type, int ctx, int num_desc,
int buf_size, int split_buf_size, int context_base);
static void free_dma_rcv_ctx(struct dma_rcv_ctx *d);
static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
enum context_type type, int ctx, int num_desc,
int context_base);
static void ohci1394_pci_remove(struct pci_dev *pdev);
#ifndef __LITTLE_ENDIAN
static const size_t hdr_sizes[] = {
3, /* TCODE_WRITEQ */
4, /* TCODE_WRITEB */
3, /* TCODE_WRITE_RESPONSE */
0, /* reserved */
3, /* TCODE_READQ */
4, /* TCODE_READB */
3, /* TCODE_READQ_RESPONSE */
4, /* TCODE_READB_RESPONSE */
1, /* TCODE_CYCLE_START */
4, /* TCODE_LOCK_REQUEST */
2, /* TCODE_ISO_DATA */
4, /* TCODE_LOCK_RESPONSE */
/* rest is reserved or link-internal */
};
static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode)
{
size_t size;
if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes)))
return;
size = hdr_sizes[tcode];
while (size--)
data[size] = le32_to_cpu(data[size]);
}
#else
#define header_le32_to_cpu(w,x) do {} while (0)
#endif /* !LITTLE_ENDIAN */
/***********************************
* IEEE-1394 functionality section *
***********************************/
static u8 get_phy_reg(struct ti_ohci *ohci, u8 addr)
{
int i;
unsigned long flags;
quadlet_t r;
spin_lock_irqsave (&ohci->phy_reg_lock, flags);
reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | 0x00008000);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (reg_read(ohci, OHCI1394_PhyControl) & 0x80000000)
break;
mdelay(1);
}
r = reg_read(ohci, OHCI1394_PhyControl);
if (i >= OHCI_LOOP_COUNT)
PRINT (KERN_ERR, "Get PHY Reg timeout [0x%08x/0x%08x/%d]",
r, r & 0x80000000, i);
spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
return (r & 0x00ff0000) >> 16;
}
static void set_phy_reg(struct ti_ohci *ohci, u8 addr, u8 data)
{
int i;
unsigned long flags;
u32 r = 0;
spin_lock_irqsave (&ohci->phy_reg_lock, flags);
reg_write(ohci, OHCI1394_PhyControl, (addr << 8) | data | 0x00004000);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
r = reg_read(ohci, OHCI1394_PhyControl);
if (!(r & 0x00004000))
break;
mdelay(1);
}
if (i == OHCI_LOOP_COUNT)
PRINT (KERN_ERR, "Set PHY Reg timeout [0x%08x/0x%08x/%d]",
r, r & 0x00004000, i);
spin_unlock_irqrestore (&ohci->phy_reg_lock, flags);
return;
}
/* Or's our value into the current value */
static void set_phy_reg_mask(struct ti_ohci *ohci, u8 addr, u8 data)
{
u8 old;
old = get_phy_reg (ohci, addr);
old |= data;
set_phy_reg (ohci, addr, old);
return;
}
static void handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host,
int phyid, int isroot)
{
quadlet_t *q = ohci->selfid_buf_cpu;
quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount);
size_t size;
quadlet_t q0, q1;
/* Check status of self-id reception */
if (ohci->selfid_swap)
q0 = le32_to_cpu(q[0]);
else
q0 = q[0];
if ((self_id_count & 0x80000000) ||
((self_id_count & 0x00FF0000) != (q0 & 0x00FF0000))) {
PRINT(KERN_ERR,
"Error in reception of SelfID packets [0x%08x/0x%08x] (count: %d)",
self_id_count, q0, ohci->self_id_errors);
/* Tip by James Goodwin <jamesg@Filanet.com>:
* We had an error, generate another bus reset in response. */
if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) {
set_phy_reg_mask (ohci, 1, 0x40);
ohci->self_id_errors++;
} else {
PRINT(KERN_ERR,
"Too many errors on SelfID error reception, giving up!");
}
return;
}
/* SelfID Ok, reset error counter. */
ohci->self_id_errors = 0;
size = ((self_id_count & 0x00001FFC) >> 2) - 1;
q++;
while (size > 0) {
if (ohci->selfid_swap) {
q0 = le32_to_cpu(q[0]);
q1 = le32_to_cpu(q[1]);
} else {
q0 = q[0];
q1 = q[1];
}
if (q0 == ~q1) {
DBGMSG ("SelfID packet 0x%x received", q0);
hpsb_selfid_received(host, cpu_to_be32(q0));
if (((q0 & 0x3f000000) >> 24) == phyid)
DBGMSG ("SelfID for this node is 0x%08x", q0);
} else {
PRINT(KERN_ERR,
"SelfID is inconsistent [0x%08x/0x%08x]", q0, q1);
}
q += 2;
size -= 2;
}
DBGMSG("SelfID complete");
return;
}
static void ohci_soft_reset(struct ti_ohci *ohci) {
int i;
reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
for (i = 0; i < OHCI_LOOP_COUNT; i++) {
if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset))
break;
mdelay(1);
}
DBGMSG ("Soft reset finished");
}
/* Generate the dma receive prgs and start the context */
static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d, int generate_irq)
{
struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
int i;
ohci1394_stop_context(ohci, d->ctrlClear, NULL);
for (i=0; i<d->num_desc; i++) {
u32 c;
c = DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | DMA_CTL_BRANCH;
if (generate_irq)
c |= DMA_CTL_IRQ;
d->prg_cpu[i]->control = cpu_to_le32(c | d->buf_size);
/* End of descriptor list? */
if (i + 1 < d->num_desc) {
d->prg_cpu[i]->branchAddress =
cpu_to_le32((d->prg_bus[i+1] & 0xfffffff0) | 0x1);
} else {
d->prg_cpu[i]->branchAddress =
cpu_to_le32((d->prg_bus[0] & 0xfffffff0));
}
d->prg_cpu[i]->address = cpu_to_le32(d->buf_bus[i]);
d->prg_cpu[i]->status = cpu_to_le32(d->buf_size);
}
d->buf_ind = 0;
d->buf_offset = 0;
if (d->type == DMA_CTX_ISO) {
/* Clear contextControl */
reg_write(ohci, d->ctrlClear, 0xffffffff);
/* Set bufferFill, isochHeader, multichannel for IR context */
reg_write(ohci, d->ctrlSet, 0xd0000000);
/* Set the context match register to match on all tags */
reg_write(ohci, d->ctxtMatch, 0xf0000000);
/* Clear the multi channel mask high and low registers */
reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
/* Set up isoRecvIntMask to generate interrupts */
reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << d->ctx);
}
/* Tell the controller where the first AR program is */
reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1);
/* Run context */
reg_write(ohci, d->ctrlSet, 0x00008000);
DBGMSG("Receive DMA ctx=%d initialized", d->ctx);
}
/* Initialize the dma transmit context */
static void initialize_dma_trm_ctx(struct dma_trm_ctx *d)
{
struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
/* Stop the context */
ohci1394_stop_context(ohci, d->ctrlClear, NULL);
d->prg_ind = 0;
d->sent_ind = 0;
d->free_prgs = d->num_desc;
d->branchAddrPtr = NULL;
INIT_LIST_HEAD(&d->fifo_list);
INIT_LIST_HEAD(&d->pending_list);
if (d->type == DMA_CTX_ISO) {
/* enable interrupts */
reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << d->ctx);
}
DBGMSG("Transmit DMA ctx=%d initialized", d->ctx);
}
/* Count the number of available iso contexts */
static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
{
int i,ctx=0;
u32 tmp;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -