📄 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#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...) do {} while (0)#define OHCI_DMA_FREE(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 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_ENDIANstatic 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; reg_write(ohci, reg, 0xffffffff); tmp = reg_read(ohci, reg);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -