]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/c_can_irq.c
The first phase of integration of Hynix HMS30c7202 C_CAN support
[lincan.git] / lincan / src / c_can_irq.c
diff --git a/lincan/src/c_can_irq.c b/lincan/src/c_can_irq.c
new file mode 100644 (file)
index 0000000..5257d0f
--- /dev/null
@@ -0,0 +1,510 @@
+/* c_can_irq.c - Hynix HMS30c7202 ARM IRQ handling code
+ * Linux CAN-bus device driver.
+ * Written by Sebastian Stolzenberg email:stolzi@sebastian-stolzenberg.de
+ * Based on code from Arnaud Westenberg email:arnaud@wanadoo.nl
+ * and Ake Hedman, eurosource, akhe@eurosource.se
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.2  9 Jul 2003
+ */
+#include "../include/can.h"
+#include "../include/can_sysdep.h"
+#include "../include/main.h"
+#include "../include/c_can.h"
+
+union c_can_data
+{
+   unsigned short wdata[4];
+   unsigned char bdata[8];
+};
+
+
+
+// prototypes
+inline void c_can_irq_read_handler( struct chip_t *pchip, int idxobj, u32 msgid  );
+
+inline void c_can_irq_write_handler( struct chip_t *pchip, int idxobj);
+
+void c_can_irq_rtr_handler( struct chip_t *pchip, int idxobj, u32 msgid );
+
+u16 readMaskCM = IFXCM_ARB | IFXCM_CNTRL | IFXCM_CLRINTPND
+  | IFXCM_TRND | IFXCM_DA | IFXCM_DB;
+
+u16 msgLstReadMaskCM = IFXCM_CNTRL;
+u16 msgLstWriteMaskCM =  IFXCM_CNTRL | IFXCM_WRRD;
+
+///////////////////////////////////////////////////////////////////////////////
+// c_can_irq_write_handler
+//
+// Send a message from the output fifo ( if any ).
+//
+
+inline void c_can_irq_write_handler( struct chip_t *pchip, int idxobj)
+{
+       int cmd;
+       struct msgobj_t *pmsgobj = pchip->msgobj[idxobj];
+
+       DEBUGMSG("(c%dm%d)calling c_can_irq_write_handler(...)\n",
+               pchip->chip_idx, pmsgobj->object);
+
+       if(pmsgobj->tx_slot){
+               /* Do local transmitted message distribution if enabled */
+               if (processlocal){
+                       pmsgobj->tx_slot->msg.flags |= MSG_LOCAL;
+                       canque_filter_msg2edges(pmsgobj->qends, &pmsgobj->tx_slot->msg);
+               }
+               /* Free transmitted slot */
+               canque_free_outslot(pmsgobj->qends, pmsgobj->tx_qedge, pmsgobj->tx_slot);
+               pmsgobj->tx_slot=NULL;
+       }
+   
+       // Get ready to send next message
+       spin_lock( &c_can_spwlock );
+
+       cmd=canque_test_outslot(pmsgobj->qends, &pmsgobj->tx_qedge, &pmsgobj->tx_slot);
+       if(cmd<0){
+               DEBUGMSG("(c%dm%d)Nothin to write\n",
+                       pchip->chip_idx, pmsgobj->object);
+               spin_unlock( &c_can_spwlock );
+               return;
+       }
+
+       // Send the message
+       if ( pchip->chipspecops->send_msg( pchip, pmsgobj,&pmsgobj->tx_slot->msg) ) {
+               pmsgobj->ret = -1;
+               canque_notify_inends(pmsgobj->tx_qedge, CANQUEUE_NOTIFY_ERRTX_SEND);
+               canque_free_outslot(pmsgobj->qends, pmsgobj->tx_qedge, pmsgobj->tx_slot);
+               pmsgobj->tx_slot=NULL;
+               spin_unlock( &c_can_spwlock );
+               DEBUGMSG("(c%dm%d)c_can_irq_handler: Unable to send message\n",
+                       pchip->chip_idx, pmsgobj->object );
+               return;
+       } else {
+               // Another message sent
+           #ifdef CAN_WITH_STATISTICS
+               pchip->stat.cntTxPkt++;
+               pchip->stat.cntTxData += pmsgobj->tx_slot->length;
+           #endif /*CAN_WITH_STATISTICS*/
+       }
+       spin_unlock( &c_can_spwlock );
+
+       // Wake up any waiting writer
+       return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// c_can_irq_read_handler
+//
+// Message received form the line. Write it in the input fifo->
+//
+
+inline void c_can_irq_read_handler( struct chip_t *pchip,
+                                   int idxobj, u32 msgid  )
+{
+       int i=0;
+       u16 bDataAvail=1 ;
+       u16 msgCntlReg = 0;
+       union c_can_data readData;
+       struct msgobj_t *pmsgobj = pchip->msgobj[idxobj];
+               
+       DEBUGMSG("(c%dm%d)calling c_can_irq_read_handler(...)\n",
+               pchip->chip_idx, pmsgobj->object);
+       
+       while ( bDataAvail ) {
+            
+           #ifdef CAN_WITH_STATISTICS
+               pchip->stat.cntRxFifoOvr++;
+           #endif /*CAN_WITH_STATISTICS*/
+               // Message length
+               msgCntlReg = c_can_read_reg_w( pchip, CCIF1DMC );
+       
+               pmsgobj->rx_msg.length = msgCntlReg & 0x000F;
+       
+               // Message id
+               pmsgobj->rx_msg.id = (u32)msgid;
+       
+               // Fetch message bytes
+               if (pmsgobj->rx_msg.length > 0)
+                       readData.wdata[0] = c_can_read_reg_w(pchip, CCIF1DA1);
+               if (pmsgobj->rx_msg.length > 2)
+                       readData.wdata[1] = c_can_read_reg_w(pchip, CCIF1DA2);
+               if (pmsgobj->rx_msg.length > 4)
+                       readData.wdata[2] = c_can_read_reg_w(pchip, CCIF1DB1);
+               if (pmsgobj->rx_msg.length > 6)
+                       readData.wdata[3] = c_can_read_reg_w(pchip, CCIF1DB2);
+               
+               for ( i=0; i < pmsgobj->rx_msg.length; i++ ) {
+                       pmsgobj->rx_msg.data[ i ] = readData.bdata[i];
+               }
+               DEBUGMSG("(c%dm%d)Received Message:\n",
+                     pchip->chip_idx, pmsgobj->object);
+               DEBUGMSG(" id = %d\n",
+                     pmsgobj->rx_msg.id);
+               DEBUGMSG(" length = %d\n",
+                     pmsgobj->rx_msg.length);
+               for ( i=0; i < pmsgobj->rx_msg.length; i++ )
+                       DEBUGMSG(" data[%d] = 0x%.2x\n", i, pmsgobj->rx_msg.data[i]);
+               
+               canque_filter_msg2edges(pmsgobj->qends, &pmsgobj->rx_msg);
+           
+           #ifdef CAN_WITH_STATISTICS
+               // Another received packet
+               pchip->stat.cntRxPkt++;
+
+               // Add databytes read to statistics block
+               pchip->stat.cntRxData += pmsgobj->rx_msg.length;
+           #endif /*CAN_WITH_STATISTICS*/
+
+               spin_unlock( &c_can_sprlock );
+
+               // Check if new data arrived
+               if (c_can_if1_busycheck(pchip)) ;
+               c_can_write_reg_w(pchip, readMaskCM, CCIF1CM);
+               c_can_write_reg_w(pchip, idxobj+1, CCIF1CR);
+               if (c_can_if1_busycheck(pchip)) ;
+               if ( !( ( bDataAvail = c_can_read_reg_w( pchip, CCIF1DMC ) ) &
+                       IFXMC_NEWDAT ) ) {
+                       break;
+               }
+
+               if ( bDataAvail & IFXMC_MSGLST ) {
+                       CANMSG("(c%dm%d)c-can fifo full: Message lost!\n",
+                       pchip->chip_idx, pmsgobj->object);
+               }
+
+       }
+       // while
+}
+
+void c_can_irq_sync_activities(struct chip_t *chip, struct msgobj_t *obj)
+{
+       while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)) {
+
+               /*if(can_msgobj_test_and_clear_fl(obj,TX_REQUEST)) {
+                       if(canobj_read_reg(chip,obj,iMSGCTL1)&TXRQ_RES)
+                               i82527_irq_write_handler(chip, obj);
+               }
+
+               if(!obj->tx_slot) {
+                       if(can_msgobj_test_and_clear_fl(obj,FILTCH_REQUEST)) {
+                               i82527_irq_update_filter(chip, obj);
+                       }
+               }*/
+               /* FIXME: these functionality has to be implemented to start TX */
+
+               can_msgobj_clear_fl(obj,TX_LOCK);
+               if(can_msgobj_test_fl(obj,TX_REQUEST))
+                       continue;
+               if(can_msgobj_test_fl(obj,FILTCH_REQUEST) && !obj->tx_slot)
+                       continue;
+               break;
+       }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// c_can_irq_handler
+//
+
+can_irqreturn_t c_can_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct rtr_id *rtr_search = hardware_p->rtr_queue;
+       struct chip_t *pchip = (struct chip_t *)dev_id;
+       u16 chip_status;
+       int id0=0, id1=0;
+       u16 errcount = 0;
+       u16 irqreg = 0;
+       u32 msgid = 0;
+       u16 tempCntlReg = 0;
+       //#ifdef CAN_DEBUG
+       // u32 intCntrVAddr = 0;
+       //#endif
+       //unsigned short flags = 0;
+
+       //if (pchip->ntype != CAN_CHIPTYPE_C_CAN)       {
+       //      DEBUGMSG("\n(c%d)IRQ not for c_can_irq_handler(...)\n", pchip->chip_idx);
+       //      return;
+       //}
+
+       irqreg = c_can_read_reg_w( pchip, CCINTR );
+
+       if(!irqreg) {
+               DEBUGMSG( "\n(c%d)IRQ handler: addr=%.8lx spurious interrupt\n",
+                       pchip->chip_idx,
+                       (long)( pchip->/*v*/base_addr/* + CCSR*/));
+               return CAN_IRQ_NONE;
+       }
+       
+       DEBUGMSG( "\n(c%d)IRQ handler: addr=%.8lx irqreg=0x%.4x\n",
+            pchip->chip_idx,
+            (long)( pchip->/*v*/base_addr/* + CCSR*/),
+            irqreg);
+
+
+       
+    #ifdef REGDUMP
+       c_can_registerdump(pchip);
+    #endif
+
+/*
+#ifdef CAN_DEBUG
+       if ( (!( intCntrVAddr = (u32)ioremap( 0x80024000, 0xCD ) ))) {
+               DEBUGMSG("Failed to map Interrupt Controller IO-memory\n");
+       }
+       else {
+
+   DEBUGMSG( "Mapped Interrupt Controller IO-memory: 0x%lx - 0x%lx to 0x%lx\n",
+               (unsigned long)0X80024000,
+             (unsigned long)0X800240CC,
+             (unsigned long)intCntrVAddr);
+       }
+
+       DEBUGMSG("Current Interrupt Status Register (ISR): 0x%4.4lx\n",
+                               (long)readl(intCntrVAddr + 4));
+       DEBUGMSG("Current Interrupt ID: %d\n",
+                               (int)(readl(intCntrVAddr + 0x90) & 0xF));
+       iounmap( (void*)intCntrVAddr);
+       DEBUGMSG( "Unmapped Interrupt Controller IO-memory: 0x%lx\n",
+             (unsigned long)intCntrVAddr);
+#endif
+*/
+       while ( irqreg ){
+               // Handle change in status register
+           
+               if ( irqreg == INT_STAT ) {
+                       chip_status = c_can_read_reg_w( pchip, CCSR );
+                       DEBUGMSG( "(c%d)Status register: 0x%x\n",
+                               pchip->chip_idx, chip_status );
+               
+                       if ( chip_status & SR_EWARN ) {
+                               // There is an abnormal # of errors
+                           #ifdef CAN_WITH_STATISTICS
+                               pchip->stat.cntWarnings++;
+                           #endif /*CAN_WITH_STATISTICS*/
+                               errcount = c_can_read_reg_w( pchip, CCEC);
+                               DEBUGMSG("(c%d)stat: c_can_irq_handler: Abnormal number of Errors Warning\n"
+                                        "       txErr=%d, rxErr=%d\n",
+                                       pchip->chip_idx, (errcount&0x00ff), ((errcount&0x7f00)>>8));
+
+                               /*
+                               // this code deactivates the chip if the transmiterrorcounter grew above 127
+                               if ((pchip->stat.cntWarnings > 100) && ((errcount&0x00ff) > 127))
+                               {
+                                       CANMSG("(c%d)to much Errornumber warnings (>100), deactivating chip",
+                                       pchip->chip_idx);
+                                       pchip->config_irqs(pchip, 0);
+                                       pchip->enable_configuration(pchip);
+                                       pchip->clear_objects(pchip);
+                                       pchip->flags &= ~CHANNEL_CONFIGURED;
+                                       return;
+                               }*/
+                       }
+
+                       if ( chip_status & SR_EPASS ) {
+                               // There is an abnormal # of errors
+                           #ifdef CAN_WITH_STATISTICS
+                               pchip->stat.cntErrPassive++;
+                           #endif /*CAN_WITH_STATISTICS*/
+                               DEBUGMSG("(c%d)stat: c_can_irq_handler: Chip entering Error Passive Mode\n",
+                               pchip->chip_idx);
+                       }
+
+                       if ( chip_status & SR_BOFF ) {
+                               // We have a bus off condition
+                           #ifdef CAN_WITH_STATISTICS
+                               pchip->stat.cntBusOff++;
+                           #endif /*CAN_WITH_STATISTICS*/
+                               //pchip->fifo->tx_in_progress = 0;
+                               //reset init bit
+                               CANMSG("(c%d)stat: c_can_irq_handler: Bus Off\n",
+                                       pchip->chip_idx);
+                               /*if (pchip->stat.cntBusOff > 100)
+                               {
+                               CANMSG("(c%d)to much busoff warnings (>100), deactivating chip",
+                                       pchip->chip_idx);
+                               pchip->config_irqs(pchip, 0);
+                               pchip->enable_configuration(pchip);
+                               pchip->clear_objects(pchip);
+                               pchip->flags &= ~CHANNEL_CONFIGURED;
+                               return;
+                               }
+                               else*/
+                               CANMSG("(c%d)try to reconnect",
+                                       pchip->chip_idx);
+                               pchip->chipspecops->disable_configuration(pchip);
+                       }
+
+                       if (chip_status & SR_TXOK) {
+                               DEBUGMSG("(c%d)stat: Transmitted a Message successfully\n",
+                                       pchip->chip_idx);
+                               c_can_write_reg_w(pchip, chip_status & ~SR_TXOK, CCSR);
+                       }
+               
+                       if (chip_status & SR_RXOK) {
+                               DEBUGMSG("(c%d)stat: Received a Message successfully\n",
+                                       pchip->chip_idx);
+                               c_can_write_reg_w(pchip, chip_status & ~SR_RXOK, CCSR);
+                       }
+               
+                   #ifdef CAN_WITH_STATISTICS
+                       // Errors to statistics
+                       switch( chip_status & 0x07 )
+                       {
+                               case SRLEC_NE: // No error
+                                       break;
+                               case SRLEC_SE: // Stuff error
+                                       pchip->stat.cntStuffErr++;
+                                       break;
+                               case SRLEC_FE: // Form error
+                                       pchip->stat.cntFormErr++;
+                                       break;
+                               case SRLEC_AE: // Ack error
+                                       pchip->stat.cntAckErr++;
+                                       break;
+                               case SRLEC_B1: // Bit 1 error
+                                       pchip->stat.cntBit1Err++;
+                                       break;
+                               case SRLEC_B0: // Bit 0 error
+                                       pchip->stat.cntBit0Err++;
+                                       break;
+                               case SRLEC_CR: // CRC error
+                                       pchip->stat.cntCrcErr++;
+                                       break;
+                               case 7: // unused
+                                       break;
+                       }
+                   #endif /*CAN_WITH_STATISTICS*/
+                       //return; // continue?
+               } else {
+                       if (irqreg >0 && irqreg <33) {
+                               struct msgobj_t *pmsgobj;
+                               int idxobj;
+                               
+                               //get id
+                               idxobj = irqreg-1;
+                               pmsgobj=pchip->msgobj[idxobj];
+                               
+                               //DEBUGMSG( "Interrupt handler: addr=%lx devid=%lx irqreq=%x status=0x%x\n",
+                               //            (unsigned long)pchip->vbase_addr + iIRQ,
+                               //      (unsigned long)dev_id,
+                               //      irqreg,
+                               //      statreg );
+                               //
+                               spin_lock( &c_can_if1lock );
+               
+                               //Message Lost Check
+                               if (c_can_if1_busycheck(pchip)) ; /*??????????*/
+                               c_can_write_reg_w(pchip, msgLstReadMaskCM, CCIF1CM);
+                               c_can_write_reg_w(pchip, idxobj+1, CCIF1CR);
+               
+                               if (c_can_if1_busycheck(pchip)) ; /*??????????*/
+                               tempCntlReg = c_can_read_reg_w( pchip, CCIF1DMC );
+
+                               if (tempCntlReg & IFXMC_MSGLST) {
+                                       CANMSG("(c%dm%d)Chip lost a message\n",
+                                               pchip->chip_idx, pmsgobj->object);
+                                   #ifdef CAN_WITH_STATISTICS
+                                       pchip->stat.cntMsgLst++;
+                                   #endif /*CAN_WITH_STATISTICS*/
+                      
+                                       //Reset Message Lost Bit
+                                       tempCntlReg = tempCntlReg & (~IFXMC_MSGLST);
+                                       c_can_write_reg_w(pchip, tempCntlReg, CCIF1DMC);
+                                       c_can_write_reg_w(pchip, msgLstWriteMaskCM, CCIF1CM);
+                                       c_can_write_reg_w(pchip, idxobj+1, CCIF1CR);
+                               }
+
+                               //transfer Message Object to IF1 Buffer
+                               if (c_can_if1_busycheck(pchip)) ;
+                               c_can_write_reg_w(pchip, readMaskCM, CCIF1CM);
+                               c_can_write_reg_w(pchip, idxobj+1, CCIF1CR);
+
+                               if (c_can_if1_busycheck(pchip)) ;
+                               if (c_can_read_reg_w(pchip, CCIF1A2) & IFXARB2_DIR) {
+                                       spin_unlock( &c_can_if1lock );
+                                       c_can_irq_write_handler(pchip,idxobj);
+                               } else {
+                                       if (can_msgobj_test_fl(pmsgobj,RX_MODE_EXT)) {
+                                               id0=c_can_read_reg_w(pchip, CCIF1A1);
+                                               id1=(c_can_read_reg_w(pchip, CCIF1A2)&0x1FFF)<<16;
+                                               msgid= id0|id1;
+                                       } else {
+                                               msgid=((c_can_read_reg_w(pchip, CCIF1A2)&0x1FFC)>>2)&0x7FF;
+                                       }
+                                       spin_unlock( &c_can_if1lock );
+
+                                       spin_lock(&hardware_p->rtr_lock);
+                                       while ( rtr_search != NULL )
+                                       {
+                                               if ( rtr_search->id == msgid ) {
+                                                       break;
+                                               }
+                                               rtr_search = rtr_search->next;
+                                       }
+                                       spin_unlock(&hardware_p->rtr_lock);
+
+                                       spin_lock( &c_can_if1lock );
+
+                                       //transfer Message Object to IF1 Buffer
+                                       if (c_can_if1_busycheck(pchip)) ;
+                                       c_can_write_reg_w(pchip, readMaskCM, CCIF1CM);
+                                       c_can_write_reg_w(pchip, idxobj+1, CCIF1CR);
+                       
+                                       if (c_can_if1_busycheck(pchip)) ;
+                       
+                                       if ( ( rtr_search != NULL ) && (rtr_search->id == msgid ) ) {
+                                               c_can_irq_rtr_handler( pchip, idxobj, msgid );
+                                       } else {
+                                               c_can_irq_read_handler( pchip, idxobj, msgid );
+                                       }
+                                       spin_unlock( &c_can_if1lock );
+                       
+                                       //}
+                               }
+                       //else
+                       }
+               //if
+               }
+               // Get irq status again
+               irqreg = c_can_read_reg_w( pchip, CCINTR );
+       }
+       return CAN_IRQ_HANDLED;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// c_can_irq_rtr_handler
+//
+
+void c_can_irq_rtr_handler( struct chip_t *pchip, int idxobj, u32 msgid   )
+{
+       short int i=0;
+       struct rtr_id *prtr_search = hardware_p->rtr_queue;
+       union c_can_data rtrData;
+       
+       spin_lock( &hardware_p->rtr_lock );
+       
+       prtr_search->rtr_message->id = msgid;
+       prtr_search->rtr_message->length =
+               ( c_can_read_reg_w( pchip, CCIF1DMC ) & 0x000f);
+       
+       // Fetch message bytes
+       if (prtr_search->rtr_message->length > 0)
+               rtrData.wdata[0] = c_can_read_reg_w(pchip, CCIF1DA1);
+       if (prtr_search->rtr_message->length > 2)
+               rtrData.wdata[1] = c_can_read_reg_w(pchip, CCIF1DA2);
+       if (prtr_search->rtr_message->length > 4)
+               rtrData.wdata[2] = c_can_read_reg_w(pchip, CCIF1DB1);
+       if (prtr_search->rtr_message->length > 6)
+               rtrData.wdata[3] = c_can_read_reg_w(pchip, CCIF1DB2);
+       
+       for ( i=0; i<prtr_search->rtr_message->length; i++ ) {
+               prtr_search->rtr_message->data[ i ] = rtrData.bdata[i];
+       }
+       
+       spin_unlock( &hardware_p->rtr_lock );
+       wake_up_interruptible( &prtr_search->rtr_wq );
+       return;
+}
+