📄 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> * * 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 * * Things not implemented: * . Iso Transmit * . DMA error recovery * * Things to be fixed: * . Config ROM * * Known bugs: * . Self-id are sometimes not received properly * if card is initialized with no other nodes * on the bus * . Apple PowerBook detected but not working yet *//* * Acknowledgments: * * Adam J Richter <adam@yggdrasil.com> * . Use of pci_class to find device * Andreas Tobler <toa@pop.agri.ch> * . Updated proc_fs calls * Emilie Chung <emilie.chung@axis.com> * . Tip on Async Request Filter * Pascal Drolet <pascal.drolet@informission.ca> * . Various tips for optimization and functionnalities * Robert Ficklin <rficklin@westengineering.com> * . Loop in irq_handler * James Goodwin <jamesg@Filanet.com> * . Various tips on initialization, self-id reception, etc. * Albrecht Dress <ad@mpifr-bonn.mpg.de> * . Apple PowerBook detection * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> * . Reset the board properly before leaving + misc cleanups * Leon van Stuivenberg <leonvs@iae.nl> * . Bug fixes */#include <linux/config.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/wait.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/fs.h>#include <linux/poll.h>#include <asm/byteorder.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/uaccess.h>#include <linux/proc_fs.h>#include <linux/tqueue.h>#include <linux/delay.h>#include <asm/pgtable.h>#include <asm/page.h>#include <linux/sched.h>#include <asm/segment.h>#include <linux/types.h>#include <linux/wrapper.h>#include <linux/vmalloc.h>#include <linux/init.h>#include "ieee1394.h"#include "ieee1394_types.h"#include "hosts.h"#include "ieee1394_core.h"#include "ohci1394.h"#ifdef CONFIG_IEEE1394_VERBOSEDEBUG#define OHCI1394_DEBUG#endif#ifdef DBGMSG#undef DBGMSG#endif#ifdef OHCI1394_DEBUG#define DBGMSG(card, fmt, args...) \printk(KERN_INFO "ohci1394_%d: " fmt "\n" , card , ## args)#else#define DBGMSG(card, fmt, args...)#endif/* print general (card independent) information */#define PRINT_G(level, fmt, args...) \printk(level "ohci1394: " fmt "\n" , ## args)/* print card specific information */#define PRINT(level, card, fmt, args...) \printk(level "ohci1394_%d: " fmt "\n" , card , ## args)#define FAIL(fmt, args...) \ PRINT_G(KERN_ERR, fmt , ## args); \ num_of_cards--; \ remove_card(ohci); \ return 1;#if USE_DEVICEint supported_chips[][2] = { { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_LV22 }, { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_LV23 }, { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_LV26 }, { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_PCI4450 }, { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_OHCI1394 }, { PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SONY_CXD3222 }, { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72862 }, { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72870 }, { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72871 }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_FW }, { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_ALI_OHCI1394_M5251 }, { PCI_VENDOR_ID_LUCENT, PCI_DEVICE_ID_LUCENT_FW323 }, { -1, -1 }};#else#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)static struct pci_device_id ohci1394_pci_tbl[] __initdata = { { class: PCI_CLASS_FIREWIRE_OHCI, class_mask: 0x00ffffff, vendor: PCI_ANY_ID, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID, }, { 0, },};MODULE_DEVICE_TABLE(pci, ohci1394_pci_tbl);#endif#endif /* USE_DEVICE */MODULE_PARM(attempt_root,"i");static int attempt_root = 0;static struct ti_ohci cards[MAX_OHCI1394_CARDS];static int num_of_cards = 0;static int add_card(struct pci_dev *dev);static void remove_card(struct ti_ohci *ohci);static int init_driver(void);static void dma_trm_bh(void *data);static void dma_rcv_bh(void *data);static void dma_trm_reset(struct dma_trm_ctx *d);/*********************************** * IEEE-1394 functionality section * ***********************************/#if 0 /* not needed at this time */static int get_phy_reg(struct ti_ohci *ohci, int addr) { int timeout=10000; static quadlet_t r; if ((addr < 1) || (addr > 15)) { PRINT(KERN_ERR, ohci->id, __FUNCTION__ ": PHY register address %d out of range", addr); return -EFAULT; } spin_lock(&ohci->phy_reg_lock); /* initiate read request */ reg_write(ohci, OHCI1394_PhyControl, ((addr<<8)&0x00000f00) | 0x00008000); /* wait */ while (!(reg_read(ohci, OHCI1394_PhyControl)&0x80000000) && timeout) timeout--; if (!timeout) { PRINT(KERN_ERR, ohci->id, "get_phy_reg timeout !!!\n"); spin_unlock(&ohci->phy_reg_lock); return -EFAULT; } r = reg_read(ohci, OHCI1394_PhyControl); spin_unlock(&ohci->phy_reg_lock); return (r&0x00ff0000)>>16;}static int set_phy_reg(struct ti_ohci *ohci, int addr, unsigned char data) { int timeout=10000; u32 r; if ((addr < 1) || (addr > 15)) { PRINT(KERN_ERR, ohci->id, __FUNCTION__ ": PHY register address %d out of range", addr); return -EFAULT; } r = ((addr<<8)&0x00000f00) | 0x00004000 | ((u32)data & 0x000000ff); spin_lock(&ohci->phy_reg_lock); reg_write(ohci, OHCI1394_PhyControl, r); /* wait */ while (!(reg_read(ohci, OHCI1394_PhyControl)&0x80000000) && timeout) timeout--; spin_unlock(&ohci->phy_reg_lock); if (!timeout) { PRINT(KERN_ERR, ohci->id, "set_phy_reg timeout !!!\n"); return -EFAULT; } return 0;}#endif /* unneeded functions */inline static int 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 lsid; /* Self-id handling seems much easier than for the aic5800 chip. All the self-id packets, including this device own self-id, should be correctly arranged in the selfid buffer at this stage */ /* Check status of self-id reception */ if ((self_id_count&0x80000000) || ((self_id_count&0x00FF0000) != (q[0]&0x00FF0000))) { PRINT(KERN_ERR, ohci->id, "Error in reception of self-id packets" "Self-id count: %08x q[0]: %08x", self_id_count, q[0]); /* * Tip by James Goodwin <jamesg@Filanet.com>: * We had an error, generate another bus reset in response. * TODO. Actually read the current value in the phy before * generating a bus reset (read modify write). This way * we don't stomp any current gap count settings, etc. */ if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) { reg_write(ohci, OHCI1394_PhyControl, 0x000041ff); ohci->self_id_errors++; } else { PRINT(KERN_ERR, ohci->id, "Timeout on self-id error reception"); } return -1; } size = ((self_id_count&0x00001FFC)>>2) - 1; q++; while (size > 0) { if (q[0] == ~q[1]) { PRINT(KERN_INFO, ohci->id, "selfid packet 0x%x rcvd", q[0]); hpsb_selfid_received(host, cpu_to_be32(q[0])); if (((q[0]&0x3f000000)>>24)==phyid) { lsid=q[0]; PRINT(KERN_INFO, ohci->id, "This node self-id is 0x%08x", lsid); } } else { PRINT(KERN_ERR, ohci->id, "inconsistent selfid 0x%x/0x%x", q[0], q[1]); } q += 2; size -= 2; } PRINT(KERN_INFO, ohci->id, "calling self-id complete"); hpsb_selfid_complete(host, phyid, isroot); return 0;}static int ohci_detect(struct hpsb_host_template *tmpl){ struct hpsb_host *host; int i; init_driver(); for (i = 0; i < num_of_cards; i++) { host = hpsb_get_host(tmpl, 0); if (host == NULL) { /* simply don't init more after out of mem */ return i; } host->hostdata = &cards[i]; cards[i].host = host; } return num_of_cards;}static int ohci_soft_reset(struct ti_ohci *ohci) { int timeout=10000; reg_write(ohci, OHCI1394_HCControlSet, 0x00010000); while ((reg_read(ohci, OHCI1394_HCControlSet)&0x00010000) && timeout) timeout--; if (!timeout) { PRINT(KERN_ERR, ohci->id, "soft reset timeout !!!"); return -EFAULT; } else PRINT(KERN_INFO, ohci->id, "soft reset finished"); return 0;}static int run_context(struct ti_ohci *ohci, int reg, char *msg){ u32 nodeId; /* check that the node id is valid */ nodeId = reg_read(ohci, OHCI1394_NodeID); if (!(nodeId&0x80000000)) { PRINT(KERN_ERR, ohci->id, "Running dma failed because Node ID not valid"); return -1; } /* check that the node number != 63 */ if ((nodeId&0x3f)==63) { PRINT(KERN_ERR, ohci->id, "Running dma failed because Node ID == 63"); return -1; } /* Run the dma context */ reg_write(ohci, reg, 0x8000); if (msg) PRINT(KERN_INFO, ohci->id, "%s", msg); return 0;}/* Generate the dma receive prgs and start the context */static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d){ 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++) { /* end of descriptor list? */ if ((i+1) < d->num_desc) { d->prg_cpu[i]->control = (0x283C << 16) | d->buf_size; d->prg_cpu[i]->branchAddress = (d->prg_bus[i+1] & 0xfffffff0) | 0x1; } else { d->prg_cpu[i]->control = (0x283C << 16) | d->buf_size; d->prg_cpu[i]->branchAddress = d->prg_bus[0] & 0xfffffff0; } d->prg_cpu[i]->address = d->buf_bus[i]; d->prg_cpu[i]->status = d->buf_size; } d->buf_ind = 0; d->buf_offset = 0; /* Tell the controller where the first AR program is */ reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1); /* Run AR context */ reg_write(ohci, d->ctrlSet, 0x00008000); PRINT(KERN_INFO, ohci->id, "Receive DMA ctx=%d initialized", d->ctx);}/* Initialize the dma transmit context */static void initialize_dma_trm_ctx(struct dma_trm_ctx *d){ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); /* Stop the context */ ohci1394_stop_context(ohci, d->ctrlClear, NULL); d->prg_ind = 0; d->sent_ind = 0; d->free_prgs = d->num_desc; d->branchAddrPtr = NULL; d->fifo_first = NULL; d->fifo_last = NULL; d->pending_first = NULL; d->pending_last = NULL; PRINT(KERN_INFO, ohci->id, "AT dma ctx=%d initialized", d->ctx);}/* Count the number of available iso contexts */static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg){ int i,ctx=0; u32 tmp; reg_write(ohci, reg, 0xffffffff); tmp = reg_read(ohci, reg); DBGMSG(ohci->id,"Iso contexts reg: %08x implemented: %08x", reg, tmp); /* Count the number of contexts */ for(i=0; i<32; i++) { if(tmp & 1) ctx++; tmp >>= 1; } return ctx;}/* Global initialization */static int ohci_initialize(struct hpsb_host *host){ struct ti_ohci *ohci=host->hostdata; int retval, i; spin_lock_init(&ohci->phy_reg_lock); /* * Tip by James Goodwin <jamesg@Filanet.com>: * We need to add delays after the soft reset, setting LPS, and * enabling our link. This might fixes the self-id reception * problem at initialization. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -