/* * ohci1394.c - driver for OHCI 1394 boards * Copyright (C)1999,2000 Sebastien Rougeaux * Gord Peters * * 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: * * Emilie Chung * . Tip on Async Request Filter * Pascal Drolet * . Various tips for optimization and functionnalities * Robert Ficklin * . Loop in irq_handler * James Goodwin * . Various tips on initialization, self-id reception, etc. * Albrecht Dress * . Apple PowerBook detection * Daniel Kobras * . Reset the board properly before leaving + misc cleanups * Leon van Stuivenberg * . Bug fixes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; int 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_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 } }; 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 : * 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_errorsself_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; inum_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 : * 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. */ /* Soft reset */ if ((retval=ohci_soft_reset(ohci))<0) return retval; /* * Delay after soft reset to make sure everything has settled * down (sanity) */ mdelay(100); /* Set Link Power Status (LPS) */ reg_write(ohci, OHCI1394_HCControlSet, 0x00080000); /* * Delay after setting LPS in order to make sure link/phy * communication is established */ mdelay(100); /* Set the bus number */ reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); /* Enable posted writes */ reg_write(ohci, OHCI1394_HCControlSet, 0x00040000); /* Clear link control register */ reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff); /* Enable cycle timer and cycle master */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00300000); /* Clear interrupt registers */ reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); /* Set up self-id dma buffer */ reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); /* enable self-id dma */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00000200); /* Set the configuration ROM mapping register */ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); /* Set bus options */ reg_write(ohci, OHCI1394_BusOptions, cpu_to_be32(ohci->csr_config_rom_cpu[2])); #if 0 /* Write the GUID into the csr config rom */ ohci->csr_config_rom_cpu[3] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDHi)); ohci->csr_config_rom_cpu[4] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDLo)); #endif /* Write the config ROM header */ reg_write(ohci, OHCI1394_ConfigROMhdr, cpu_to_be32(ohci->csr_config_rom_cpu[0])); ohci->max_packet_size = 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); PRINT(KERN_INFO, ohci->id, "max packet size = %d bytes", ohci->max_packet_size); /* Don't accept phy packets into AR request context */ reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); /* Initialize IR dma */ ohci->nb_iso_rcv_ctx = get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); PRINT(KERN_INFO, ohci->id, "%d iso receive contexts available", ohci->nb_iso_rcv_ctx); for (i=0;inb_iso_rcv_ctx;i++) { reg_write(ohci, OHCI1394_IsoRcvContextControlClear+32*i, 0xffffffff); reg_write(ohci, OHCI1394_IsoRcvContextMatch+32*i, 0); reg_write(ohci, OHCI1394_IsoRcvCommandPtr+32*i, 0); } /* Set bufferFill, isochHeader, multichannel for IR context */ reg_write(ohci, OHCI1394_IsoRcvContextControlSet, 0xd0000000); /* Set the context match register to match on all tags */ reg_write(ohci, OHCI1394_IsoRcvContextMatch, 0xf0000000); /* Clear the interrupt mask */ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); /* Initialize IT dma */ ohci->nb_iso_xmit_ctx = get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); PRINT(KERN_INFO, ohci->id, "%d iso transmit contexts available", ohci->nb_iso_xmit_ctx); for (i=0;inb_iso_xmit_ctx;i++) { reg_write(ohci, OHCI1394_IsoXmitContextControlClear+32*i, 0xffffffff); reg_write(ohci, OHCI1394_IsoXmitCommandPtr+32*i, 0); } /* Clear the interrupt mask */ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); /* Clear the multi channel mask high and low registers */ reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff); reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff); /* Initialize AR dma */ initialize_dma_rcv_ctx(ohci->ar_req_context); initialize_dma_rcv_ctx(ohci->ar_resp_context); /* Initialize AT dma */ initialize_dma_trm_ctx(ohci->at_req_context); initialize_dma_trm_ctx(ohci->at_resp_context); /* Initialize IR dma */ initialize_dma_rcv_ctx(ohci->ir_context); /* Set up isoRecvIntMask to generate interrupts for context 0 (thanks to Michael Greger for seeing that I forgot this) */ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001); /* * 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)); #ifndef __BIG_ENDIAN reg_write(ohci, OHCI1394_HCControlClear, 0x40000000); #else reg_write(ohci, OHCI1394_HCControlSet, 0x40000000); #endif /* Enable interrupts */ reg_write(ohci, OHCI1394_IntMaskSet, OHCI1394_masterIntEnable | OHCI1394_phyRegRcvd | OHCI1394_busReset | OHCI1394_selfIDComplete | OHCI1394_RSPkt | OHCI1394_RQPkt | OHCI1394_ARRS | OHCI1394_ARRQ | OHCI1394_respTxComplete | OHCI1394_reqTxComplete | OHCI1394_isochRx | OHCI1394_isochTx ); /* Enable link */ reg_write(ohci, OHCI1394_HCControlSet, 0x00020000); return 1; } static void ohci_remove(struct hpsb_host *host) { struct ti_ohci *ohci; if (host != NULL) { ohci = host->hostdata; remove_card(ohci); } } /* * Insert a packet in the AT DMA fifo and generate the DMA prg * FIXME: rewrite the program in order to accept packets crossing * page boundaries. * check also that a single dma descriptor doesn't cross a * page boundary. */ static void insert_packet(struct ti_ohci *ohci, struct dma_trm_ctx *d, struct hpsb_packet *packet) { u32 cycleTimer; int idx = d->prg_ind; d->prg_cpu[idx]->begin.address = 0; d->prg_cpu[idx]->begin.branchAddress = 0; if (d->ctx==1) { /* * 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 = (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) | ((cycleTimer&0x01fff000)>>12); DBGMSG(ohci->id, "cycleTimer: %08x timeStamp: %08x", cycleTimer, d->prg_cpu[idx]->begin.status); } else d->prg_cpu[idx]->begin.status = 0; d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | (packet->header[0] & 0xFFFF); d->prg_cpu[idx]->data[1] = (packet->header[1] & 0xFFFF) | (packet->header[0] & 0xFFFF0000); d->prg_cpu[idx]->data[2] = packet->header[2]; d->prg_cpu[idx]->data[3] = packet->header[3]; if (packet->data_size) { /* block transmit */ d->prg_cpu[idx]->begin.control = OUTPUT_MORE_IMMEDIATE | 0x10; d->prg_cpu[idx]->end.control = OUTPUT_LAST | packet->data_size; /* * FIXME: check that the packet data buffer * do not cross a page boundary */ if (cross_bound((unsigned long)packet->data, packet->data_size)>0) { /* FIXME: do something about it */ PRINT(KERN_ERR, ohci->id, __FUNCTION__ ": packet data addr: %p size %d bytes " "cross page boundary", packet->data, packet->data_size); } d->prg_cpu[idx]->end.address = pci_map_single(ohci->dev, packet->data, packet->data_size, PCI_DMA_TODEVICE); d->prg_cpu[idx]->end.branchAddress = 0; d->prg_cpu[idx]->end.status = 0; if (d->branchAddrPtr) *(d->branchAddrPtr) = d->prg_bus[idx] | 0x3; d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress); } else { /* quadlet transmit */ d->prg_cpu[idx]->begin.control = OUTPUT_LAST_IMMEDIATE | packet->header_size; if (d->branchAddrPtr) *(d->branchAddrPtr) = d->prg_bus[idx] | 0x2; d->branchAddrPtr = &(d->prg_cpu[idx]->begin.branchAddress); } d->free_prgs--; /* queue the packet in the appropriate context queue */ if (d->fifo_last) { d->fifo_last->xnext = packet; d->fifo_last = packet; } else { d->fifo_first = packet; d->fifo_last = packet; } d->prg_ind = (d->prg_ind+1)%d->num_desc; } /* * This function fills the AT FIFO with the (eventual) pending packets * and runs or wake up the AT DMA prg if necessary. * The function MUST be called with the d->lock held. */ static int dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d) { int idx,z; if (d->pending_first == NULL || d->free_prgs == 0) return 0; idx = d->prg_ind; z = (d->pending_first->data_size) ? 3 : 2; /* insert the packets into the at dma fifo */ while (d->free_prgs>0 && d->pending_first) { insert_packet(ohci, d, d->pending_first); d->pending_first = d->pending_first->xnext; } if (d->pending_first == NULL) d->pending_last = NULL; else PRINT(KERN_INFO, ohci->id, "AT DMA FIFO ctx=%d full... waiting",d->ctx); /* Is the context running ? (should be unless it is the first packet to be sent in this context) */ if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { DBGMSG(ohci->id,"Starting AT DMA ctx=%d",d->ctx); reg_write(ohci, d->cmdPtr, d->prg_bus[idx]|z); run_context(ohci, d->ctrlSet, NULL); } else { DBGMSG(ohci->id,"Waking AT DMA ctx=%d",d->ctx); /* wake up the dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) reg_write(ohci, d->ctrlSet, 0x1000); } return 1; } /* * Transmission of an async packet */ static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) { struct ti_ohci *ohci = host->hostdata; struct dma_trm_ctx *d; unsigned char tcode; unsigned long flags; if (packet->data_size > ohci->max_packet_size) { PRINT(KERN_ERR, ohci->id, "transmit packet size = %d too big", packet->data_size); return 0; } packet->xnext = NULL; /* Decide wether we have a request or a response packet */ tcode = (packet->header[0]>>4)&0xf; if (tcode & 0x02) d = ohci->at_resp_context; else d = ohci->at_req_context; spin_lock_irqsave(&d->lock,flags); /* queue the packet for later insertion into to dma fifo */ if (d->pending_last) { d->pending_last->xnext = packet; d->pending_last = packet; } else { d->pending_first = packet; d->pending_last = packet; } dma_trm_flush(ohci, d); spin_unlock_irqrestore(&d->lock,flags); return 1; } static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) { struct ti_ohci *ohci = host->hostdata; int retval = 0; unsigned long flags; switch (cmd) { case RESET_BUS: /* * FIXME: this flag might be necessary in some case */ /* host->attempt_root = 1; */ PRINT(KERN_INFO, ohci->id, "resetting bus on request%s", (host->attempt_root ? " and attempting to become root" : "")); reg_write(ohci, OHCI1394_PhyControl, (host->attempt_root) ? 0x000041ff : 0x0000417f); break; case GET_CYCLE_COUNTER: retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer); break; case SET_CYCLE_COUNTER: reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg); break; case SET_BUS_ID: PRINT(KERN_ERR, ohci->id, "devctl command SET_BUS_ID err"); break; case ACT_CYCLE_MASTER: if (arg) { /* check if we are root and other nodes are present */ u32 nodeId = reg_read(ohci, OHCI1394_NodeID); if ((nodeId & (1<<30)) && (nodeId & 0x3f)) { /* * enable cycleTimer, cycleMaster */ DBGMSG(ohci->id, "Cycle master enabled"); reg_write(ohci, OHCI1394_LinkControlSet, 0x00300000); } } else { /* disable cycleTimer, cycleMaster, cycleSource */ reg_write(ohci, OHCI1394_LinkControlClear, 0x00700000); } break; case CANCEL_REQUESTS: DBGMSG(ohci->id, "Cancel request received"); dma_trm_reset(ohci->at_req_context); dma_trm_reset(ohci->at_resp_context); break; case MODIFY_USAGE: if (arg) { MOD_INC_USE_COUNT; } else { MOD_DEC_USE_COUNT; } break; case ISO_LISTEN_CHANNEL: { int *isochannels, offset= OHCI1394_IRMultiChanMaskLoSet; unsigned int channel= (unsigned int)arg; unsigned int channelbit= channel; u32 setMask= 0x00000001; /* save people from themselves */ if (channel > 63) break; if (channel > 31) { isochannels= &(((int*)&ohci->IR_channel_usage)[0]); channelbit-= 32; offset= OHCI1394_IRMultiChanMaskHiSet; } else isochannels= &(((int*)&ohci->IR_channel_usage)[1]); while(channelbit--) setMask= setMask << 1; spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (!test_and_set_bit(channelbit, isochannels)) reg_write(ohci, offset, setMask); spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); DBGMSG(ohci->id, "listening enabled on channel %u", channel); break; } case ISO_UNLISTEN_CHANNEL: { int *isochannels, offset= OHCI1394_IRMultiChanMaskLoClear; unsigned int channel= (unsigned int)arg; unsigned int channelbit= channel; u32 clearMask= 0x00000001; /* save people from themselves */ if (channel > 63) break; if (channel > 31) { isochannels= &(((int*)&ohci->IR_channel_usage)[0]); channelbit-= 32; offset= OHCI1394_IRMultiChanMaskHiClear; } else isochannels= &(((int*)&ohci->IR_channel_usage)[1]); while(channelbit--) clearMask= clearMask << 1; spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (!test_and_clear_bit(channelbit, isochannels)) reg_write(ohci, offset, clearMask); spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); DBGMSG(ohci->id, "listening disabled on channel %u", channel); break; } default: PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet", cmd); break; } return retval; } /*************************************** * IEEE-1394 functionality section END * ***************************************/ /******************************************************** * Global stuff (interrupt handler, init/shutdown code) * ********************************************************/ static void dma_trm_reset(struct dma_trm_ctx *d) { struct ti_ohci *ohci; unsigned long flags; struct hpsb_packet *nextpacket; if (d==NULL) { PRINT_G(KERN_ERR, "dma_trm_reset called with NULL arg"); return; } ohci = (struct ti_ohci *)(d->ohci); ohci1394_stop_context(ohci, d->ctrlClear, NULL); spin_lock_irqsave(&d->lock,flags); /* is there still any packet pending in the fifo ? */ while(d->fifo_first) { PRINT(KERN_INFO, ohci->id, "AT dma reset ctx=%d, aborting transmission", d->ctx); nextpacket = d->fifo_first->xnext; hpsb_packet_sent(ohci->host, d->fifo_first, ACKX_ABORTED); d->fifo_first = nextpacket; } d->fifo_first = d->fifo_last = NULL; /* is there still any packet pending ? */ while(d->pending_first) { PRINT(KERN_INFO, ohci->id, "AT dma reset ctx=%d, aborting transmission", d->ctx); nextpacket = d->pending_first->xnext; hpsb_packet_sent(ohci->host, d->pending_first, ACKX_ABORTED); d->pending_first = nextpacket; } d->pending_first = d->pending_last = NULL; d->branchAddrPtr=NULL; d->sent_ind = d->prg_ind; d->free_prgs = d->num_desc; spin_unlock_irqrestore(&d->lock,flags); } static void ohci_irq_handler(int irq, void *dev_id, struct pt_regs *regs_are_unused) { quadlet_t event,node_id; struct ti_ohci *ohci = (struct ti_ohci *)dev_id; struct hpsb_host *host = ohci->host; int phyid = -1, isroot = 0; int timeout = 255; do { /* read the interrupt event register */ event=reg_read(ohci, OHCI1394_IntEventClear); if (!event) return; DBGMSG(ohci->id, "IntEvent: %08x",event); /* clear the interrupt event register */ reg_write(ohci, OHCI1394_IntEventClear, event); if (event & OHCI1394_busReset) { if (!host->in_bus_reset) { PRINT(KERN_INFO, ohci->id, "Bus reset"); /* Wait for the AT fifo to be flushed */ dma_trm_reset(ohci->at_req_context); dma_trm_reset(ohci->at_resp_context); /* Subsystem call */ hpsb_bus_reset(ohci->host); ohci->NumBusResets++; } } /* * Problem: How can I ensure that the AT bottom half will be * executed before the AR bottom half (both events may have * occured within a single irq event) * Quick hack: just launch it within the IRQ handler */ if (event & OHCI1394_reqTxComplete) { struct dma_trm_ctx *d = ohci->at_req_context; DBGMSG(ohci->id, "Got reqTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "reqTxComplete"); else dma_trm_bh((void *)d); } if (event & OHCI1394_respTxComplete) { struct dma_trm_ctx *d = ohci->at_resp_context; DBGMSG(ohci->id, "Got respTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "respTxComplete"); else dma_trm_bh((void *)d); } if (event & OHCI1394_RQPkt) { struct dma_rcv_ctx *d = ohci->ar_req_context; DBGMSG(ohci->id, "Got RQPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt"); else { #if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else dma_rcv_bh((void *)d); #endif } } if (event & OHCI1394_RSPkt) { struct dma_rcv_ctx *d = ohci->ar_resp_context; DBGMSG(ohci->id, "Got RSPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt"); else { #if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else dma_rcv_bh((void *)d); #endif } } if (event & OHCI1394_isochRx) { quadlet_t isoRecvIntEvent; struct dma_rcv_ctx *d = ohci->ir_context; isoRecvIntEvent = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, isoRecvIntEvent); DBGMSG(ohci->id, "Got isochRx interrupt " "status=0x%08X isoRecvIntEvent=%08x", reg_read(ohci, d->ctrlSet), isoRecvIntEvent); if (isoRecvIntEvent & 0x1) { if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "isochRx"); else { #if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else dma_rcv_bh((void *)d); #endif } } if (ohci->video_tmpl) ohci->video_tmpl->irq_handler(ohci->id, isoRecvIntEvent, 0); } if (event & OHCI1394_isochTx) { quadlet_t isoXmitIntEvent; isoXmitIntEvent = reg_read(ohci, OHCI1394_IsoXmitIntEventSet); reg_write(ohci, OHCI1394_IsoXmitIntEventClear, isoXmitIntEvent); DBGMSG(ohci->id, "Got isochTx interrupt"); if (ohci->video_tmpl) ohci->video_tmpl->irq_handler(ohci->id, 0, isoXmitIntEvent); } if (event & OHCI1394_selfIDComplete) { if (host->in_bus_reset) { /* * Begin Fix (JSG): Check to make sure our * node id is valid */ node_id = reg_read(ohci, OHCI1394_NodeID); if (!(node_id & 0x80000000)) { mdelay(1); /* phy is upset - * this happens once in * a while on hot-plugs... * give it a ms to recover */ } /* End Fix (JSG) */ node_id = reg_read(ohci, OHCI1394_NodeID); if (node_id & 0x80000000) { /* NodeID valid */ phyid = node_id & 0x0000003f; isroot = (node_id & 0x40000000) != 0; PRINT(KERN_INFO, ohci->id, "SelfID process finished " "(phyid %d, %s)", phyid, (isroot ? "root" : "not root")); handle_selfid(ohci, host, phyid, isroot); } else PRINT(KERN_ERR, ohci->id, "SelfID process finished but " "NodeID not valid: %08X", node_id); /* Accept Physical requests from all nodes. */ reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0xffffffff); reg_write(ohci,OHCI1394_AsReqFilterLoSet, 0xffffffff); /* * Tip by James Goodwin * Turn on phys dma reception. We should * probably manage the filtering somehow, * instead of blindly turning it on. */ reg_write(ohci,OHCI1394_PhyReqFilterHiSet, 0xffffffff); reg_write(ohci,OHCI1394_PhyReqFilterLoSet, 0xffffffff); reg_write(ohci,OHCI1394_PhyUpperBound, 0xffff0000); } else PRINT(KERN_ERR, ohci->id, "self-id received outside of bus reset" "sequence"); } if (event & OHCI1394_phyRegRcvd) { #if 1 if (host->in_bus_reset) { PRINT(KERN_INFO, ohci->id, "PhyControl: %08X", reg_read(ohci, OHCI1394_PhyControl)); } else PRINT(KERN_ERR, ohci->id, "phy reg received outside of bus reset" "sequence"); #endif } } while (--timeout); PRINT(KERN_ERR, ohci->id, "irq_handler timeout event=0x%08x", event); } /* Put the buffer back into the dma context */ static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx) { struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); DBGMSG(ohci->id, "Inserting dma buf ctx=%d idx=%d", d->ctx, idx); d->prg_cpu[idx]->status = d->buf_size; d->prg_cpu[idx]->branchAddress &= 0xfffffff0; idx = (idx + d->num_desc - 1 ) % d->num_desc; d->prg_cpu[idx]->branchAddress |= 0x1; /* wake up the dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { PRINT(KERN_INFO, ohci->id, "Waking dma cxt=%d ... processing is probably too slow", d->ctx); reg_write(ohci, d->ctrlSet, 0x1000); } } static int block_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr, int offset) { int length=0; /* Where is the data length ? */ if (offset+12>=d->buf_size) length = (d->buf_cpu[(idx+1)%d->num_desc] [3-(d->buf_size-offset)/4]>>16); else length = (buf_ptr[3]>>16); if (length % 4) length += 4 - (length % 4); return length; } 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 */ static int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr, int offset) { unsigned char tcode; int length = -1; /* Let's see what kind of packet is in there */ tcode = (buf_ptr[0] >> 4) & 0xf; if (d->ctx < 2) { /* Async Receive Response/Request */ length = TCODE_SIZE[tcode]; if (length == 0) length = block_length(d, idx, buf_ptr, offset) + 20; } else if (d->ctx==2) { /* Iso receive */ /* Assumption: buffer fill mode with header/trailer */ length = (buf_ptr[0]>>16); if (length % 4) length += 4 - (length % 4); length+=8; } return length; } /* Bottom half that processes dma receive buffers */ static void dma_rcv_bh(void *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; quadlet_t *buf_ptr; char *split_ptr; char msg[256]; spin_lock(&d->lock); idx = d->buf_ind; offset = d->buf_offset; buf_ptr = d->buf_cpu[idx] + offset/4; rescount = d->prg_cpu[idx]->status&0xffff; bytes_left = d->buf_size - rescount - offset; while (bytes_left>0) { tcode = (buf_ptr[0]>>4)&0xf; length = packet_length(d, idx, buf_ptr, offset); if (length<4) { /* something is wrong */ sprintf(msg,"unexpected tcode 0x%X in AR ctx=%d", tcode, d->ctx); ohci1394_stop_context(ohci, d->ctrlClear, msg); spin_unlock(&d->lock); return; } if ((offset+length)>d->buf_size) { /* Split packet */ 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(&d->lock); return; } if (d->prg_cpu[(idx+1)%d->num_desc]->status ==d->buf_size) { /* other part of packet not written yet */ /* this should never happen I think */ /* anyway we'll get it on the next call */ PRINT(KERN_INFO, ohci->id, "Got only half a packet !!!"); d->buf_ind = idx; d->buf_offset = offset; spin_unlock(&d->lock); 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; } /* * We get one phy packet for each bus reset. * we know that from now on the bus topology may * have changed. Just ignore it for the moment */ if (tcode != 0xE) { DBGMSG(ohci->id, "Split packet received from" " node %d ack=0x%02X spd=%d tcode=0x%X" " length=%d data=0x%08x ctx=%d", (d->spb[1]>>16)&0x3f, (d->spb[length/4-1]>>16)&0x1f, (d->spb[length/4-1]>>21)&0x3, tcode, length, d->spb[3], d->ctx); ack = (((d->spb[length/4-1]>>16)&0x1f) == 0x11) ? 1 : 0; hpsb_packet_received(ohci->host, d->spb, length, ack); } else PRINT(KERN_INFO, ohci->id, "Got phy packet ctx=%d ... discarded", d->ctx); } else { /* * We get one phy packet for each bus reset. * we know that from now on the bus topology may * have changed. Just ignore it for the moment */ if (tcode != 0xE) { DBGMSG(ohci->id, "Packet received from node" " %d ack=0x%02X spd=%d tcode=0x%X" " length=%d data=0x%08x ctx=%d", (buf_ptr[1]>>16)&0x3f, (buf_ptr[length/4-1]>>16)&0x1f, (buf_ptr[length/4-1]>>21)&0x3, tcode, length, buf_ptr[3], d->ctx); ack = (((buf_ptr[length/4-1]>>16)&0x1f) == 0x11) ? 1 : 0; hpsb_packet_received(ohci->host, buf_ptr, length, ack); } else PRINT(KERN_INFO, ohci->id, "Got phy packet ctx=%d ... discarded", d->ctx); 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; } } rescount = d->prg_cpu[idx]->status & 0xffff; bytes_left = d->buf_size - rescount - offset; } d->buf_ind = idx; d->buf_offset = offset; spin_unlock(&d->lock); } /* Bottom half that processes sent packets */ static void dma_trm_bh(void *data) { struct dma_trm_ctx *d = (struct dma_trm_ctx*)data; struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); struct hpsb_packet *packet, *nextpacket; unsigned long flags; u32 ack; size_t datasize; spin_lock_irqsave(&d->lock, flags); if (d->fifo_first==NULL) { #if 0 ohci1394_stop_context(ohci, d->ctrlClear, "Packet sent ack received but queue is empty"); #endif spin_unlock_irqrestore(&d->lock, flags); return; } while (d->fifo_first) { packet = d->fifo_first; datasize = d->fifo_first->data_size; if (datasize) ack = d->prg_cpu[d->sent_ind]->end.status>>16; else ack = d->prg_cpu[d->sent_ind]->begin.status>>16; if (ack==0) /* this packet hasn't been sent yet*/ break; #ifdef OHCI1394_DEBUG if (datasize) DBGMSG(ohci->id, "Packet sent to node %d tcode=0x%X tLabel=" "0x%02X ack=0x%X spd=%d dataLength=%d ctx=%d", (d->prg_cpu[d->sent_ind]->data[1]>>16)&0x3f, (d->prg_cpu[d->sent_ind]->data[0]>>4)&0xf, (d->prg_cpu[d->sent_ind]->data[0]>>10)&0x3f, ack&0x1f, (ack>>5)&0x3, d->prg_cpu[d->sent_ind]->data[3]>>16, d->ctx); else DBGMSG(ohci->id, "Packet sent to node %d tcode=0x%X tLabel=" "0x%02X ack=0x%X spd=%d data=0x%08X ctx=%d", (d->prg_cpu[d->sent_ind]->data[1]>>16)&0x3f, (d->prg_cpu[d->sent_ind]->data[0]>>4)&0xf, (d->prg_cpu[d->sent_ind]->data[0]>>10)&0x3f, ack&0x1f, (ack>>5)&0x3, d->prg_cpu[d->sent_ind]->data[3], d->ctx); #endif nextpacket = packet->xnext; hpsb_packet_sent(ohci->host, packet, ack&0xf); if (datasize) pci_unmap_single(ohci->dev, d->prg_cpu[d->sent_ind]->end.address, datasize, PCI_DMA_TODEVICE); d->sent_ind = (d->sent_ind+1)%d->num_desc; d->free_prgs++; d->fifo_first = nextpacket; } if (d->fifo_first==NULL) d->fifo_last=NULL; dma_trm_flush(ohci, d); spin_unlock_irqrestore(&d->lock, flags); } static int free_dma_rcv_ctx(struct dma_rcv_ctx **d) { int i; struct ti_ohci *ohci; if (*d==NULL) return -1; ohci = (struct ti_ohci *)(*d)->ohci; DBGMSG(ohci->id, "Freeing dma_rcv_ctx %d",(*d)->ctx); ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); 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]); kfree((*d)->buf_cpu); kfree((*d)->buf_bus); } if ((*d)->prg_cpu) { for (i=0; i<(*d)->num_desc; i++) if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) pci_free_consistent( ohci->dev, sizeof(struct dma_cmd), (*d)->prg_cpu[i], (*d)->prg_bus[i]); kfree((*d)->prg_cpu); kfree((*d)->prg_bus); } if ((*d)->spb) kfree((*d)->spb); kfree(*d); *d = NULL; return 0; } static struct dma_rcv_ctx * alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, int buf_size, int split_buf_size, int ctrlSet, int ctrlClear, int cmdPtr) { struct dma_rcv_ctx *d=NULL; int i; d = (struct dma_rcv_ctx *)kmalloc(sizeof(struct dma_rcv_ctx), GFP_KERNEL); if (d==NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma_rcv_ctx"); return NULL; } d->ohci = (void *)ohci; d->ctx = ctx; d->num_desc = num_desc; d->buf_size = buf_size; d->split_buf_size = split_buf_size; d->ctrlSet = ctrlSet; d->ctrlClear = ctrlClear; d->cmdPtr = cmdPtr; d->buf_cpu = NULL; d->buf_bus = NULL; d->prg_cpu = NULL; d->prg_bus = NULL; d->spb = NULL; d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL); d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); if (d->buf_cpu == NULL || d->buf_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); free_dma_rcv_ctx(&d); return NULL; } memset(d->buf_cpu, 0, d->num_desc * sizeof(quadlet_t*)); memset(d->buf_bus, 0, d->num_desc * sizeof(dma_addr_t)); d->prg_cpu = kmalloc(d->num_desc * sizeof(struct dma_cmd*), GFP_KERNEL); d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); if (d->prg_cpu == NULL || d->prg_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); free_dma_rcv_ctx(&d); return NULL; } memset(d->prg_cpu, 0, d->num_desc * sizeof(struct dma_cmd*)); memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); d->spb = kmalloc(d->split_buf_size, GFP_KERNEL); if (d->spb == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate split buffer"); free_dma_rcv_ctx(&d); return NULL; } for (i=0; inum_desc; i++) { d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, d->buf_size, d->buf_bus+i); if (d->buf_cpu[i] != NULL) { memset(d->buf_cpu[i], 0, d->buf_size); } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); free_dma_rcv_ctx(&d); return NULL; } d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, sizeof(struct dma_cmd), d->prg_bus+i); if (d->prg_cpu[i] != NULL) { memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); free_dma_rcv_ctx(&d); return NULL; } } spin_lock_init(&d->lock); /* initialize bottom handler */ d->task.sync = 0; d->task.next = NULL; d->task.routine = dma_rcv_bh; d->task.data = (void*)d; return d; } static int free_dma_trm_ctx(struct dma_trm_ctx **d) { struct ti_ohci *ohci; int i; if (*d==NULL) return -1; ohci = (struct ti_ohci *)(*d)->ohci; DBGMSG(ohci->id, "Freeing dma_trm_ctx %d",(*d)->ctx); ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); if ((*d)->prg_cpu) { for (i=0; i<(*d)->num_desc; i++) if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) pci_free_consistent( ohci->dev, sizeof(struct at_dma_prg), (*d)->prg_cpu[i], (*d)->prg_bus[i]); kfree((*d)->prg_cpu); kfree((*d)->prg_bus); } kfree(*d); *d = NULL; return 0; } static struct dma_trm_ctx * alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, int ctrlSet, int ctrlClear, int cmdPtr) { struct dma_trm_ctx *d=NULL; int i; d = (struct dma_trm_ctx *)kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL); if (d==NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma_trm_ctx"); return NULL; } d->ohci = (void *)ohci; d->ctx = ctx; d->num_desc = num_desc; d->ctrlSet = ctrlSet; d->ctrlClear = ctrlClear; d->cmdPtr = cmdPtr; d->prg_cpu = NULL; d->prg_bus = NULL; d->prg_cpu = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), GFP_KERNEL); d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); if (d->prg_cpu == NULL || d->prg_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate at dma prg"); free_dma_trm_ctx(&d); return NULL; } memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*)); memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); for (i=0; inum_desc; i++) { d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, sizeof(struct at_dma_prg), d->prg_bus+i); if (d->prg_cpu[i] != NULL) { memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg)); } else { PRINT(KERN_ERR, ohci->id, "failed to allocate at dma prg"); free_dma_trm_ctx(&d); return NULL; } } spin_lock_init(&d->lock); /* initialize bottom handler */ d->task.routine = dma_trm_bh; d->task.data = (void*)d; return d; } static u32 ohci_crc16(unsigned *data, int length) { int check=0, i; int shift, sum, next=0; for (i = length; i; i--) { for (next = check, shift = 28; shift >= 0; shift -= 4 ) { sum = ((next >> 12) ^ (*data >> shift)) & 0xf; next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); } check = next & 0xffff; data++; } return check; } static void ohci_init_config_rom(struct ti_ohci *ohci) { int i; ohci_csr_rom[3] = reg_read(ohci, OHCI1394_GUIDHi); ohci_csr_rom[4] = reg_read(ohci, OHCI1394_GUIDLo); ohci_csr_rom[0] = 0x04040000 | ohci_crc16(ohci_csr_rom+1, 4); for (i=0;icsr_config_rom_cpu[i] = cpu_to_be32(ohci_csr_rom[i]); } static int add_card(struct pci_dev *dev) { struct ti_ohci *ohci; /* shortcut to currently handled device */ if (num_of_cards == MAX_OHCI1394_CARDS) { PRINT_G(KERN_WARNING, "cannot handle more than %d cards. " "Adjust MAX_OHCI1394_CARDS in ti_ohci1394.h.", MAX_OHCI1394_CARDS); return 1; } if (pci_enable_device(dev)) { PRINT_G(KERN_NOTICE, "failed to enable OHCI hardware %d", num_of_cards); return 1; } pci_set_master(dev); ohci = &cards[num_of_cards++]; ohci->id = num_of_cards-1; ohci->dev = dev; ohci->state = 0; /* csr_config rom allocation */ ohci->csr_config_rom_cpu = pci_alloc_consistent(ohci->dev, sizeof(ohci_csr_rom), &ohci->csr_config_rom_bus); if (ohci->csr_config_rom_cpu == NULL) { FAIL("failed to allocate buffer config rom"); } /* * self-id dma buffer allocation * FIXME: some early chips may need 8KB alignment for the * selfid buffer... if you have problems a temporary fic * is to allocate 8192 bytes instead of 2048 */ ohci->selfid_buf_cpu = pci_alloc_consistent(ohci->dev, 2048, &ohci->selfid_buf_bus); if (ohci->selfid_buf_cpu == NULL) { FAIL("failed to allocate DMA buffer for self-id packets"); } if ((unsigned long)ohci->selfid_buf_cpu & 0x1fff) PRINT(KERN_INFO, ohci->id, "Selfid buffer %p not aligned on " "8Kb boundary... may cause pb on some CXD3222 chip", ohci->selfid_buf_cpu); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) ohci->registers = ioremap_nocache(dev->base_address[0], OHCI1394_REGISTER_SIZE); #else ohci->registers = ioremap_nocache(dev->resource[0].start, OHCI1394_REGISTER_SIZE); #endif if (ohci->registers == NULL) { FAIL("failed to remap registers - card not accessible"); } PRINT(KERN_INFO, ohci->id, "remapped memory spaces reg 0x%p", ohci->registers); ohci->ar_req_context = alloc_dma_rcv_ctx(ohci, 0, AR_REQ_NUM_DESC, AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE, OHCI1394_AsReqRcvContextControlSet, OHCI1394_AsReqRcvContextControlClear, OHCI1394_AsReqRcvCommandPtr); if (ohci->ar_req_context == NULL) { FAIL("failed to allocate AR Req context"); } ohci->ar_resp_context = alloc_dma_rcv_ctx(ohci, 1, AR_RESP_NUM_DESC, AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE, OHCI1394_AsRspRcvContextControlSet, OHCI1394_AsRspRcvContextControlClear, OHCI1394_AsRspRcvCommandPtr); if (ohci->ar_resp_context == NULL) { FAIL("failed to allocate AR Resp context"); } ohci->at_req_context = alloc_dma_trm_ctx(ohci, 0, AT_REQ_NUM_DESC, OHCI1394_AsReqTrContextControlSet, OHCI1394_AsReqTrContextControlClear, OHCI1394_AsReqTrCommandPtr); if (ohci->at_req_context == NULL) { FAIL("failed to allocate AT Req context"); } ohci->at_resp_context = alloc_dma_trm_ctx(ohci, 1, AT_RESP_NUM_DESC, OHCI1394_AsRspTrContextControlSet, OHCI1394_AsRspTrContextControlClear, OHCI1394_AsRspTrCommandPtr); if (ohci->at_resp_context == NULL) { FAIL("failed to allocate AT Resp context"); } ohci->ir_context = alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC, IR_BUF_SIZE, IR_SPLIT_BUF_SIZE, OHCI1394_IsoRcvContextControlSet, OHCI1394_IsoRcvContextControlClear, OHCI1394_IsoRcvCommandPtr); if (ohci->ir_context == NULL) { FAIL("failed to allocate IR context"); } ohci->IR_channel_usage= 0x0000000000000000; spin_lock_init(&ohci->IR_channel_lock); if (!request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ, OHCI1394_DRIVER_NAME, ohci)) { PRINT(KERN_INFO, ohci->id, "allocated interrupt %d", dev->irq); } else { FAIL("failed to allocate shared interrupt %d", dev->irq); } ohci_init_config_rom(ohci); DBGMSG(ohci->id, "The 1st byte at offset 0x404 is: 0x%02x", *((char *)ohci->csr_config_rom_cpu+4)); return 0; #undef FAIL } #ifdef CONFIG_PROC_FS #define SR(fmt, reg0, reg1, reg2)\ p += sprintf(p,fmt,reg_read(ohci, reg0),\ reg_read(ohci, reg1),reg_read(ohci, reg2)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) static int ohci_get_status(char *buf) #else int ohci_get_info(char *buf, char **start, off_t fpos, int length, int dummy) #endif { struct ti_ohci *ohci=&cards[0]; struct hpsb_host *host=ohci->host; char *p=buf; //unsigned char phyreg; //int i, nports; int i; struct dma_rcv_ctx *d=NULL; struct dma_trm_ctx *dt=NULL; p += sprintf(p,"IEEE-1394 OHCI Driver status report:\n"); p += sprintf(p," bus number: 0x%x Node ID: 0x%x\n", (reg_read(ohci, OHCI1394_NodeID) & 0xFFC0) >> 6, reg_read(ohci, OHCI1394_NodeID)&0x3f); #if 0 p += sprintf(p," hardware version %d.%d GUID_ROM is %s\n\n", (reg_read(ohci, OHCI1394_Version) & 0xFF0000) >>16, reg_read(ohci, OHCI1394_Version) & 0xFF, (reg_read(ohci, OHCI1394_Version) & 0x01000000) ? "set" : "clear"); #endif p += sprintf(p,"\n### Host data ###\n"); p += sprintf(p,"node_count: %8d ",host->node_count); p += sprintf(p,"node_id : %08X\n",host->node_id); p += sprintf(p,"irm_id : %08X ",host->irm_id); p += sprintf(p,"busmgr_id : %08X\n",host->busmgr_id); p += sprintf(p,"%s %s %s\n", host->initialized ? "initialized" : "", host->in_bus_reset ? "in_bus_reset" : "", host->attempt_root ? "attempt_root" : ""); p += sprintf(p,"%s %s %s %s\n", host->is_root ? "root" : "", host->is_cycmst ? "cycle_master" : "", host->is_irm ? "iso_res_mgr" : "", host->is_busmgr ? "bus_mgr" : ""); p += sprintf(p,"\n---Iso Receive DMA---\n"); d = ohci->ir_context; #if 0 for (i=0; inum_desc; i++) { p += sprintf(p, "IR buf[%d] : %p prg[%d]: %p\n", i, d->buf[i], i, d->prg[i]); } #endif p += sprintf(p, "Current buf: %d offset: %d\n", d->buf_ind,d->buf_offset); p += sprintf(p,"\n---Async Receive DMA---\n"); d = ohci->ar_req_context; #if 0 for (i=0; inum_desc; i++) { p += sprintf(p, "AR req buf[%d] : %p prg[%d]: %p\n", i, d->buf[i], i, d->prg[i]); } #endif p += sprintf(p, "Ar req current buf: %d offset: %d\n", d->buf_ind,d->buf_offset); d = ohci->ar_resp_context; #if 0 for (i=0; inum_desc; i++) { p += sprintf(p, "AR resp buf[%d] : %p prg[%d]: %p\n", i, d->buf[i], i, d->prg[i]); } #endif p += sprintf(p, "AR resp current buf: %d offset: %d\n", d->buf_ind,d->buf_offset); p += sprintf(p,"\n---Async Transmit DMA---\n"); dt = ohci->at_req_context; p += sprintf(p, "AT req prg: %d sent: %d free: %d branchAddrPtr: %p\n", dt->prg_ind, dt->sent_ind, dt->free_prgs, dt->branchAddrPtr); p += sprintf(p, "AT req queue: first: %p last: %p\n", dt->fifo_first, dt->fifo_last); dt = ohci->at_resp_context; #if 0 for (i=0; inum_desc; i++) { p += sprintf(p, "------- AT resp prg[%02d] ------\n",i); p += sprintf(p, "%p: control : %08x\n", &(dt->prg[i].begin.control), dt->prg[i].begin.control); p += sprintf(p, "%p: address : %08x\n", &(dt->prg[i].begin.address), dt->prg[i].begin.address); p += sprintf(p, "%p: brancAddr: %08x\n", &(dt->prg[i].begin.branchAddress), dt->prg[i].begin.branchAddress); p += sprintf(p, "%p: status : %08x\n", &(dt->prg[i].begin.status), dt->prg[i].begin.status); p += sprintf(p, "%p: header[0]: %08x\n", &(dt->prg[i].data[0]), dt->prg[i].data[0]); p += sprintf(p, "%p: header[1]: %08x\n", &(dt->prg[i].data[1]), dt->prg[i].data[1]); p += sprintf(p, "%p: header[2]: %08x\n", &(dt->prg[i].data[2]), dt->prg[i].data[2]); p += sprintf(p, "%p: header[3]: %08x\n", &(dt->prg[i].data[3]), dt->prg[i].data[3]); p += sprintf(p, "%p: control : %08x\n", &(dt->prg[i].end.control), dt->prg[i].end.control); p += sprintf(p, "%p: address : %08x\n", &(dt->prg[i].end.address), dt->prg[i].end.address); p += sprintf(p, "%p: brancAddr: %08x\n", &(dt->prg[i].end.branchAddress), dt->prg[i].end.branchAddress); p += sprintf(p, "%p: status : %08x\n", &(dt->prg[i].end.status), dt->prg[i].end.status); } #endif p += sprintf(p, "AR resp prg: %d sent: %d free: %d" " branchAddrPtr: %p\n", dt->prg_ind, dt->sent_ind, dt->free_prgs, dt->branchAddrPtr); p += sprintf(p, "AT resp queue: first: %p last: %p\n", dt->fifo_first, dt->fifo_last); /* ----- Register Dump ----- */ p += sprintf(p,"\n### HC Register dump ###\n"); SR("Version : %08x GUID_ROM : %08x ATRetries : %08x\n", OHCI1394_Version, OHCI1394_GUID_ROM, OHCI1394_ATRetries); SR("CSRData : %08x CSRCompData : %08x CSRControl : %08x\n", OHCI1394_CSRData, OHCI1394_CSRCompareData, OHCI1394_CSRControl); SR("ConfigROMhdr: %08x BusID : %08x BusOptions : %08x\n", OHCI1394_ConfigROMhdr, OHCI1394_BusID, OHCI1394_BusOptions); SR("GUIDHi : %08x GUIDLo : %08x ConfigROMmap: %08x\n", OHCI1394_GUIDHi, OHCI1394_GUIDLo, OHCI1394_ConfigROMmap); SR("PtdWrAddrLo : %08x PtdWrAddrHi : %08x VendorID : %08x\n", OHCI1394_PostedWriteAddressLo, OHCI1394_PostedWriteAddressHi, OHCI1394_VendorID); SR("HCControl : %08x SelfIDBuffer: %08x SelfIDCount : %08x\n", OHCI1394_HCControlSet, OHCI1394_SelfIDBuffer, OHCI1394_SelfIDCount); SR("IRMuChMaskHi: %08x IRMuChMaskLo: %08x IntEvent : %08x\n", OHCI1394_IRMultiChanMaskHiSet, OHCI1394_IRMultiChanMaskLoSet, OHCI1394_IntEventSet); SR("IntMask : %08x IsoXmIntEvnt: %08x IsoXmIntMask: %08x\n", OHCI1394_IntMaskSet, OHCI1394_IsoXmitIntEventSet, OHCI1394_IsoXmitIntMaskSet); SR("IsoRcvIntEvt: %08x IsoRcvIntMsk: %08x FairnessCtrl: %08x\n", OHCI1394_IsoRecvIntEventSet, OHCI1394_IsoRecvIntMaskSet, OHCI1394_FairnessControl); SR("LinkControl : %08x NodeID : %08x PhyControl : %08x\n", OHCI1394_LinkControlSet, OHCI1394_NodeID, OHCI1394_PhyControl); SR("IsoCyclTimer: %08x AsRqFilterHi: %08x AsRqFilterLo: %08x\n", OHCI1394_IsochronousCycleTimer, OHCI1394_AsReqFilterHiSet, OHCI1394_AsReqFilterLoSet); SR("PhyReqFiltHi: %08x PhyReqFiltLo: %08x PhyUpperBnd : %08x\n", OHCI1394_PhyReqFilterHiSet, OHCI1394_PhyReqFilterLoSet, OHCI1394_PhyUpperBound); SR("AsRqTrCxtCtl: %08x AsRqTrCmdPtr: %08x AsRsTrCtxCtl: %08x\n", OHCI1394_AsReqTrContextControlSet, OHCI1394_AsReqTrCommandPtr, OHCI1394_AsRspTrContextControlSet); SR("AsRsTrCmdPtr: %08x AsRqRvCtxCtl: %08x AsRqRvCmdPtr: %08x\n", OHCI1394_AsRspTrCommandPtr, OHCI1394_AsReqRcvContextControlSet, OHCI1394_AsReqRcvCommandPtr); SR("AsRsRvCtxCtl: %08x AsRsRvCmdPtr: %08x IntEvent : %08x\n", OHCI1394_AsRspRcvContextControlSet, OHCI1394_AsRspRcvCommandPtr, OHCI1394_IntEventSet); for (i=0;inb_iso_rcv_ctx;i++) { p += sprintf(p,"IsoRCtxCtl%02d: %08x IsoRCmdPtr%02d: %08x" " IsoRCxtMch%02d: %08x\n", i, reg_read(ohci, OHCI1394_IsoRcvContextControlSet+32*i), i,reg_read(ohci, OHCI1394_IsoRcvCommandPtr+32*i), i,reg_read(ohci, OHCI1394_IsoRcvContextMatch+32*i)); } for (i=0;inb_iso_xmit_ctx;i++) { p += sprintf(p,"IsoTCtxCtl%02d: %08x IsoTCmdPtr%02d: %08x\n", i, reg_read(ohci, OHCI1394_IsoXmitContextControlSet+32*i), i,reg_read(ohci,OHCI1394_IsoXmitCommandPtr+32*i)); } #if 0 p += sprintf(p,"\n### Phy Register dump ###\n"); phyreg=get_phy_reg(ohci,1); p += sprintf(p,"offset: %d val: 0x%02x -> RHB: %d" "IBR: %d Gap_count: %d\n", 1,phyreg,(phyreg&0x80) != 0, (phyreg&0x40) !=0, phyreg&0x3f); phyreg=get_phy_reg(ohci,2); nports=phyreg&0x1f; p += sprintf(p,"offset: %d val: 0x%02x -> SPD: %d" " E : %d Ports : %2d\n", 2,phyreg, (phyreg&0xC0)>>6, (phyreg&0x20) !=0, nports); for (i=0;i [port %d]" " TPA: %d TPB: %d | %s %s\n", 3+i,phyreg, i, (phyreg&0xC0)>>6, (phyreg&0x30)>>4, (phyreg&0x08) ? "child" : "parent", (phyreg&0x04) ? "connected" : "disconnected"); } phyreg=get_phy_reg(ohci,3+i); p += sprintf(p,"offset: %d val: 0x%02x -> ENV: %s Reg_count: %d\n", 3+i,phyreg, (((phyreg&0xC0)>>6)==0) ? "backplane" : (((phyreg&0xC0)>>6)==1) ? "cable" : "reserved", phyreg&0x3f); #endif return p - buf; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) static int ohci1394_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = ohci_get_status(page); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len>count) len = count; if (len<0) len = 0; return len; } #else struct proc_dir_entry ohci_proc_entry = { 0, /* Inode number - dynamic */ 8, /* Length of the file name */ "ohci1394", /* The file name */ S_IFREG | S_IRUGO, /* File mode */ 1, /* Number of links */ 0, 0, /* The uid and gid for the file */ 0, /* The size of the file reported by ls. */ NULL, /* functions which can be done on the inode */ ohci_get_info, /* The read function for this file */ NULL }; #endif /* LINUX_VERSION_CODE */ #endif /* CONFIG_PROC_FS */ static void remove_card(struct ti_ohci *ohci) { /* * Reset the board properly before leaving * Daniel Kobras */ ohci_soft_reset(ohci); /* Free AR dma */ free_dma_rcv_ctx(&ohci->ar_req_context); free_dma_rcv_ctx(&ohci->ar_resp_context); /* Free AT dma */ free_dma_trm_ctx(&ohci->at_req_context); free_dma_trm_ctx(&ohci->at_resp_context); /* Free IR dma */ free_dma_rcv_ctx(&ohci->ir_context); /* Free self-id buffer */ if (ohci->selfid_buf_cpu) pci_free_consistent(ohci->dev, 2048, ohci->selfid_buf_cpu, ohci->selfid_buf_bus); /* Free config rom */ if (ohci->csr_config_rom_cpu) pci_free_consistent(ohci->dev, sizeof(ohci_csr_rom), ohci->csr_config_rom_cpu, ohci->csr_config_rom_bus); /* Free the IRQ */ free_irq(ohci->dev->irq, ohci); if (ohci->registers) iounmap(ohci->registers); ohci->state = 0; } static int init_driver() { struct pci_dev *dev = NULL; int success = 0; int i; if (num_of_cards) { PRINT_G(KERN_DEBUG, __PRETTY_FUNCTION__ " called again"); return 0; } PRINT_G(KERN_INFO, "looking for Ohci1394 cards"); for (i = 0; supported_chips[i][0] != -1; i++) { while ((dev = pci_find_device(supported_chips[i][0], supported_chips[i][1], dev)) != NULL) { if (add_card(dev) == 0) { success = 1; } } } if (success == 0) { PRINT_G(KERN_WARNING, "no operable Ohci1394 cards found"); return -ENXIO; } #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) create_proc_read_entry ("ohci1394", 0, NULL, ohci1394_read_proc, NULL); #else if (proc_register(&proc_root, &ohci_proc_entry)) { PRINT_G(KERN_ERR, "unable to register proc file"); return -EIO; } #endif #endif return 0; } static size_t get_ohci_rom(struct hpsb_host *host, const quadlet_t **ptr) { struct ti_ohci *ohci=host->hostdata; DBGMSG(ohci->id, "request csr_rom address: %08X", (u32)ohci->csr_config_rom_cpu); *ptr = ohci->csr_config_rom_cpu; return sizeof(ohci_csr_rom); } struct hpsb_host_template *get_ohci_template(void) { static struct hpsb_host_template tmpl; static int initialized = 0; if (!initialized) { /* Initialize by field names so that a template structure * reorganization does not influence this code. */ tmpl.name = "ohci1394"; tmpl.detect_hosts = ohci_detect; tmpl.initialize_host = ohci_initialize; tmpl.release_host = ohci_remove; tmpl.get_rom = get_ohci_rom; tmpl.transmit_packet = ohci_transmit; tmpl.devctl = ohci_devctl; initialized = 1; } return &tmpl; } void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) { int i=0; /* stop the channel program if it's still running */ reg_write(ohci, reg, 0x8000); /* Wait until it effectively stops */ while (reg_read(ohci, reg) & 0x400) { i++; if (i>5000) { PRINT(KERN_ERR, ohci->id, "runaway loop while stopping context..."); break; } } if (msg) PRINT(KERN_ERR, ohci->id, "%s\n dma prg stopped\n", msg); } struct ti_ohci *ohci1394_get_struct(int card_num) { if (card_num>=0 && card_numvideo_tmpl) return -ENFILE; ohci->video_tmpl = tmpl; MOD_INC_USE_COUNT; return 0; } void ohci1394_unregister_video(struct ti_ohci *ohci, struct video_template *tmpl) { if (ohci->video_tmpl != tmpl) { PRINT(KERN_ERR, ohci->id, "Trying to unregister wrong video device"); } else { ohci->video_tmpl = NULL; MOD_DEC_USE_COUNT; } } #if 0 int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data, quadlet_t compare, int sel) { int timeout = 255; reg_write(ohci, OHCI1394_CSRData, *data); reg_write(ohci, OHCI1394_CSRCompareData, compare); reg_write(ohci, OHCI1394_CSRControl, sel); while(!(reg_read(ohci, OHCI1394_CSRControl)&0x80000000)) { if (timeout--) { PRINT(KERN_INFO, ohci->id, "request_channel timeout"); return -1; } } *data = reg_read(ohci, OHCI1394_CSRData); return 0; } int ohci1394_request_channel(struct ti_ohci *ohci, int channel) { int csrSel; quadlet_t chan, data1=0, data2=0; int timeout = 32; if (channel<32) { chan = 1<id, "request_channel timeout"); return -1; } while (timeout--) { if (data1 & chan) { PRINT(KERN_INFO, ohci->id, "request channel %d failed", channel); return -1; } data2 = data1; data1 |= chan; if (ohci_compare_swap(ohci, &data1, data2, csrSel)<0) { PRINT(KERN_INFO, ohci->id, "request_channel timeout"); return -1; } if (data1==data2) { PRINT(KERN_INFO, ohci->id, "request channel %d succeded", channel); return 0; } } PRINT(KERN_INFO, ohci->id, "request channel %d failed", channel); return -1; } #endif EXPORT_SYMBOL(ohci1394_stop_context); EXPORT_SYMBOL(ohci1394_get_struct); EXPORT_SYMBOL(ohci1394_register_video); EXPORT_SYMBOL(ohci1394_unregister_video); #ifdef MODULE /* EXPORT_NO_SYMBOLS; */ MODULE_AUTHOR("Sebastien Rougeaux "); MODULE_DESCRIPTION("driver for PCI Ohci IEEE-1394 controller"); MODULE_SUPPORTED_DEVICE("ohci1394"); void cleanup_module(void) { hpsb_unregister_lowlevel(get_ohci_template()); #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) remove_proc_entry ("ohci1394", NULL); #else proc_unregister(&proc_root, ohci_proc_entry.low_ino); #endif #endif PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module"); } int init_module(void) { memset(cards, 0, MAX_OHCI1394_CARDS * sizeof (struct ti_ohci)); if (hpsb_register_lowlevel(get_ohci_template())) { PRINT_G(KERN_ERR, "registering failed"); return -ENXIO; } return 0; } #endif /* MODULE */