]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/hcan2.c
The LinCAN driver license unified according to DCE FEE CTU head and superiors request.
[lincan.git] / lincan / src / hcan2.c
index 96b104361330119ebd1a697f0ff43b5af089c7e0..23d15ab84149f29300914603b6375fc3df2b04f9 100644 (file)
@@ -1,7 +1,37 @@
-/* hcan2.c
- * Linux CAN-bus device driver.
- * This software is released under the GPL-License.
- */
+/**************************************************************************/
+/* File: hcan2.c - Renesas SH7760 HCAN2 controller support                */
+/*                                                                        */
+/* LinCAN - (Not only) Linux CAN bus driver                               */
+/* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz>   */
+/* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz>             */
+/* Copyright (C) 2007-2008 Martin Petera <peterm4@fel.cvut.cz>            */
+/* Funded by OCERA and FRESCOR IST projects                               */
+/* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl>      */
+/*                                                                        */
+/* LinCAN is free software; you can redistribute it and/or modify it      */
+/* under terms of the GNU General Public License as published by the      */
+/* Free Software Foundation; either version 2, or (at your option) any    */
+/* later version.  LinCAN 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 LinCAN; see file     */
+/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave,  */
+/* Cambridge, MA 02139, USA.                                              */
+/*                                                                        */
+/* To allow use of LinCAN in the compact embedded systems firmware        */
+/* and RT-executives (RTEMS for example), main authors agree with next    */
+/* special exception:                                                     */
+/*                                                                        */
+/* Including LinCAN header files in a file, instantiating LinCAN generics */
+/* or templates, or linking other files with LinCAN objects to produce    */
+/* an application image/executable, does not by itself cause the          */
+/* resulting application image/executable to be covered by                */
+/* the GNU General Public License.                                        */
+/* This exception does not however invalidate any other reasons           */
+/* why the executable file might be covered by the GNU Public License.    */
+/* Publication of enhanced or derived LinCAN files is required although.  */
+/**************************************************************************/
 
 #include "../include/can.h"
 #include "../include/can_sysdep.h"
@@ -32,12 +62,15 @@ void hcan2_setup_mbox4read(struct msgobj_t * obj);
 void hcan2_setup_ctrl_regs(struct canmsg_t * msg, uint16_t * ctrl0, uint16_t * ctrl1, uint16_t * ctrl2);
 
 int hcan2_compare_msg(struct msgobj_t * obj, struct canmsg_t * msg);
-
+void hcan2_notifyRXends(struct msgobj_t * obj, int what);
 /* Enable folowing IRQs
  * HCAN2_IRR_DFRI = Data Frame Received Interrupt Flag
  * HCAN2_IRR_MBEI = Mailbox Empty Interrupt Flag
+ *
+ * and Errors:
+ * Bus Off, Error Passive, Message Overrun/Overwrite
  */
-uint16_t IRQs = ~(HCAN2_IRR_DFRI + HCAN2_IRR_MBEI);
+uint16_t IRQs = ~(HCAN2_IRR_DFRI + HCAN2_IRR_MBEI + HCAN2_IRR_BOI + HCAN2_IRR_EPI + HCAN2_IRR_MOOI);
 /* 1 - mask interrupt, 0 - interrupt not masked */
 
 int hcan2_chip_config(struct canchip_t *chip)
@@ -308,18 +341,19 @@ int hcan2_standard_mask(struct canchip_t *chip, unsigned short code, unsigned sh
        else
            return -ENODEV;
 
-       chip->chip_data = (void*)0; /* reset mbox number */
+       chip->chip_data = (void*)0;     /* reset mbox number */
 
 
        ctrl0 = ((code & 0x07ff) << 4);
        lafm0 = ((mask & 0x07ff) << 4);
+       lafm0 |= 0x0003;                /* ignore Ext ID 17:16  */
        
        can_write_reg_w(chip, ctrl0, (int) obj->obj_base_addr + HCAN2_MB_CTRL0);
        can_write_reg_w(chip, 0x0000, (int) obj->obj_base_addr + HCAN2_MB_CTRL1);
        can_write_reg_w(chip, lafm0, (int) obj->obj_base_addr + HCAN2_MB_MASK);
        can_write_reg_w(chip, 0xffff, (int) obj->obj_base_addr + HCAN2_MB_MASK + 2);
 
-       DEBUGMSG("Set standard_mask [id:0x%04x, m:0x%04x]\n", code, mask);
+       DEBUGMSG("MB%02d: Set standard_mask [id:0x%04x, m:0x%04x]\n", obj_idx, code, lafm0);
        return 0;
 }
 
@@ -352,7 +386,7 @@ int hcan2_extended_mask(struct canchip_t *chip, unsigned long code, unsigned lon
        can_write_reg_w(chip, lafm0, (int) obj->obj_base_addr + HCAN2_MB_MASK);
        can_write_reg_w(chip, lafm1, (int) obj->obj_base_addr + HCAN2_MB_MASK + 2);
        
-       DEBUGMSG("Set extended_mask [id:0x%08x, m:0x%08x]\n", (uint32_t)code, (uint32_t)mask);
+       DEBUGMSG("MB%02d: Set extended_mask [id:0x%08x, m:0x%08x]\n", obj_idx, (uint32_t)code, (uint32_t)mask);
     
         return 0;
 }
@@ -397,7 +431,6 @@ int hcan2_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, struct
 int hcan2_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg)
 {
        unsigned obj_bit;
-       int i;
        int b_addr = ((obj->object - 1) / 16) * (-2); 
 
        obj_bit = (1 << ((obj->object - 1) % 16));
@@ -405,17 +438,7 @@ int hcan2_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t
 /*     CANMSG("Sending message [obj: %d]\n", obj->object - 1); */
 
        can_write_reg_w(chip, obj_bit, b_addr + HCAN2_TXPR0);
-                       
-       /* Waits until chip sends message */
-       i = 0;
-       while (hcan2_check_tx_stat(chip) && ((i++) <= MAX_TRANSMIT_WAIT_LOOPS))
-           udelay(200);
 
-       if (i >= MAX_TRANSMIT_WAIT_LOOPS) {
-               CANMSG("Failed while waiting for Transmitt finished\n");
-               return -ENODEV;
-       }
-       
 /*     CANMSG("Message sent [obj: %d]\n", obj->object - 1); */
        return 0;
 }
@@ -432,11 +455,25 @@ int hcan2_irq_handler(int irq, struct canchip_t *chip)
        uint16_t irq_reg, idx;
        short loop_cnt = MAX_IRQ_WAIT_LOOPS;
        uint32_t rxdf, txdf;
-       struct msgobj_t * obj;
+
+/*
+       HCAN2_IRR_TCMI  - Time Compare Match Register
+       HCAN2_IRR_TOI   - Time Overrun Interrupt
+       HCAN2_IRR_WUBA  - Wake-up on Bus Activity
+       HCAN2_IRR_MOOI  - Message Overrun/Overwrite Interrupt Flag
+       HCAN2_IRR_MBEI  - Messagebox Empty Interrupt Flag
+       HCAN2_IRR_OF    - Overload Frame
+       HCAN2_IRR_BOI   - Bus Off Interrupt Flag
+       HCAN2_IRR_EPI   - Error Passive Interrupt Flag
+       HCAN2_IRR_ROWI  - Receive Overload Warning Interrupt Flag
+       HCAN2_IRR_TOWI  - Transmit Overload Warining Interrupt Flag
+       HCAN2_IRR_RFRI  - Remote Frame Request Interrupt Flag
+       HCAN2_IRR_DFRI  - Data Frame Received Interrupt Flag
+       HCAN2_IRR_RHSI  - Reset/Halt/Sleep Interrupt Flag */
 
        irq_reg = can_read_reg_w(chip, HCAN2_IRR);
        DEBUGMSG("irq: %d, chip base addr: 0x%08x\n", irq, (uint32_t)chip->chip_base_addr);
-/*     DEBUGMSG("IRQ Handler: HCAN2_IRR: 0x%04x\n", irq_reg); */
+       DEBUGMSG("IRQ Handler: HCAN2_IRR: 0x%04x\n", irq_reg); //*/
 
        do {
 
@@ -445,37 +482,85 @@ int hcan2_irq_handler(int irq, struct canchip_t *chip)
                        return CANCHIP_IRQ_STUCK;
                }
 
+               /* Received message */
                if (irq_reg & HCAN2_IRR_DFRI)
-               {       /* Received message */
-                       rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
-                           can_read_reg_w(chip, HCAN2_RXPR0);
+               {
+                       rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
+                               can_read_reg_w(chip, HCAN2_RXPR0);
 
-                       DEBUGMSG("Received message [0x%08x]\n", rxdf);
+                       while(rxdf) {
+                               DEBUGMSG("Received message [0x%08x]\n", rxdf);
 
-                       while (rxdf) {
-                           /* find the message object */
-                           for (idx = 0; (idx < chip->max_objects) && !(rxdf & (1<<idx)); idx++) { }
+                               /* find the message object */
+                               for (idx = 0; (idx < chip->max_objects) && rxdf; idx++)
+                                       if ((rxdf & (1<<idx))) {
+                                               hcan2_irq_read_handler(chip, chip->msgobj[idx]);
+                                               /* RXPR flag for this msgobj is cleared during irq_read_handler*/
+                                               rxdf &= ~(1 << idx);
+                                       }
 
-                           /* realy i got one? */
-                           if (idx < chip->max_objects)
-                               hcan2_irq_read_handler(chip, chip->msgobj[idx]);
-                           else
-                               break;
 
-                           /* clear RXPR flag for this msgobj */
-                           can_write_reg_w(chip, (1 << (idx % 16)), HCAN2_RXPR0 - 2*(idx / 16));
+                               DEBUGMSG("Before reset flags [0x%08x]\n", rxdf);
+                               rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
+                                       can_read_reg_w(chip, HCAN2_RXPR0);
+                       }
+               }
 
-                           rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
-                               can_read_reg_w(chip, HCAN2_RXPR0);
+               /* Error: Bus Off */
+               if (irq_reg & HCAN2_IRR_BOI) {
+                       CANMSG("Error: entering BUS OFF state\nstatus register: 0x%02x irq register: 0x%02x\n",
+                               can_read_reg_w(chip, HCAN2_GSR), irq_reg);
+
+                       /* notify all RX/TX ends */
+                       for (idx = 0; idx < chip->max_objects; idx++) {
+                               /* notify TX */
+                               chip->msgobj[idx]->ret=-1;
+                               if(chip->msgobj[idx]->tx_slot)
+                                       canque_notify_inends(chip->msgobj[idx]->tx_qedge,
+                                               CANQUEUE_NOTIFY_ERROR);
+                               /* notify RX */
+                               hcan2_notifyRXends(chip->msgobj[idx], CANQUEUE_NOTIFY_ERROR);
                        }
-                           
-                   /* reset HCAN2_IRR_DFRI flag */
-                   can_write_reg_w(chip, irq_reg & ~HCAN2_IRR_DFRI, HCAN2_IRR);
+
+                       /* reset flag - by writing '1' */
+                       can_write_reg_w(chip, HCAN2_IRR_BOI, HCAN2_IRR);
                }
 
-               if (irq_reg & (HCAN2_IRR_MBEI))
+               /* Warning: Error Passive */
+               if (irq_reg & HCAN2_IRR_EPI) {
+                       uint16_t tecrec;
+                       tecrec = can_read_reg_w(chip, HCAN2_TECREC);
+
+                       CANMSG("Warning: entering ERROR PASSIVE state\nTEC: %d REC: %d\n",
+                               (uint16_t)((tecrec >> 8) & 0x00ff), (uint16_t)(tecrec & 0x00ff));
+                       
+                       /* Show warning only */
+
+                       /* reset flag - by writing '1' */
+                       can_write_reg_w(chip, HCAN2_IRR_EPI, HCAN2_IRR);
+               }
+
+               /* Message Overrun/Overwritten */
+               if (irq_reg & HCAN2_IRR_MOOI) { 
+                       /* put get Unread Message Status Register */
+                       rxdf =  (can_read_reg_w(chip, HCAN2_UMSR1) << 16) + can_read_reg_w(chip, HCAN2_UMSR0);
+
+                       /* find the message object */
+                       for (idx = 0; (idx < chip->max_objects) && !(rxdf & (1<<idx)); idx++) { }
+                       
+                       CANMSG("Error: MESSAGE OVERRUN/OVERWRITTEN [MB: %d]\n",idx);
+                       
+                       /* notify only injured RXqueue-end */
+                       if (idx < chip->max_objects) 
+                               hcan2_notifyRXends(chip->msgobj[idx], CANQUEUE_NOTIFY_ERROR);
+
+                       /* reset flag */
+                       can_write_reg_w(chip, (1 << (idx % 16)), HCAN2_UMSR0 - 2 * (idx / 16));
+               }
+
+               /* Mailbox empty - after message was sent */
+               if (irq_reg & HCAN2_IRR_MBEI)
                {
-                   /* Mailbox empty - after message was sent */
                    txdf = (can_read_reg_w(chip, HCAN2_TXACK1) << 16) +
                        can_read_reg_w(chip, HCAN2_TXACK0);
 
@@ -489,21 +574,16 @@ int hcan2_irq_handler(int irq, struct canchip_t *chip)
                        can_write_reg_w(chip, 0xffff, HCAN2_ABACK1);
                        return CANCHIP_IRQ_HANDLED;
                    } 
-                   
-                   obj = chip->msgobj[idx];
 
                    /* Clear TXACK flag */                  
                    can_write_reg_w(chip, 1 << (idx % 16), HCAN2_TXACK0 - 2 * (idx / 16));
 
                    /* sends message */     
-                   hcan2_wakeup_tx(chip, obj);
+                   hcan2_wakeup_tx(chip, chip->msgobj[idx]);
                }
 
-               irq_reg=can_read_reg_w(chip, HCAN2_IRR);
-       } while(irq_reg & (HCAN2_IRR_MBEI | HCAN2_IRR_DFRI));
-
-       /* reset ALL tinterrupt flags */
-       can_write_reg_w(chip, irq_reg, HCAN2_IRR);
+               irq_reg = can_read_reg_w(chip, HCAN2_IRR);
+       } while(irq_reg & ~IRQs);
 
        return CANCHIP_IRQ_HANDLED;
 }
@@ -549,6 +629,19 @@ int hcan2_check_tx_stat(struct canchip_t *chip)
                return 1;
 }
 
+/* Note: this checks TX status of concrete messagebox */
+int hcan2_check_MB_tx_stat(struct canchip_t *chip, struct msgobj_t *obj)
+{
+       /* Transmition is complete return 0 - no error */
+       
+       /* MB1-MB15 are in CANTXPR0 and MB16-MB31 are in CANTXPR1
+          CANTXPR0 = CANTXPR1 + 0x0002
+          MB0 - receive only */
+
+       char id = obj->object - 1;
+       return (can_read_reg_w(chip, HCAN2_TXPR0 - 2 * (id / 16)) & (1 << (id & 0x00ff)));
+}
+
 int hcan2_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
 {
        DEBUGMSG("WakeUP TX\n");
@@ -559,14 +652,18 @@ int hcan2_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
        can_preempt_disable();
        
        can_msgobj_set_fl(obj,TX_REQUEST);
-       while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+       if(!can_msgobj_test_and_set_fl(obj,TX_LOCK) &&
+               !hcan2_check_MB_tx_stat(chip, obj))
+       {       /* enable transmition only if MB is empty */
                can_msgobj_clear_fl(obj,TX_REQUEST);
 
                hcan2_irq_write_handler(chip, obj);
        
                can_msgobj_clear_fl(obj,TX_LOCK);
-               if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
        }
+       else
+               can_msgobj_clear_fl(obj,TX_REQUEST);
+
 
        can_preempt_enable();
 
@@ -628,22 +725,22 @@ void hcan2_irq_read_handler(struct canchip_t *chip, struct msgobj_t *obj)
        }
        else
            obj->rx_msg.id = (ctrl0 & HCAN2_MBCT0_STDID)>>4;
-           
+
 
        if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
        for (i = 0; i < len; i++)
        {
                /* rcanqueue_ends_filt_conjuctionead 16bit data - two data bytes*/
                data = can_read_reg_w(chip, (int) obj->obj_base_addr + HCAN2_MB_DATA1 + i);
-               obj->rx_msg.data[i] = (data & 0xff00) >> 8;
-               if (++i < len) obj->rx_msg.data[i] = data & 0x00ff;
+               obj->rx_msg.data[i] = (data & 0xff00) >> 8;             // one data byte
+               if (++i < len) obj->rx_msg.data[i] = data & 0x00ff;     // second data byte
        }
 
        /* Computes correct offset address of register from MSGBOX_IDX and RTR flag
         * result is one of these:
         * HCAN2_RXPR1, HCAN2_RXPR0, HCAN2_RFPR1, HCAN2_RFPR0
         */
-       flag_addr = ((ctrl0 & HCAN2_MBCT0_RTR) << 3) + HCAN2_RXPR0 - ((obj->object - 1) / 16) * 2;
+       flag_addr = HCAN2_RXPR0 - (int)((obj->object - 1) / 16) * 2;
                
        /* Reset flag by writing 1 to its position */
        can_write_reg_w(chip, (1 << ((obj->object - 1) % 16)), flag_addr);
@@ -989,3 +1086,10 @@ int hcan2_compare_msg(struct msgobj_t * obj, struct canmsg_t * msg)
 
        return ((ctrl0 ^ c0) || (ctrl2 ^ c2));
 }
+
+void hcan2_notifyRXends(struct msgobj_t * obj, int what){
+       struct canque_edge_t * edge;
+       canque_for_each_inedge(obj->qends, edge){
+               canque_notify_outends(edge, what);
+       }
+}