/* * ti_ohci1394.c - Texas Instruments Ohci1394 driver * 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. */ #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" #undef CONFIG_PROC_FS /* 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) int supported_chips[][2] = { { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394 }, { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_2 }, { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_OHCI1394 }, { -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); /*********************************** * 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->self_id_buffer; 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 self_id_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"); return -1; } size = ((self_id_count&0x0000EFFC)>>2) - 1; q++; while (size > 0) { if (q[0] == ~q[1]) { printk("-%d- selfid packet 0x%x rcvd\n", ohci->id, q[0]); hpsb_selfid_received(host, q[0]); if (((q[0]&0x3f000000)>>24)==phyid) { lsid=q[0]; printk("This node self-id is 0x%08x\n",lsid); } } else { printk("-%d- inconsistent selfid 0x%x/0x%x\n", ohci->id, q[0], q[1]); } q += 2; size -= 2; } printk(" calling self-id complete\n"); 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 ohci_initialize(struct hpsb_host *host) { struct ti_ohci *ohci=host->hostdata; int retval, i; spin_lock_init(&ohci->phy_reg_lock); /* Soft reset */ if ((retval=ohci_soft_reset(ohci))<0) return retval; /* Set the bus number */ reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0); /* Set Link Power Status (LPS) */ reg_write(ohci, OHCI1394_HCControlSet, 0x00080000); /* 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_IntEventClear, 0xffffffff); reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); /* Set up self-id dma buffer */ reg_write(ohci, OHCI1394_SelfIDBuffer, virt_to_bus(ohci->self_id_buffer)); /* enable self-id dma */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00000200); /* Set the configuration ROM mapping register */ reg_write(ohci, OHCI1394_ConfigROMmap, virt_to_bus(ohci->csr_config_rom)); #if 1 /* Why is this step necessary ? */ /* Write the config ROM header */ reg_write(ohci, OHCI1394_ConfigROMhdr,0x04040000); /* Set bus options */ reg_write(ohci, OHCI1394_BusOptions, 0xf064A002); #endif #if 1 /* Accept phy packets into AR request context */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400); #endif /* Enable link */ reg_write(ohci, OHCI1394_HCControlSet, 0x00020000); /* Initialize IR dma */ /* make sure the context isn't running, dead, or active */ if (!(reg_read(ohci, OHCI1394_IrRcvContextControlSet) & 0x00008F00)) { /* initialize IR program */ for (i= 0; i < IR_NUM_DESC; i++) { /* end of descriptor list? */ if ((i + 1) < IR_NUM_DESC) { ohci->IR_recv_prg[i]->control= (0x283C << 16) | IR_RECV_BUF_SIZE; ohci->IR_recv_prg[i]->branchAddress= (virt_to_bus(ohci->IR_recv_prg[i + 1]) & 0xfffffff0) | 0x1; } else { ohci->IR_recv_prg[i]->control= (0x283C << 16) | IR_RECV_BUF_SIZE; ohci->IR_recv_prg[i]->branchAddress= (virt_to_bus(ohci->IR_recv_prg[0]) & 0xfffffff0) | 0x1; } ohci->IR_recv_prg[i]->address= virt_to_bus(ohci->IR_recv_buf[i]); ohci->IR_recv_prg[i]->status= IR_RECV_BUF_SIZE; } /* Tell the controller where the first IR program is */ reg_write(ohci, OHCI1394_IrRcvCommandPtr, virt_to_bus(ohci->IR_recv_prg[0]) | 0x1 ); /* Set bufferFill, isochHeader, multichannel for IR context */ reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0xd0000000); /* Set the context match register to match on all tags */ reg_write(ohci, OHCI1394_IrRcvContextMatch, 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 for context 0 (thanks to Michael Greger for seeing that I forgot this) */ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001); /* Run IR context */ reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0x00008000); } /* Initialize AR dma */ ohci->AR_resp_prg->control=0x283C << 16 | AR_RESP_BUF_SIZE; ohci->AR_resp_prg->address=virt_to_bus(ohci->AR_resp_buf); ohci->AR_resp_prg->status=AR_RESP_BUF_SIZE; PRINT(KERN_INFO, ohci->id, "AR control: %x", ohci->AR_resp_prg->control); PRINT(KERN_INFO, ohci->id, "AR status: %x %d", ohci->AR_resp_prg->status & 0xffff, ohci->AR_resp_prg->status & 0xffff); /* Tell the controller where the AR program is */ reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, virt_to_bus(ohci->AR_resp_prg)|0x00000001); #if 1 /* Accept phy packets into AR request context */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400); #endif /* Run AR context */ reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x00008000); #ifndef __BIG_ENDIAN reg_write(ohci, OHCI1394_HCControlSet, 0x40000000); #else reg_write(ohci, OHCI1394_HCControlClear, 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 ); return 1; } static void ohci_remove(struct hpsb_host *host) { struct ti_ohci *ohci; if (host != NULL) { ohci = host->hostdata; remove_card(ohci); } } /* This must be called with the async_queue_lock held. */ static void send_next_async(struct ti_ohci *ohci) { int i=0; struct hpsb_packet *packet = ohci->async_queue; struct dma_cmd prg; quadlet_t *ptr = (quadlet_t *)ohci->AT_req_prg; //HPSB_TRACE(); /* stop the channel program if it's still running */ reg_write(ohci, OHCI1394_AsReqTrContextControlClear, 0x8000); /* Wait until it effectively stops */ while (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x400) { i++; if (i>5000) { PRINT(KERN_ERR, ohci->id, "runaway loop in DmaAT. bailing out..."); break; } }; if (packet->type == async) { /* re-format packet header according to ohci specification */ packet->header[1] = (packet->header[1] & 0xFFFF) | (packet->header[0] & 0xFFFF0000); packet->header[0] = DMA_SPEED_200 | (packet->header[0] & 0xFFFF); if (packet->data_size) { /* block transmit */ prg.control = OUTPUT_MORE_IMMEDIATE | 0x10; prg.address = 0; prg.branchAddress = 0; prg.status = 0; memcpy(ohci->AT_req_prg, &prg, 16); memcpy(ohci->AT_req_prg + 1, packet->header, 16); prg.control = OUTPUT_LAST | packet->data_size; prg.address = virt_to_bus(packet->data); memcpy(ohci->AT_req_prg + 2, &prg, 16); reg_write(ohci, OHCI1394_AsReqTrCommandPtr, virt_to_bus(ohci->AT_req_prg)|0x3); } else { /* quadlet transmit */ prg.control = OUTPUT_LAST_IMMEDIATE | packet->header_size; prg.address = 0; prg.branchAddress = 0; prg.status = 0; memcpy(ohci->AT_req_prg, &prg, 16); memcpy(ohci->AT_req_prg + 1, packet->header, 16); PRINT(KERN_INFO, ohci->id, "dma_cmd: %08x %08x %08x %08x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); PRINT(KERN_INFO, ohci->id, "header: %08x %08x %08x %08x", *(ptr+4), *(ptr+5), *(ptr+6), *(ptr+7)); reg_write(ohci, OHCI1394_AsReqTrCommandPtr, virt_to_bus(ohci->AT_req_prg)|0x2); } } else if (packet->type == raw) { prg.control = OUTPUT_LAST | packet->data_size; prg.address = virt_to_bus(packet->data); prg.branchAddress = 0; prg.status = 0; memcpy(ohci->AT_req_prg, &prg, 16); PRINT(KERN_INFO, ohci->id, "dma_cmd: %08x %08x %08x %08x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); reg_write(ohci, OHCI1394_AsReqTrCommandPtr, virt_to_bus(ohci->AT_req_prg)|0x2); } /* run program */ reg_write(ohci, OHCI1394_AsReqTrContextControlSet, 0x00008000); } static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) { struct ti_ohci *ohci = host->hostdata; struct hpsb_packet *p; unsigned long flags; if (packet->data_size >= 4096) { PRINT(KERN_ERR, ohci->id, "transmit packet data too big (%d)", packet->data_size); return 0; } //HPSB_TRACE(); packet->xnext = NULL; spin_lock_irqsave(&ohci->async_queue_lock, flags); if (ohci->async_queue == NULL) { ohci->async_queue = packet; send_next_async(ohci); } else { p = ohci->async_queue; while (p->xnext != NULL) { p = p->xnext; } p->xnext = packet; } spin_unlock_irqrestore(&ohci->async_queue_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; struct hpsb_packet *packet, *lastpacket; u32 r; switch (cmd) { case RESET_BUS: PRINT(KERN_INFO, ohci->id, "resetting bus on request%s", (host->attempt_root ? " and attempting to become root" : "")); r = (host->attempt_root) ? 0x000041ff : 0x0000417f; reg_write(ohci, OHCI1394_PhyControl, r); 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 0 if (arg) { /* enable cycleTimer, cycleMaster, cycleSource */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00700000); } else { /* disable cycleTimer, cycleMaster, cycleSource */ reg_write(ohci, OHCI1394_LinkControlClear, 0x00700000); }; #endif break; case CANCEL_REQUESTS: spin_lock_irqsave(&ohci->async_queue_lock, flags); /* stop any chip activity */ reg_write(ohci, OHCI1394_HCControlClear, 0x00020000); packet = ohci->async_queue; ohci->async_queue = NULL; spin_unlock_irqrestore(&ohci->async_queue_lock, flags); while (packet != NULL) { lastpacket = packet; packet = packet->xnext; hpsb_packet_sent(host, lastpacket, ACKX_ABORTED); } break; case MODIFY_USAGE: if (arg) { MOD_INC_USE_COUNT; } else { MOD_DEC_USE_COUNT; } break; case ISO_LISTEN_CHANNEL: spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (!test_and_set_bit(arg, &ohci->IR_channel_usage)) { PRINT(KERN_INFO, ohci->id, "listening enabled on channel %d", arg); if (arg > 31) { u32 setMask= 0x00000001; arg-= 32; while(arg--) setMask= setMask << 1; reg_write(ohci, OHCI1394_IRMultiChanMaskHiSet, setMask); } else { u32 setMask= 0x00000001; while(arg--) setMask= setMask << 1; reg_write(ohci, OHCI1394_IRMultiChanMaskLoSet, setMask); } } spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); break; case ISO_UNLISTEN_CHANNEL: spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (test_and_clear_bit(arg, &ohci->IR_channel_usage)) { PRINT(KERN_INFO, ohci->id, "listening disabled on iso channel %d", arg); if (arg > 31) { u32 clearMask= 0x00000001; arg-= 32; while(arg--) clearMask= clearMask << 1; reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, clearMask); } else { u32 clearMask= 0x00000001; while(arg--) clearMask= clearMask << 1; reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, clearMask); } } spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); break; default: PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet\n", cmd); break; } return retval; } /*************************************** * IEEE-1394 functionality section END * ***************************************/ /******************************************************** * Global stuff (interrupt handler, init/shutdown code) * ********************************************************/ static void ohci_irq_handler(int irq, void *dev_id, struct pt_regs *regs_are_unused) { //int i; static 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; event=reg_read(ohci, OHCI1394_IntEventSet); /* Clear the interrupt register */ reg_write(ohci, OHCI1394_IntEventClear, event); /* PRINT(KERN_INFO, ohci->id, "int event %08X mask %08X", event,reg_read(ohci, OHCI1394_IntMaskSet)); */ if (event & OHCI1394_busReset) { PRINT(KERN_INFO, ohci->id, "bus reset interrupt"); if (!host->in_bus_reset) { hpsb_bus_reset(host); } ohci->NumBusResets++; } if (event & OHCI1394_reqTxComplete) { PRINT(KERN_INFO, ohci->id, "reqTxComplete int received"); } if (event & OHCI1394_RQPkt) { PRINT(KERN_INFO, ohci->id, "RQPkt int received"); } if (event & OHCI1394_RQPkt) { PRINT(KERN_INFO, ohci->id, "ControlContext: %08X", reg_read(ohci, OHCI1394_AsReqRcvContextControlSet)); } if (event & OHCI1394_RSPkt) { int rcv_bytes; int i=0; /* we calculate the number of received bytes from the residual count field */ rcv_bytes = AR_RESP_BUF_SIZE - (ohci->AR_resp_prg->status & 0xFFFF); PRINT(KERN_INFO, ohci->id, "AR_status 0x%x %d, %d bytes read", ohci->AR_resp_prg->status, ohci->AR_resp_prg->status & 0xffff, rcv_bytes); ohci->AR_resp_active = 0; if ((ohci->AR_resp_prg->status & 0x84000000) && (ohci->AR_resp_prg->status & 0xFFFF) >= 8 ) { hpsb_packet_received(host, ohci->AR_resp_buf, rcv_bytes); } else { //HPSB_TRACE(); PRINT(KERN_ERR, ohci->id, "AR resp DMA program status value 0x%x is incorrect!", ohci->AR_resp_prg->status); } /* --------------- FIXME --------------------------------- this is a complete hack... we stop the dma prg and start it again so as to reset the dma buffer address Very slow, very bad design... to change ASAP */ /* stop the channel program if it's still running */ reg_write(ohci, OHCI1394_AsRspRcvContextControlClear, 0x8000); /* Wait until it effectively stops */ while (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x400) { i++; if (i>5000) { PRINT(KERN_ERR, ohci->id, "runaway loop in DmaAT. bailing out..."); break; } } reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, virt_to_bus(ohci->AR_resp_prg)|0x00000001); ohci->AR_resp_prg->status=AR_RESP_BUF_SIZE; reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x8000); /* ---------------- end of FIXME --------------------------*/ } if (event & OHCI1394_isochRx) { quadlet_t isoRecvIntEvent; /* ASSUMPTION: We assume there is only one context for now. */ spin_lock(&ohci->IR_recv_lock); /* Clear the isoRecvIntEvent register (very important!) */ isoRecvIntEvent= reg_read(ohci, OHCI1394_IsoRecvIntEventSet); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, isoRecvIntEvent); ohci->IR_buf_used++; ohci->IR_buf_next_ind= (ohci->IR_buf_next_ind + 1) % IR_NUM_DESC; /* is buffer processing too slow? (all buffers used) */ if (ohci->IR_buf_next_ind == ohci->IR_buf_last_ind) { int i= 0; /* stop the context */ reg_write(ohci, OHCI1394_IrRcvContextControlClear, 0x8000); while (reg_read(ohci, OHCI1394_IrRcvContextControlSet) & 0x400) { i++; if (i>5000) { PRINT(KERN_ERR, ohci->id, "runaway loop in DmaIR. bailing out..."); break; } } spin_unlock(&ohci->IR_recv_lock); PRINT(KERN_ERR, ohci->id, "iso receive processing too slow... stopped"); return; } /* reset status field of next descriptor */ ohci->IR_recv_prg[ohci->IR_buf_next_ind]->status= IR_RECV_BUF_SIZE; spin_unlock(&ohci->IR_recv_lock); /* queue bottom half in immediate queue */ queue_task(&ohci->IR_pdl_task, &tq_immediate); mark_bh(IMMEDIATE_BH); } if (event & OHCI1394_selfIDComplete) { if (host->in_bus_reset) { node_id = reg_read(ohci, OHCI1394_NodeID); if (node_id & 0x8000000) { /* 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); } else PRINT(KERN_INFO, ohci->id, "phy reg received without reset\n"); } if (event & OHCI1394_phyRegRcvd) { #if 0 if (host->in_bus_reset) { PRINT(KERN_INFO, ohci->id, "PhyControl: %08X", reg_read(ohci, OHCI1394_PhyControl)); } else printk("-%d- phy reg received without reset\n", ohci->id); #endif } if (event & OHCI1394_reqTxComplete) { /* async packet sent - transmitter ready */ u32 ack; struct hpsb_packet *packet; if (ohci->async_queue) { spin_lock(&ohci->async_queue_lock); ack=reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0xF; packet = ohci->async_queue; ohci->async_queue = packet->xnext; if (ohci->async_queue != NULL) { send_next_async(ohci); } spin_unlock(&ohci->async_queue_lock); PRINT(KERN_INFO,ohci->id, "packet sent with ack code %d",ack); hpsb_packet_sent(host, packet, ack); } else PRINT(KERN_INFO,ohci->id, "packet sent without async_queue (self-id?)"); ohci->TxRdy++; } ohci->NumInterrupts++; } /* This is the bottom half that processes iso receive descriptor buffers. */ static void ohci_ir_proc_desc(void *data) { quadlet_t *buf_ptr; struct ti_ohci *ohci= (struct ti_ohci*)data; int bytes_left, data_length; unsigned int idx; unsigned long flags; spin_lock_irqsave(&ohci->IR_recv_lock, flags); while(ohci->IR_buf_used > 0) { idx= ohci->IR_buf_last_ind; /* check to see if a fatal error occurred */ if ((ohci->IR_recv_prg[idx]->status >> 16) & 0x800) { int i= 0; /* stop the context */ reg_write(ohci, OHCI1394_IrRcvContextControlClear, 0x8000); while (reg_read(ohci, OHCI1394_IrRcvContextControlSet) & 0x400) { i++; if (i > 5000) { PRINT(KERN_ERR, ohci->id, "runaway loop in DmaIR. bailing out..."); break; } } spin_unlock_irqrestore(&ohci->IR_recv_lock, flags); PRINT(KERN_ERR, ohci->id, "fatal iso receive error -- status is %d", ohci->IR_recv_prg[idx]->status & 0x1F); return; } spin_unlock_irqrestore(&ohci->IR_recv_lock, flags); buf_ptr= bus_to_virt(ohci->IR_recv_prg[idx]->address); bytes_left= IR_RECV_BUF_SIZE; /* are we processing a split packet from last buffer */ if (ohci->IR_sp_bytes_left) { if (!ohci->IR_spb_bytes_used) { /* packet is in process of being dropped */ if (ohci->IR_sp_bytes_left > bytes_left) { ohci->IR_sp_bytes_left-= bytes_left; bytes_left= 0; } else { buf_ptr= bus_to_virt((unsigned long) &((quadlet_t*)ohci->IR_recv_prg [idx]->address) [ohci->IR_sp_bytes_left / 4]); bytes_left-= ohci->IR_sp_bytes_left; ohci->IR_sp_bytes_left= 0; } } else { /* packet is being assembled */ if (ohci->IR_sp_bytes_left > bytes_left) { memcpy(&ohci->IR_spb [ohci->IR_spb_bytes_used / 4], buf_ptr, bytes_left); ohci->IR_spb_bytes_used+= bytes_left; ohci->IR_sp_bytes_left-= bytes_left; bytes_left= 0; } else { memcpy(&ohci->IR_spb [ohci->IR_spb_bytes_used / 4], buf_ptr, ohci->IR_sp_bytes_left); ohci->IR_spb_bytes_used+= ohci->IR_sp_bytes_left; hpsb_packet_received(ohci->host, ohci->IR_spb, ohci->IR_spb_bytes_used); buf_ptr= bus_to_virt((unsigned long) &((quadlet_t*)ohci->IR_recv_prg [idx]->address) [ohci->IR_sp_bytes_left / 4]); bytes_left-= ohci->IR_sp_bytes_left; ohci->IR_sp_bytes_left= 0; ohci->IR_spb_bytes_used= 0; } } } while(bytes_left > 0) { data_length= (int)((buf_ptr[0] >> 16) & 0xffff); if (data_length % 4) data_length+= 4 - (data_length % 4); /* is this a split packet? */ if ( (bytes_left - (data_length + 8)) < 0 ) { if ( (data_length + 8) <= IR_SPLIT_PACKET_BUF_SIZE ) { memcpy(ohci->IR_spb, buf_ptr, bytes_left); ohci->IR_spb_bytes_used= bytes_left; } else { PRINT(KERN_ERR, ohci->id, "Packet too large for split packet buffer... dropping it"); PRINT(KERN_DEBUG, ohci->id, "Header: %8.8x\n", buf_ptr[0]); ohci->IR_spb_bytes_used= 0; } ohci->IR_sp_bytes_left= (data_length + 8) - bytes_left; } else { hpsb_packet_received(ohci->host, buf_ptr, (data_length + 8)); buf_ptr= bus_to_virt((unsigned long) &((quadlet_t*)ohci->IR_recv_prg [idx]->address) [(IR_RECV_BUF_SIZE - bytes_left + data_length + 8) / 4]); } bytes_left-= (data_length + 8); } spin_lock_irqsave(&ohci->IR_recv_lock, flags); ohci->IR_buf_last_ind= (idx + 1) % IR_NUM_DESC; ohci->IR_buf_used--; } spin_unlock_irqrestore(&ohci->IR_recv_lock, flags); } static int add_card(struct pci_dev *dev) { #define FAIL(fmt, args...) \ PRINT_G(KERN_ERR, fmt , ## args); \ num_of_cards--; \ remove_card(ohci); \ return 1; struct ti_ohci *ohci; /* shortcut to currently handled device */ int i; 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; } ohci = &cards[num_of_cards++]; ohci->id = num_of_cards-1; ohci->dev = dev; ohci->state = 0; 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); } /* csr_config rom allocation */ ohci->csr_config_rom = kmalloc(1024, GFP_KERNEL); if (ohci->csr_config_rom == NULL) { FAIL("failed to allocate buffer config rom"); } memcpy(ohci->csr_config_rom, ohci_csr_rom, sizeof(ohci_csr_rom)); /* self-id dma buffer allocation */ ohci->self_id_buffer = kmalloc(2048, GFP_KERNEL); if (ohci->self_id_buffer == NULL) { FAIL("failed to allocate DMA buffer for self-id packets"); } /* AR dma buffer allocation */ ohci->AR_resp_buf = kmalloc(AR_RESP_BUF_SIZE, GFP_KERNEL); if (ohci->AR_resp_buf != NULL) { memset(ohci->AR_resp_buf, 0, AR_RESP_BUF_SIZE); } else { FAIL("failed to allocate AR response DMA buffer"); } /* AR dma program allocation */ ohci->AR_resp_prg = (struct dma_cmd *) kmalloc(AR_RESP_PRG_SIZE, GFP_KERNEL); if (ohci->AR_resp_prg != NULL) { memset(ohci->AR_resp_prg, 0, AR_RESP_PRG_SIZE); } else { FAIL("failed to allocate AR response DMA program"); } /* AT dma program allocation */ ohci->AT_req_prg = (struct dma_cmd *) kmalloc(AT_REQ_PRG_SIZE, GFP_KERNEL); if (ohci->AT_req_prg != NULL) { memset(ohci->AT_req_prg, 0, AT_REQ_PRG_SIZE); } else { FAIL("failed to allocate AT request DMA program"); } /* IR dma buffer and program allocation */ ohci->IR_recv_buf= kmalloc(IR_NUM_DESC * sizeof(quadlet_t*), GFP_KERNEL); if (ohci->IR_recv_buf == NULL) { FAIL("failed to allocate IR receive DMA buffer"); } ohci->IR_recv_prg= kmalloc(IR_NUM_DESC * sizeof(struct dma_cmd*), GFP_KERNEL); if (ohci->IR_recv_prg == NULL) { FAIL("failed to allocate IR receive DMA program"); } ohci->IR_spb= kmalloc(IR_SPLIT_PACKET_BUF_SIZE, GFP_KERNEL); if (ohci->IR_spb == NULL) { FAIL("failed to allocate IR split packet buffer"); } for (i= 0; i < IR_NUM_DESC; i++) { ohci->IR_recv_buf[i]= kmalloc(IR_RECV_BUF_SIZE, GFP_KERNEL); if (ohci->IR_recv_buf[i] != NULL) { memset(ohci->IR_recv_buf[i], 0, IR_RECV_BUF_SIZE); } else { FAIL("failed to allocate IR DMA buffer"); } ohci->IR_recv_prg[i]= kmalloc(sizeof(struct dma_cmd), GFP_KERNEL); if (ohci->IR_recv_prg[i] != NULL) { memset(ohci->IR_recv_prg[i], 0, sizeof(struct dma_cmd)); } else { FAIL("failed to allocate IR DMA buffer"); } } ohci->IR_buf_used= 0; ohci->IR_buf_last_ind= 0; ohci->IR_buf_next_ind= 0; spin_lock_init(&ohci->IR_recv_lock); spin_lock_init(&ohci->IR_channel_lock); ohci->IR_channel_usage= 0x0000000000000000; /* initialize iso receive task */ ohci->IR_pdl_task.routine= ohci_ir_proc_desc; ohci->IR_pdl_task.data= (void*)ohci; #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); 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)); int ohci_get_info(char *buf, char **start, off_t fpos, int length, int dummy) { struct ti_ohci *ohci=&cards[0]; struct hpsb_host *host=ohci->host; char *p=buf; //unsigned char phyreg; //int i, nports; int i; 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### ohci data ###\n"); p += sprintf(p,"AR_resp_buf : %p AR_resp_prg: %p\n", ohci->AR_resp_buf, ohci->AR_resp_prg); for (i= 0; i < IR_NUM_DESC; i++) { p += sprintf(p, "IR_recv_buf[%d] : %p IR_recv_prg[%d]: %p\n", i, ohci->IR_recv_buf[i], i, ohci->IR_recv_prg[i]); } /* ----- 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("CSRReadData : %08x CSRCompData : %08x CSRControl : %08x\n", OHCI1394_CSRReadData, 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); #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 p += sprintf(p,"AR_resp_prg ctrl: %08x\n",ohci->AR_resp_prg->control); p += sprintf(p,"AR_resp_prg status: %08x\n",ohci->AR_resp_prg->status); return p - buf; } 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 static void remove_card(struct ti_ohci *ohci) { if (ohci->registers) iounmap(ohci->registers); if (ohci->AR_resp_buf) kfree(ohci->AR_resp_buf); if (ohci->AR_resp_prg) kfree(ohci->AR_resp_prg); if (ohci->AT_req_prg) kfree(ohci->AT_req_prg); if (ohci->IR_recv_buf) { int i; for (i= 0; i < IR_NUM_DESC; i++) { kfree(ohci->IR_recv_buf[i]); } kfree(ohci->IR_recv_buf); } if (ohci->IR_recv_prg) { int i; for (i= 0; i < IR_NUM_DESC; i++) { kfree(ohci->IR_recv_prg[i]); } kfree(ohci->IR_recv_prg); } kfree(ohci->IR_spb); if (ohci->self_id_buffer) kfree(ohci->self_id_buffer); if (ohci->csr_config_rom) kfree(ohci->csr_config_rom); free_irq(ohci->dev->irq, ohci); 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 (proc_register(&proc_root, &ohci_proc_entry)) { PRINT_G(KERN_ERR, "unable to register proc file\n"); return -EIO; } #endif return 0; } static size_t get_ohci_rom(struct hpsb_host *host, const quadlet_t **ptr) { struct ti_ohci *ohci=host->hostdata; #if 0 PRINT(KERN_INFO, ohci->id, "request csr_rom address: %08X", (u32)ohci->csr_config_rom); #endif *ptr = ohci->csr_config_rom; 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; } #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 proc_unregister(&proc_root, ohci_proc_entry.low_ino); #endif PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module\n"); } int init_module(void) { if (hpsb_register_lowlevel(get_ohci_template())) { PRINT_G(KERN_ERR, "registering failed\n"); return -ENXIO; } else { return 0; } } #endif /* MODULE */