]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/sja1000p.c
The first enhanced version of Linux CAN-bus driver for OCERA project
[lincan.git] / lincan / src / sja1000p.c
index f95f1a78c0ea83e71f1f10114087e95cf83c4483..deb1c894d4bbfb5ed836b2f022fc20c1710aac70 100644 (file)
 #include "../include/main.h"
 #include "../include/sja1000p.h"
 
-struct chip_t *chip_irq=NULL;
-struct candevice_t *device_irq=NULL;
-struct canfifo_t *fifo_irq=NULL;
-void (*put_reg)(unsigned char data, unsigned long address);
-unsigned (*get_reg)(unsigned long address);
-
-
-
 int sja1000p_enable_configuration(struct chip_t *chip)
 {
        int i=0;
@@ -41,8 +33,8 @@ int sja1000p_enable_configuration(struct chip_t *chip)
        flags=can_read_reg(chip,SJAMOD);
 
        while ((!(flags & MOD_RM)) && (i<=10)) {
-               can_write_reg(chip, MOD_RM, SJAMOD);
-// TODO: chinfigurable MOD_AFM (32/16 bit acceptance filter)
+               can_write_reg(chip, MOD_RM, SJAMOD);
+// TODO: configurable MOD_AFM (32/16 bit acceptance filter)
 // config MOD_LOM (listen only)
                udelay(100);
                i++;
@@ -64,9 +56,10 @@ int sja1000p_disable_configuration(struct chip_t *chip)
 
        flags=can_read_reg(chip,SJAMOD);
 
-       while ( (flags & MOD_RM) && (i<=10) ) {
+       while ( (flags & MOD_RM) && (i<=50) ) {
+// could be as long as 11*128 bit times after buss-off
                can_write_reg(chip, 0, SJAMOD);
-// TODO: chinfigurable MOD_AFM (32/16 bit acceptance filter)
+// TODO: configurable MOD_AFM (32/16 bit acceptance filter)
 // config MOD_LOM (listen only)
                udelay(100);
                i++;
@@ -203,39 +196,53 @@ void sja1000p_read(struct chip_t *chip, struct canfifo_t *fifo) {
        do {
                flags = can_read_reg(chip,SJAFRM);
                if(flags&FRM_FF) {
-                       fifo->buf_rx_entry[fifo->head].id =
+                       fifo->rx_writep->id =
                                (can_read_reg(chip,SJAID0)<<21) +
                                (can_read_reg(chip,SJAID1)<<13) +
                                (can_read_reg(chip,SJAID2)<<5) +
                                (can_read_reg(chip,SJAID3)>>3);
                        datastart = SJADATE;
                } else {
-                       fifo->buf_rx_entry[fifo->head].id =
+                       fifo->rx_writep->id =
                                (can_read_reg(chip,SJAID0)<<3) +
                                (can_read_reg(chip,SJAID1)>>5);
                        datastart = SJADATS;
                }
-               fifo->buf_rx_entry[fifo->head].flags =
+               fifo->rx_writep->flags =
                        ((flags & FRM_RTR) ? MSG_RTR : 0) |
                        ((flags & FRM_FF) ? MSG_EXT : 0);
                len = flags & FRM_DLC_M;
                for(i=0; i< len; i++) {
-                       fifo->buf_rx_entry[fifo->head].data[i]=
-                               can_read_reg(chip,datastart+i);
+                       fifo->rx_writep->data[i]=can_read_reg(chip,datastart+i);
                }
-               fifo->buf_rx_entry[fifo->head].length = len;
-               fifo->head++; fifo->head %= MAX_BUF_LENGTH;
-// FIXME: what if fifo->head == fifo->tail again ?
+               fifo->rx_writep->length = len;
+
+               fifo->rx_writep++;
+               if (fifo->rx_writep >= fifo->buf_rx_entry + MAX_BUF_LENGTH)
+                       fifo->rx_writep = fifo->buf_rx_entry;
+
+// FIXME: what if fifo->rx_writep == fifo->rx_readp again ?
+
                can_write_reg(chip, CMR_RRB, SJACMR);
        } while (can_read_reg(chip, SJASR) & SR_RBS);
 }
 
 int sja1000p_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
 {
-       int i;
-       i=can_read_reg(chip,SJASR);
+       int status;
+       status=can_read_reg(chip,SJASR);
        
-       if (!(i&SR_RBS)) {
+       if(status  & SR_BS) {
+               /* Try to recover from error condition */
+               DEBUGMSG("sja1000p_pre_read_config bus-off recover 0x%x\n",status);
+               sja1000p_enable_configuration(chip);
+               can_write_reg(chip, 0, SJARXERR);
+               can_write_reg(chip, 0, SJATXERR1);
+               can_read_reg(chip, SJAECC);
+               sja1000p_disable_configuration(chip);
+       }
+
+       if (!(status&SR_RBS)) {
                return 0;
        }
 
@@ -251,13 +258,23 @@ int sja1000p_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
 {
        int i=0; 
        unsigned int id;
+       int status;
 
        /* Wait until Transmit Buffer Status is released */
-       while ( !(can_read_reg(chip, SJASR) & SR_TBS) && 
+       while ( !((status=can_read_reg(chip, SJASR)) & SR_TBS) && 
                                                i++<MAX_TRANSMIT_WAIT_LOOPS) {
                udelay(i);
        }
        
+       if(status & SR_BS) {
+               /* Try to recover from error condition */
+               DEBUGMSG("sja1000p_pre_write_config bus-off recover 0x%x\n",status);
+               sja1000p_enable_configuration(chip);
+               can_write_reg(chip, 0, SJARXERR);
+               can_write_reg(chip, 0, SJATXERR1);
+               can_read_reg(chip, SJAECC);
+               sja1000p_disable_configuration(chip);
+       }
        if (!(can_read_reg(chip, SJASR) & SR_TBS)) {
                CANMSG("Transmit timed out, cancelling\n");
 // here we should check if there is no write/select waiting for this
@@ -292,9 +309,8 @@ int sja1000p_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
                        can_write_reg(chip, msg->data[i], SJADATE+i);
                }
        } else {
-               id=msg->id >> 5;
-               can_write_reg(chip, id & 0xff, SJAID0);
-               id >>= 8;
+               id=msg->id<<5;
+               can_write_reg(chip, (id >> 8) & 0xff, SJAID0);
                can_write_reg(chip, id & 0xff, SJAID1);
                for(i=0; i < msg->length; i++) {
                        can_write_reg(chip, msg->data[i], SJADATS+i);
@@ -379,47 +395,100 @@ int sja1000p_config_irqs(struct chip_t *chip, short irqs)
        return -ENOSYS;
 }
 
-void sja1000p_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+void sja1000p_irq_write_handler(struct chip_t *chip, struct canfifo_t *fifo)
 {
-       int irq_register;
-       chip_irq=(struct chip_t *)dev_id;
-       device_irq=(struct candevice_t *)chip_irq->hostdevice;
+       fifo->tx_readp++;
+       if (fifo->tx_readp >= fifo->buf_tx_entry + MAX_BUF_LENGTH)
+               fifo->tx_readp = fifo->buf_tx_entry;
+       if (fifo->tx_readp == fifo->tx_writep) { // Output buffer is empty
+               fifo->tx_in_progress = 0;
+               if (waitqueue_active(&fifo->writeq)) {
+                       chip->msgobj[0]->ret = 0; //CHECKME or 26?
+                       wake_up_interruptible(&fifo->writeq);
+               }
+               return;
+       }
+       if (chip->chipspecops->pre_write_config(chip, chip->msgobj[0],
+                               fifo->tx_readp)) {
+               if (waitqueue_active(&fifo->writeq)) {
+                       chip->msgobj[0]->ret = -1;
+                       wake_up_interruptible(&fifo->writeq);
+                       return;
+               }
+       }
+       if (chip->chipspecops->send_msg(chip, chip->msgobj[0],
+               fifo->tx_readp)) {
+               if (waitqueue_active(&fifo->writeq)) {
+                       chip->msgobj[0]->ret = -1;
+                       wake_up_interruptible(&fifo->writeq);
+                       return;
+               }
+       }
 
-       put_reg=device_irq->hwspecops->write_register;
-       get_reg=device_irq->hwspecops->read_register;
+}
+
+#define MAX_RETR 10
 
-       irq_register=get_reg(chip_irq->chip_base_addr+SJAIR);
+void sja1000p_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       int irq_register, status, error_code;
+       int static retransmitted=0; /* FIXME - should go into chip struct */
+       struct chip_t *chip=(struct chip_t *)dev_id;
+       struct canfifo_t *fifo;
+
+       irq_register=can_read_reg(chip,SJAIR);
 //     DEBUGMSG("sja1000_irq_handler: SJAIR:%02x\n",irq_register);
 //     DEBUGMSG("sja1000_irq_handler: SJASR:%02x\n",
-//                                     get_reg(chip_irq->chip_base_addr+SJASR));
+//                                     can_read_reg(chip,SJASR));
 
        if ((irq_register & (IR_BEI|IR_EPI|IR_DOI|IR_EI|IR_TI|IR_RI)) == 0)
                return;
 
-       fifo_irq=chip_irq->msgobj[0]->fifo;
+       if(!chip->msgobj[0]->flags & BUFFERS_ALLOCATED) {
+               CANMSG("sja1000p_irq_handler: called with device closed, irq_register 0x%02x\n", irq_register);
+               return;
+       }
+       fifo=chip->msgobj[0]->fifo;
 
        if ((irq_register & IR_RI) != 0) {
-               sja1000p_read(chip_irq,fifo_irq);
-               chip_irq->msgobj[0]->ret = 0;
-               if (waitqueue_active(&fifo_irq->readq))
-                       wake_up_interruptible(&fifo_irq->readq);
+               sja1000p_read(chip,fifo);
+               chip->msgobj[0]->ret = 0;
+               if (waitqueue_active(&fifo->readq))
+                       wake_up_interruptible(&fifo->readq);
        }
        if ((irq_register & IR_TI) != 0) {
-               chip_irq->msgobj[0]->ret = 0;
-               if (waitqueue_active(&fifo_irq->writeq))
-                       wake_up_interruptible(&fifo_irq->writeq);
+               chip->msgobj[0]->ret = 0;
+               sja1000p_irq_write_handler(chip,fifo);
        }
        if ((irq_register & (IR_EI|IR_BEI|IR_EPI|IR_DOI)) != 0) { 
                // Some error happened
-               CANMSG("Error: status register: 0x%x irq_register: 0x%02x\n",
-                       get_reg(chip_irq->chip_base_addr+SJASR), irq_register);
+               status=can_read_reg(chip,SJASR);
+               error_code=can_read_reg(chip,SJAECC);
+               CANMSG("Error: status register: 0x%x irq_register: 0x%02x error: 0x%02x\n",
+                       status, irq_register, error_code);
 // FIXME: chip should be brought to usable state. Transmission cancelled if in progress.
 // Reset flag set to 0 if chip is already off the bus. Full state report
-               chip_irq->msgobj[0]->ret=-1;
-               if (waitqueue_active(&fifo_irq->writeq))
-                       wake_up_interruptible(&fifo_irq->writeq);
-               if (waitqueue_active(&fifo_irq->readq))
-                       wake_up_interruptible(&fifo_irq->readq);
+               chip->msgobj[0]->ret=-1;
+               
+               if(error_code == 0xd9) {
+                       chip->msgobj[0]->ret= -ENXIO;
+                       /* no such device or address - no ACK received */
+               }
+               if(retransmitted++>MAX_RETR) {
+                       can_write_reg(chip, CMR_AT, SJACMR); // cancel any transmition
+                       retransmitted = 0;
+               }
+               if(status&SR_BS) {
+                       CANMSG("bus-off, resetting sja1000p\n");
+                       can_write_reg(chip, 0, SJAMOD);
+               }
+               
+               if (waitqueue_active(&fifo->writeq))
+                       wake_up_interruptible(&fifo->writeq);
+               if (waitqueue_active(&fifo->readq))
+                       wake_up_interruptible(&fifo->readq);
+       } else {
+               retransmitted=0;
        }
 
        return;