]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/mcp2515.c
Support for asynchronous SPI messages and callbacks
[lincan.git] / lincan / src / mcp2515.c
index 10e4c5f05c5f183f777e103389f98d0d4252ef82..51ff64b32a3ceade98b3baacc53a6ac0ad78b74d 100644 (file)
 #define RESET_CMD  0xc0
 #define READ_CMD   0x03
 #define WRITE_CMD  0x02
+#define READ_RX0_CMD 0x90
+#define READ_RX1_CMD 0x94
 #define BITMOD_CMD 0x05
 
+#define MCP2515_SPI_ASYNC
+
+enum MCP2515_OPS {
+       NOOP,
+       READ_RX0,
+       READ_RX1,
+       READ_CANINTF,
+       READ_ERR
+};
+
+static struct workqueue_struct *mcp2515_wq;
+
+static void mcp2515_async_callback(void *data, uint8_t count);
+static void tx_handler(struct canchip_t *chip,  struct msgobj_t *obj);
 
 /*****************************************************************************/
 /*                       SPI ACCESS FUNCTIONS                                */
@@ -122,6 +138,241 @@ static void bitmod_reg(struct canchip_t *chip, unsigned addr,
 }
 
 
+/*****************************************************************************/
+static void bitmod_reg_async(struct canchip_t *chip, unsigned addr, 
+                      unsigned char mask, unsigned char data, uint8_t opcode)
+{
+       struct can_spi_async_t async;
+
+       async.chip = chip;
+       async.opcode = opcode;
+       async.tx_buf[0] = BITMOD_CMD;
+       async.tx_buf[1] = addr;
+       async.tx_buf[2] = mask;
+       async.tx_buf[3] = data;
+       async.len = 4;
+       can_spi_async_transfer(chip, mcp2515_async_callback, &async, 1, 0);
+
+#if DEBUG
+       DEBUGMSG("reg[0x%02x]<=0x%02x mask=0x%02x\n",addr,(unsigned)data,(unsigned)mask);
+#endif
+}
+
+/*****************************************************************************/
+static void read_async(struct canchip_t *chip, unsigned startAddr, size_t len, uint8_t opcode)
+{
+       struct can_spi_async_t async[2];
+
+       async[0].chip = chip;
+       async[0].opcode = opcode;
+       async[0].tx_buf[0] = READ_CMD;
+       async[0].tx_buf[1] = startAddr;
+       async[0].len = len+2;
+       
+       if (opcode == READ_CANINTF){
+               async[1].chip = chip;
+               async[1].opcode = opcode;
+               async[1].tx_buf[0] = READ_CMD;
+               async[1].tx_buf[1] = startAddr;
+               async[1].len = len+2;
+               can_spi_async_transfer(chip, mcp2515_async_callback, async, 2, 1);
+       }
+       else
+               can_spi_async_transfer(chip, mcp2515_async_callback, async, 1, 0);
+}
+
+/*****************************************************************************/
+inline void read_rx_async(struct canchip_t *chip, unsigned rxbuf,struct msgobj_t *obj)
+{
+       struct can_spi_async_t async = {
+               .chip = chip,
+               .opcode = rxbuf?READ_RX1:READ_RX0,
+               .tx_buf[0] = rxbuf?READ_RX1_CMD:READ_RX0_CMD,
+               .len = sizeof(MCP2515_FRAME)+1,
+               .obj = obj,
+       };
+
+       can_spi_async_transfer(chip, mcp2515_async_callback, &async, 1, 0);
+}
+
+/*****************************************************************************/
+inline void read_err_async(struct canchip_t *chip)
+{
+       struct can_spi_async_t async;
+
+       async.chip = chip;
+       async.opcode = READ_ERR;
+       async.tx_buf[0] = READ_CMD;
+       async.tx_buf[1] = MCP2515_EFLG;
+       async.len = 3;
+
+       can_spi_async_transfer(chip, mcp2515_async_callback, &async, 1, 0);
+}
+
+/*****************************************************************************/
+static void txwq_handler(struct work_struct *work)
+{
+       MCP2515_PRIV *priv = container_of(work,MCP2515_PRIV,txwq_handler);
+       struct canchip_t *chip = priv->chip;
+       int n;
+       if (chip == NULL)
+               return;
+       n = find_first_bit(priv->txfree, MCP2515_TXBUF_NUM);
+       while (n<MCP2515_TXBUF_NUM){
+               clear_bit(n,priv->txfree);
+               tx_handler(chip,chip->msgobj[n]);
+               n = find_first_bit(priv->txfree, MCP2515_TXBUF_NUM);
+       }
+}
+       
+
+static int mcp2515_async_canintf_reread = 0;
+/*****************************************************************************/
+static void mcp2515_async_callback(void *data,uint8_t count){
+       struct can_spi_async_t *async = (struct can_spi_async_t *)data;
+       struct canchip_t *chip;
+       struct msgobj_t *obj;
+       int i;
+       if (async == NULL)
+               return;
+       
+       for (i=0;i<count;i++){
+               switch (async[i].opcode){
+                       case NOOP : break;
+                       case READ_RX0 : 
+                       case READ_RX1 : {
+                               uint8_t len;
+                               MCP2515_FRAME *frame = NULL;
+                               
+                               chip = async[i].chip;
+                               if (chip == NULL) return;
+                               obj = async[i].obj;
+                               if (obj == NULL) return;
+
+                               if (async[i].rx_buf == NULL)
+                                       panic("async[%d].rx_buf is null",i);
+                               frame = (MCP2515_FRAME *)(async[i].rx_buf+1); //The received data is 1 byte late
+
+                               if(frame->sidl & mcpIDE) {
+                                       DEBUGMSG("extended frame\n");
+                                       obj->rx_msg.id = 
+                                               ((uint32_t)frame->eid0)              | 
+                                               ((uint32_t)frame->eid8)        <<  8 |
+                                               ((uint32_t)frame->sidl & 0x03) << 16 |
+                                               ((uint32_t)frame->sidl & 0xe0) << 13 |
+                                               ((uint32_t)frame->sidh)        << 21;                           
+                                       obj->rx_msg.flags = MSG_EXT | ((frame->dlc & mcpRTR) ? MSG_RTR : 0);
+                               } 
+                               else {
+                                       DEBUGMSG("standard frame\n");
+                                       obj->rx_msg.id =
+                                               ((uint32_t)frame->sidl) >> 5 |
+                                               ((uint32_t)frame->sidh) << 3;
+                                       obj->rx_msg.flags = ((frame->sidl & mcpSRR) ? MSG_RTR : 0);
+                               }
+                                       
+                               len = frame->dlc & mcpDLC_MASK;
+                               if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
+                               obj->rx_msg.length = len;               
+                                       
+                               memcpy(obj->rx_msg.data,frame->data,len);
+                                       
+                               /* fill CAN message timestamp */
+//                             can_filltimestamp(&obj->rx_msg.timestamp);              
+                               memcpy(&obj->rx_msg.timestamp, &async->timestamp, sizeof(canmsg_tstamp_t));
+                               canque_filter_msg2edges(obj->qends, &obj->rx_msg);
+                               break;
+                       }
+                       case READ_CANINTF : {
+                               uint8_t flags = async[i].rx_buf[2];
+                               MCP2515_PRIV *priv;
+
+                               chip = async[i].chip;
+                               if (chip == NULL) break;
+                               priv=(MCP2515_PRIV *)(chip->chip_data);
+                               if (priv == NULL) break;
+                               
+                               if ((count > i+1) && (async[i+1].opcode == READ_CANINTF) && (async[++i].rx_buf[2] ^ flags)){
+                                       DEBUGMSG("Found differences in two consequent interrupt flag readings. Reading again...\n");
+                                       read_async(chip,MCP2515_CANINTF,1,READ_CANINTF);
+                                       break;
+                               }
+                               
+                               if((flags == 0) && (mcp2515_async_canintf_reread)){
+                                       mcp2515_async_canintf_reread = 0;
+                                       enable_irq(chip->chip_irq);
+                                       break;
+                               }
+                               else if((flags == 0)){
+                                       mcp2515_async_canintf_reread++;
+                                       read_async(chip,MCP2515_CANINTF,1,READ_CANINTF);
+                                       break;
+                               }
+                               mcp2515_async_canintf_reread = 0;
+                               
+                               DEBUGMSG("mcp251x_irq_work_handler:%s%s%s%s%s%s%s%s\n",
+                                      (flags & mcpRX0INT) ? " RX0":"",
+                                      (flags & mcpRX1INT) ? " RX1":"",
+                                      (flags & mcpTX0INT) ? " TX0":"",
+                                      (flags & mcpTX1INT) ? " TX1":"",
+                                      (flags & mcpTX2INT) ? " TX2":"",
+                                      (flags & mcpERRINT) ? " ERR":"",         
+                                      (flags & mcpWAKINT) ? " WAK":"",
+                                      (flags & mcpMERREINT) ? " MERRE":"");
+
+
+                               if(flags & mcpRX0INT) read_rx_async(chip,0,chip->msgobj[0]);
+                               if(flags & mcpRX1INT) read_rx_async(chip,1,chip->msgobj[0]);
+                               if(flags & mcpTX0INT) {
+                                       set_bit(0,priv->txfree);
+                                       queue_work(mcp2515_wq,&priv->txwq_handler);
+                               }
+                               if(flags & mcpTX1INT) {
+                                       set_bit(1,priv->txfree);
+                                       queue_work(mcp2515_wq,&priv->txwq_handler);
+                               }
+                               if(flags & mcpTX2INT) {
+                                       set_bit(2,priv->txfree);
+                                       queue_work(mcp2515_wq,&priv->txwq_handler);
+                               }
+
+                               if(flags & mcpERRINT) read_err_async(chip);
+                               if(flags & mcpMERREINT) (priv->errcnt.merre)++;
+                               if(flags & mcpWAKINT) (priv->wakeint_cnt)++;
+                               
+                               bitmod_reg_async(chip, MCP2515_CANINTF, flags & ~(mcpRX0INT|mcpRX1INT), 0, NOOP);
+
+                               read_async(chip,MCP2515_CANINTF,1,READ_CANINTF);
+                               break;
+                       }
+                       case READ_ERR : {
+                               uint8_t error = async[i].rx_buf[2];
+                               MCP2515_PRIV *priv;
+
+                               chip = async[i].chip;
+                               if (chip == NULL) break;
+                               priv=(MCP2515_PRIV *)(chip->chip_data);
+                               if (priv == NULL) break;
+                               
+                               if(error & mcpRX0OVR) {
+                                       (priv->errcnt.rx0ovr)++;
+                                       CANMSG("can: RX0OVR\n");
+                               }
+                               if(error & mcpRX1OVR) (priv->errcnt.rx1ovr)++;
+                               if(error & mcpTXBO)   (priv->errcnt.txbo)++;
+                               if(error & mcpTXEP)   (priv->errcnt.txep)++;
+                               if(error & mcpRXEP)   (priv->errcnt.rxep)++;
+                               if(error & mcpTXWAR)  (priv->errcnt.txwar)++;
+                               if(error & mcpRXWAR)  (priv->errcnt.rxwar)++;
+                               if(error & mcpEWARN)  (priv->errcnt.ewarn)++;
+
+                               break;
+                       }
+                       default : CANMSG("OMAP2_SPICAN got unknown asynchronous response");
+               }
+       }
+}
+
 /*****************************************************************************/
 /*                              PROC INTERFACE                               */
 /*****************************************************************************/
@@ -168,7 +419,6 @@ static void rx_handler(struct canchip_t *chip, int bufNo, struct msgobj_t *obj)
        if(chip == NULL) panic("rx_handler: chip==NULL");
        if(obj == NULL) panic("rx_handler: obj==NULL");
 
-
        /* get all frame data */
        if(bufNo == 0){
                read_block(chip,MCP2515_RXB0SIDH,sizeof(MCP2515_FRAME),&frame);
@@ -182,18 +432,18 @@ static void rx_handler(struct canchip_t *chip, int bufNo, struct msgobj_t *obj)
        if(frame.sidl & mcpIDE) {
                DEBUGMSG("extended frame\n");
                obj->rx_msg.id = 
-                       ((uint32_t)frame.eid0)              | 
-                       ((uint32_t)frame.eid8)        <<  8 |
-                       ((uint32_t)frame.sidl & 0x03) << 16 |
-                       ((uint32_t)frame.sidl & 0xe0) << 13 |
-                       ((uint32_t)frame.sidh)        << 21;                            
+                       ((uint32_t)frame.sidl & 0xe0) >>  5 |
+                       ((uint32_t)frame.sidh)        <<  3 |
+                       ((uint32_t)frame.eid0)        << 11 | 
+                       ((uint32_t)frame.eid8)        << 19 |
+                       ((uint32_t)frame.sidl & 0x03) << 27;
                obj->rx_msg.flags = MSG_EXT | ((frame.dlc & mcpRTR) ? MSG_RTR : 0);
        } 
        else {
                DEBUGMSG("standard frame\n");
                obj->rx_msg.id =
-                       ((uint32_t)frame.sidl) >> 5 |
-                       ((uint32_t)frame.sidh) << 3;
+                       ((uint32_t)frame.sidl & 0xe0) >>  5 |
+                       ((uint32_t)frame.sidh)        <<  3;
                obj->rx_msg.flags = ((frame.sidl & mcpSRR) ? MSG_RTR : 0);
        }
                
@@ -214,10 +464,10 @@ static void tx_handler(struct canchip_t *chip,  struct msgobj_t *obj)
        int cmd;
 
        if(chip == NULL) panic("tx_handler: chip==NULL");
-       if(obj == NULL) panic("tx_handler: obj==NULL");
-       
+       if(obj == NULL) panic("tx_handler: obj==NULL");
+#ifndef MCP2515_SPI_ASYNC
        bitmod_reg(chip,MCP2515_CANINTF, mcpTX0INT, ~mcpTX0INT);
-
+#endif
        
        if(obj->tx_slot) {
                /* Do local transmitted message distribution if enabled */
@@ -291,6 +541,10 @@ static void irq_work(void *data)
        priv=(MCP2515_PRIV *)(chip->chip_data);
        if (priv == NULL)
                return;
+       
+#ifdef MCP2515_SPI_ASYNC
+               read_async(chip,MCP2515_CANINTF,1,READ_CANINTF);
+#else
        while(1) {
 
                flags = read_reg(chip, MCP2515_CANINTF);
@@ -320,8 +574,8 @@ static void irq_work(void *data)
 
 //             bitmod_reg(chip, MCP2515_CANINTF, flags, 0);
        }
-       
        enable_irq(chip->chip_irq);
+#endif /* MCP2515_SPI_ASYNC */
 }
 
 /*****************************************************************************/
@@ -476,21 +730,12 @@ int mcp2515_chip_config(struct canchip_t *chip)
        write_block(chip,MCP2515_TXB0DATA,8,pat0);
        for (i=0;i<10;i++){
                read_block(chip,MCP2515_TXB0DATA,8,pat1);
-//             *(pat1) = read_reg(chip,MCP2515_TXB0DATA);
-//             *(pat1+1) = read_reg(chip,MCP2515_TXB0DATA+1);
-//             *(pat1+2) = read_reg(chip,MCP2515_TXB0DATA+2);
-//             *(pat1+3) = read_reg(chip,MCP2515_TXB0DATA+3);
-//             *(pat1+4) = read_reg(chip,MCP2515_TXB0DATA+4);
-//             *(pat1+5) = read_reg(chip,MCP2515_TXB0DATA+5);
-//             *(pat1+6) = read_reg(chip,MCP2515_TXB0DATA+6);
-//             *(pat1+7) = read_reg(chip,MCP2515_TXB0DATA+7);
 
                if(memcmp(pat0,pat1,8)) {
                        CANMSG("mcp2515_chip_config: chip #%d not found\n",
                               chip->chip_idx);
                        CANMSG("Requested: Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X\n",pat0[0],pat0[1],pat0[2],pat0[3],pat0[4],pat0[5],pat0[6],pat0[7]);
                        CANMSG("Obtained : Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X\n",pat1[0],pat1[1],pat1[2],pat1[3],pat1[4],pat1[5],pat1[6],pat1[7]);
-//                     return -ENODEV;
                }
                else 
                        break;
@@ -503,8 +748,12 @@ int mcp2515_chip_config(struct canchip_t *chip)
        CANMSG("Found mcp2515:%d\n",chip->chip_idx);
        
 
-       if (mcp2515_extended_mask(chip,0x00000000, 0xffffffff))
+       // Accept only most the dominant message
+       if (mcp2515_extended_mask(chip,0xffffffff, 0xffffffff))
                return -ENODEV;
+/*     // Accept only most the dominant message
+       if (mcp2515_extended_mask(chip,0xffffffff, 0x00000000))
+               return -ENODEV;*/
        
        if (!chip->baudrate) chip->baudrate=1000000;
        if (mcp2515_baud_rate(chip,chip->baudrate,chip->clock,0,75,0))
@@ -530,25 +779,38 @@ int mcp2515_chip_config(struct canchip_t *chip)
  */
 int mcp2515_extended_mask(struct canchip_t *chip, unsigned long code, unsigned  long mask)
 {
+       char c_filter[4],c_mask[4];
        DEBUGMSG("mcp2515_extended_mask\n");
 
-#if 0
+// #if 0
        if (mcp2515_enable_configuration(chip))
                return -ENODEV;
 
-// LSB to +3, MSB to +0        
-       for(i=SJA_PeliCAN_AC_LEN; --i>=0;) {
-               can_write_reg(chip,code&0xff,SJAACR0+i);
-               can_write_reg(chip,mask&0xff,SJAAMR0+i);
-               code >>= 8;
-               mask >>= 8;
-       }
-
-       DEBUGMSG("Setting acceptance code to 0x%lx\n",(unsigned long)code);
-       DEBUGMSG("Setting acceptance mask to 0x%lx\n",(unsigned long)mask);
+       c_filter[0] = (code >> 3) & 0xFF;
+       c_filter[1] = ((code << 5) & 0xE0 ) | 1<<3 | ((code >> 16) & 0x03);
+       c_filter[2] = (code >> 8) & 0xFF;
+       c_filter[3] = (code) & 0xFF;
+       write_block(chip, MCP2515_RXF0SIDH, 4, c_filter);
+       write_block(chip, MCP2515_RXF2SIDH, 4, c_filter);
+       write_block(chip, MCP2515_RXF3SIDH, 4, c_filter);
+       write_block(chip, MCP2515_RXF4SIDH, 4, c_filter);
+       write_block(chip, MCP2515_RXF5SIDH, 4, c_filter);
+       c_filter[1] = ((code << 5) & 0xE0 ) | 0<<3 | ((code >> 16) & 0x03);
+       write_block(chip, MCP2515_RXF1SIDH, 4, c_filter);
+       c_mask[0] = (mask >> 3) & 0xFF;
+       c_mask[1] = ((mask << 5) & 0xE0 ) | ((mask >> 16) & 0x03);
+       c_mask[2] = (mask >> 8) & 0xFF;
+       c_mask[3] = (mask) & 0xFF;
+       write_block(chip, MCP2515_RXM0SIDH, 4, c_mask);
+       write_block(chip, MCP2515_RXM1SIDH, 4, c_mask);
+
+       CANMSG("Setting acceptance code to 0x%lx\n",(unsigned long)code);
+       CANMSG("Setting acceptance mask to 0x%lx\n",(unsigned long)mask);
+//     DEBUGMSG("Setting acceptance code to 0x%lx\n",(unsigned long)code);
+//     DEBUGMSG("Setting acceptance mask to 0x%lx\n",(unsigned long)mask);
 
        mcp2515_disable_configuration(chip);
-#endif
+// #endif
        return 0;
 }
 
@@ -632,7 +894,7 @@ int mcp2515_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw_in,
                        continue;
                }
                readreg = read_reg(chip, MCP2515_CNF3);
-               if (readreg & CNF3_PHSEG2_MASK != (u8)((ps2-1))){
+               if ((readreg & CNF3_PHSEG2_MASK) != (u8)((ps2-1))){
                        CANMSG("Wrong value in CNF3 - sent: 0x%X, received: 0x%X\n",(ps2-1) | brp,readreg & CNF3_PHSEG2_MASK);
                        continue;
                }
@@ -671,7 +933,7 @@ int mcp2515_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj)
        return 0;
 }
 
-#define MAX_TRANSMIT_WAIT_LOOPS 10
+#define MAX_TRANSMIT_WAIT_LOOPS 20
 /**
  * mcp2515_pre_write_config: - prepares message object for message transmission
  * @chip: pointer to chip state structure
@@ -716,32 +978,32 @@ int mcp2515_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
                return -EIO;
        }
 
-
-       frame.sidl = (msg->id << 5) || 
-               ((msg->flags & MSG_EXT) ? mcpEXIDE : 0) ||
-               ((msg->id >> 16) & mcpEID_MASK);
-       
        len = msg->length;
        if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
 
        if(msg->flags & MSG_EXT) {
-               frame.sidh = msg->id >> 21;
-               frame.sidl = (msg->id << 5) | ((msg->id >> 27) & 0x3) | mcpEXIDE;
-               frame.eid8 = msg->id >> 19;
-               frame.eid0 = 13;
+               frame.sidh = (msg->id >> 3) & 0xFF;
+               frame.sidl = ((msg->id << 5) | ((msg->id >> 27) & mcpEID_MASK) | mcpEXIDE) & 0xFF;
+               frame.eid8 = (msg->id >> 19) & 0xFF;
+               frame.eid0 = (msg->id >> 11) & 0xFF;
                frame.dlc = len | ((msg->flags & MSG_RTR) ? mcpRTR : 0);
                memcpy(frame.data, msg->data, len);
        }
        else {
-               frame.sidh = msg->id >> 21;
-               frame.sidl = (msg->id << 5) | ((msg->id >> 27) & 0x3);
-               frame.eid8 = msg->id >> 19;
-               frame.eid0 = 13;
+               frame.sidh = (msg->id >> 3) & 0xFF;
+               frame.sidl = ((msg->id << 5) | ((msg->id >> 27) & mcpEID_MASK)) & 0xFF;
+               frame.eid8 = (msg->id >> 19) & 0xFF;
+               frame.eid0 = (msg->id >> 11) & 0xFF;
                frame.dlc = len | ((msg->flags & MSG_RTR) ? mcpRTR : 0);
                memcpy(frame.data, msg->data, len);
        }
 
-       write_block(chip, MCP2515_TXB0SIDH, len+5, &frame);
+       if (obj == chip->msgobj[0])
+               write_block(chip, MCP2515_TXB0SIDH, len+5, &frame);
+       if (obj == chip->msgobj[1])
+               write_block(chip, MCP2515_TXB1SIDH, len+5, &frame);
+       if (obj == chip->msgobj[2])
+               write_block(chip, MCP2515_TXB2SIDH, len+5, &frame);
 
        can_spi_release_bus(chip);
 
@@ -765,7 +1027,21 @@ int mcp2515_send_msg(struct canchip_t *chip, struct msgobj_t *obj,
        DEBUGMSG("mcp2515_send_msg\n");
 
        can_spi_acquire_bus(chip,1);    
-       bitmod_reg(chip, MCP2515_TXB0CTRL, mcpTXREQ, mcpTXREQ);
+#ifdef MCP2515_SPI_ASYNC
+       if (obj == chip->msgobj[0])
+               bitmod_reg_async(chip, MCP2515_TXB0CTRL, mcpTXREQ, mcpTXREQ, NOOP);
+       if (obj == chip->msgobj[1])
+               bitmod_reg_async(chip, MCP2515_TXB1CTRL, mcpTXREQ, mcpTXREQ, NOOP);
+       if (obj == chip->msgobj[2])
+               bitmod_reg_async(chip, MCP2515_TXB2CTRL, mcpTXREQ, mcpTXREQ, NOOP);
+#else
+       if (obj == chip->msgobj[0])
+               bitmod_reg(chip, MCP2515_TXB0CTRL, mcpTXREQ, mcpTXREQ);
+       if (obj == chip->msgobj[1])
+               bitmod_reg(chip, MCP2515_TXB1CTRL, mcpTXREQ, mcpTXREQ);
+       if (obj == chip->msgobj[2])
+               bitmod_reg(chip, MCP2515_TXB2CTRL, mcpTXREQ, mcpTXREQ);
+#endif
        can_spi_release_bus(chip);
 
        return 0;
@@ -869,13 +1145,22 @@ int mcp2515_attach_to_chip(struct canchip_t *chip)
 {
        DEBUGMSG("mcp2515_attach_to_chip\n");
        /* Initialize delayed interrupt processing */
+       mcp2515_wq = create_singlethread_workqueue("lincan_mcp2515");
+       if (mcp2515_wq == NULL)
+               return -1;
+       
  #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
        INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler), 
                  workqueue_handler, 
-                 NULL);
+                 &(((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
+       INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->txwq_handler), 
+                 txwq_handler, 
+                 &(((MCP2515_PRIV *)(chip->chip_data))->txwq_handler);
  #else
        INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler), 
                  workqueue_handler);
+       INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->txwq_handler), 
+                 txwq_handler);
  #endif
 
        tasklet_init(&(((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler),
@@ -902,8 +1187,9 @@ int mcp2515_release_chip(struct canchip_t *chip)
        
        chip->flags &= ~CHIP_ATTACHED;
        DEBUGMSG("Flush workqueue.\n");
+       cancel_delayed_work(&((MCP2515_PRIV *)(chip->chip_data))->txwq_handler);
        cancel_delayed_work(&((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
-       flush_scheduled_work();
+       flush_workqueue(mcp2515_wq);
 
        DEBUGMSG("Kill tasklets.\n");
        tasklet_kill(&((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler);
@@ -911,6 +1197,8 @@ int mcp2515_release_chip(struct canchip_t *chip)
        mcp2515_stop_chip(chip);
        can_write_reg(chip, sjaDISABLE_INTERRUPTS, SJAIER);
 #endif
+       destroy_workqueue(mcp2515_wq);
+
        return 0;
 }
 
@@ -994,11 +1282,17 @@ int mcp2515_irq_handler(int irq, struct canchip_t *chip)
        if (~chip->flags & CHIP_ATTACHED)
                return CANCHIP_IRQ_ACCEPTED;
        
+       if(can_spi_acquire_bus(chip, 0)){
+#ifdef MCP2515_SPI_ASYNC
+       /* let the SPI subsystem read interrupt status immediately - the response runs in callbacks */
+               read_async(chip,MCP2515_CANINTF,1,READ_CANINTF);
+#else
        /* do work in tasklet if bus is immediately available */
-       if(can_spi_acquire_bus(chip, 0))
-               tasklet_schedule(&((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler);
+               tasklet_hi_schedule(&((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler);
+#endif /* MCP2515_SPI_ASYNC */
+       }
        else /* do work in workqueue */
-               schedule_work(&((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
+               queue_work(mcp2515_wq,&((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
 
        disable_irq(chip->chip_irq);
        return CANCHIP_IRQ_HANDLED;
@@ -1035,6 +1329,16 @@ int mcp2515_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
                        obj->tx_retry_cnt=0;
                        tx_handler(chip, obj);
                }
+               else{
+#ifdef MCP2515_SPI_ASYNC
+               /* let the SPI subsystem read interrupt status immediately - the response runs in callbacks */
+                       read_async(chip,MCP2515_CANINTF,1,READ_CANINTF);
+#else
+               /* do work in tasklet if bus is immediately available */
+                       tasklet_hi_schedule(&((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler);
+#endif /* MCP2515_SPI_ASYNC */
+               }
+                       
                can_spi_release_bus(chip);      
 
                can_msgobj_clear_fl(obj,TX_LOCK);
@@ -1088,7 +1392,7 @@ int mcp2515_fill_chipspecops(struct canchip_t *chip)
 {
        DEBUGMSG("mcp2515_fill_chipspecops\n");
        chip->chip_type="mcp2515";
-       chip->max_objects=2;
+       chip->max_objects=3;
        mcp2515_register(chip->chipspecops);
        return 0;
 }