]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/mcp2515.c
Spican1 support added
[lincan.git] / lincan / src / mcp2515.c
diff --git a/lincan/src/mcp2515.c b/lincan/src/mcp2515.c
new file mode 100644 (file)
index 0000000..10e4c5f
--- /dev/null
@@ -0,0 +1,1094 @@
+/* mcp2515.c
+ * Linux CAN-bus device driver.
+ * Written by Sergei Sharonov email:sharonov@halliburton.com
+ * sja1000p was used as a prototype
+ * This software is released under the GPL-License.
+ * Version lincan-0.3.2  15 Feb 2006
+ */
+#include "../include/can.h"
+#include "../include/can_sysdep.h"
+#include "../include/main.h"
+#include "../include/mcp2515.h"
+
+#define DEBUG 0
+
+#define RESET_CMD  0xc0
+#define READ_CMD   0x03
+#define WRITE_CMD  0x02
+#define BITMOD_CMD 0x05
+
+
+/*****************************************************************************/
+/*                       SPI ACCESS FUNCTIONS                                */
+/*****************************************************************************/
+static unsigned char read_reg(struct canchip_t *chip, unsigned addr)
+{
+       uint8_t *command, val;
+
+       command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+       command[0] = READ_CMD;
+       command[1] = addr;
+       can_spi_transfer(chip,command,command,3);
+       val = command[2];
+
+#if DEBUG
+       DEBUGMSG("reg[0x%02x]=>0x%02x\n",addr,(unsigned)val);
+#endif
+       return val;
+}
+
+/*****************************************************************************/
+static int read_block(struct canchip_t *chip, unsigned startAddr,
+                               size_t len, void *data)
+{
+       uint8_t *command;
+
+       command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+       memset(command,0,SPI_BUF_LEN);
+       command[0] = READ_CMD;
+       command[1] = startAddr;
+       can_spi_transfer(chip, command, command, len+2);
+       memcpy(data, command+2, len);
+
+#if DEBUG
+       {
+               int i;
+               DEBUGMSG("reg[0x%02x..]=>",startAddr);  
+               for(i=0;i<len;i++) DEBUGMSG(" 0x%02x",(unsigned)((uint8_t*)data)[i]);
+               DEBUGMSG("\n");
+       }
+#endif
+       return len;
+}
+
+/*****************************************************************************/
+static void write_reg(struct canchip_t *chip, unsigned addr, unsigned char data)
+{
+       unsigned char *command;
+
+       command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+       command[0] = WRITE_CMD;
+       command[1] = addr;
+       command[2] = data;
+       can_spi_transfer(chip,command,command,3);
+
+#if DEBUG
+       DEBUGMSG("reg[0x%02x]<=0x%02x\n",addr,(unsigned)data);
+#endif
+}
+
+
+/*****************************************************************************/
+static int write_block(struct canchip_t *chip, unsigned startAddr, 
+                       size_t len, void *data)
+{
+       uint8_t *command;
+
+       command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+       command[0] = WRITE_CMD;
+       command[1] = startAddr;
+       memcpy(command+2,data,len);
+       can_spi_transfer(chip, command, command, len+2);
+
+#if DEBUG
+       {
+               int i;
+               DEBUGMSG("reg[0x%02x..]<=",startAddr);  
+               for(i=0;i<len;i++) 
+                       DEBUGMSG(" 0x%02x", (unsigned)((uint8_t*)data)[i]);
+               DEBUGMSG("\n");
+       }
+#endif
+
+       return len;
+}
+
+/*****************************************************************************/
+static void bitmod_reg(struct canchip_t *chip, unsigned addr, 
+                      unsigned char mask, unsigned char data)
+{
+       unsigned char *command;
+
+       command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+       command[0] = BITMOD_CMD;
+       command[1] = addr;
+       command[2] = mask;
+       command[3] = data;
+       can_spi_transfer(chip,command,command,4);
+
+#if DEBUG
+       DEBUGMSG("reg[0x%02x]<=0x%02x mask=0x%02x\n",addr,(unsigned)data,(unsigned)mask);
+#endif
+}
+
+
+/*****************************************************************************/
+/*                              PROC INTERFACE                               */
+/*****************************************************************************/
+int mcp2515_get_info(struct canchip_t *chip, char *buf)
+{
+       MCP2515_PRIV *priv=(MCP2515_PRIV *)(chip->chip_data);   
+       int len=0;
+       uint8_t opmode;
+
+       can_spi_acquire_bus(chip, 1);
+       opmode = read_reg(chip,MCP2515_CANSTAT) & mcpMOD_MASK;
+       can_spi_release_bus(chip);
+       len += sprintf(buf+len,"opmode : %s%s%s%s%s\n",
+                      (opmode == mcpMOD_NORM)     ? "norm"     : "",
+                      (opmode == mcpMOD_SLEEP)    ? "sleep"    : "",
+                      (opmode == mcpMOD_LOOPBACK) ? "loopback" : "",
+                      (opmode == mcpMOD_LISTEN)   ? "listen"   : "",   
+                      (opmode == mcpMOD_CONFIG)   ? "config"   : "");  
+
+       len += sprintf(buf+len,"spi ch : %d\n",chip->spi_channel);
+       len += sprintf(buf+len,"rx0ovr : %u\n",priv->errcnt.rx0ovr);
+       len += sprintf(buf+len,"rx1ovr : %u\n",priv->errcnt.rx1ovr);
+       len += sprintf(buf+len,"txbo   : %u\n",priv->errcnt.txbo);
+       len += sprintf(buf+len,"txep   : %u\n",priv->errcnt.txep);
+       len += sprintf(buf+len,"rxep   : %u\n",priv->errcnt.rxep);
+       len += sprintf(buf+len,"txwar  : %u\n",priv->errcnt.txwar);
+       len += sprintf(buf+len,"rxwar  : %u\n",priv->errcnt.rxwar);
+       len += sprintf(buf+len,"ewarn  : %u\n",priv->errcnt.ewarn);
+       len += sprintf(buf+len,"merre  : %u\n",priv->errcnt.merre);
+       len += sprintf(buf+len,"wakeup : %u\n",priv->wakeint_cnt);
+
+       return len;
+}      
+       
+
+/*****************************************************************************/
+/*                               IRQ FUNCTIONS                               */
+/*****************************************************************************/
+static void rx_handler(struct canchip_t *chip, int bufNo, struct msgobj_t *obj) 
+{
+       int len;
+       MCP2515_FRAME frame;
+
+       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);
+               bitmod_reg(chip,MCP2515_CANINTF, mcpRX0INT, ~mcpRX0INT);
+       }
+       else{
+               read_block(chip,MCP2515_RXB1SIDH,sizeof(MCP2515_FRAME),&frame); 
+               bitmod_reg(chip,MCP2515_CANINTF, mcpRX1INT, ~mcpRX1INT);
+       }
+
+       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);              
+       canque_filter_msg2edges(obj->qends, &obj->rx_msg);
+}
+
+/*****************************************************************************/
+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");
+       
+       bitmod_reg(chip,MCP2515_CANINTF, mcpTX0INT, ~mcpTX0INT);
+
+       
+       if(obj->tx_slot) {
+               /* Do local transmitted message distribution if enabled */
+               if (processlocal){
+                       /* fill CAN message timestamp */
+                       can_filltimestamp(&obj->tx_slot->msg.timestamp);
+                       
+                       obj->tx_slot->msg.flags |= MSG_LOCAL;
+                       canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
+               }
+               /* Free transmitted slot */
+               canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+               obj->tx_slot=NULL;
+       }
+       
+       can_msgobj_clear_fl(obj,TX_PENDING);
+       cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
+       if(cmd<0)
+               return;
+       can_msgobj_set_fl(obj,TX_PENDING);
+
+       if (chip->chipspecops->pre_write_config(chip, obj, &obj->tx_slot->msg)) {
+               obj->ret = -1;
+               canque_notify_inends(obj->tx_qedge, CANQUEUE_NOTIFY_ERRTX_PREP);
+               canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+               obj->tx_slot=NULL;
+               return;
+       }
+       if (chip->chipspecops->send_msg(chip, obj, &obj->tx_slot->msg)) {
+               obj->ret = -1;
+               canque_notify_inends(obj->tx_qedge, CANQUEUE_NOTIFY_ERRTX_SEND);
+               canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+               obj->tx_slot=NULL;
+               return;
+       }
+
+}
+
+/*****************************************************************************/
+static void errint_handler(struct canchip_t *chip)
+{
+       uint8_t error;
+       MCP2515_PRIV *priv=(MCP2515_PRIV *)(chip->chip_data);
+
+       error = read_reg(chip, MCP2515_EFLG);
+       bitmod_reg(chip,MCP2515_CANINTF, mcpERRINT, ~mcpERRINT);
+
+
+       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)++;
+}
+
+/*****************************************************************************/
+static void irq_work(void *data)
+{
+       struct canchip_t *chip=(struct canchip_t *)data;
+       MCP2515_PRIV *priv;
+       uint8_t flags;
+       
+       if (chip == NULL)
+               return;
+       priv=(MCP2515_PRIV *)(chip->chip_data);
+       if (priv == NULL)
+               return;
+       while(1) {
+
+               flags = read_reg(chip, MCP2515_CANINTF);
+               if(flags == 0) break;
+               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) rx_handler(chip,0,chip->msgobj[0]);
+               if(flags & mcpRX1INT) rx_handler(chip,1,chip->msgobj[0]);
+               if(flags & mcpTX0INT) tx_handler(chip,chip->msgobj[0]);
+               if(flags & mcpTX1INT) tx_handler(chip,chip->msgobj[1]);
+               if(flags & mcpTX2INT) tx_handler(chip,chip->msgobj[2]);
+               if(flags & mcpERRINT) errint_handler(chip);
+
+               if(flags & mcpMERREINT){ 
+                       bitmod_reg(chip,MCP2515_CANINTF, mcpMERREINT, ~mcpMERREINT);
+                       (priv->errcnt.merre)++;
+               }
+               if(flags & mcpWAKINT)   (priv->wakeint_cnt)++;
+
+//             bitmod_reg(chip, MCP2515_CANINTF, flags, 0);
+       }
+       
+       enable_irq(chip->chip_irq);
+}
+
+/*****************************************************************************/
+
+static void tasklet_handler(unsigned long data)
+{
+       struct canchip_t *chip=(struct canchip_t *)data;
+       if (chip == NULL)
+               return;
+
+       /* Already acquired bus, go do work */
+       irq_work(chip);
+       can_spi_release_bus(chip);
+}
+
+/*****************************************************************************/
+static void workqueue_handler(struct work_struct *work)
+{
+       MCP2515_PRIV *priv = container_of(work,MCP2515_PRIV,workqueue_handler);
+       struct canchip_t *chip = priv->chip;
+       if (chip == NULL)
+               return;
+       can_spi_acquire_bus(chip, 1);
+       irq_work(chip);
+       can_spi_release_bus(chip);
+}
+       
+/*****************************************************************************/
+/*                          EXPORT FUNCTIONS                                 */
+/*****************************************************************************/
+int mcp2515_reset_chip(struct canchip_t *chip)
+{
+       unsigned char *command;
+
+       DEBUGMSG("reset chip\n");
+
+       command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+       command[0] = RESET_CMD;
+       can_spi_acquire_bus(chip,1);
+       can_spi_transfer(chip,command,command,1);
+       can_spi_release_bus(chip);
+
+#if DEBUG
+       DEBUGMSG("reset mcp2515:%d\n",chip->chip_idx);
+#endif
+       return 0;
+}      
+
+/**
+ * mcp2515_enable_configuration - enable chip configuration mode
+ * @chip: pointer to chip state structure
+ */
+int mcp2515_enable_configuration(struct canchip_t *chip)
+{
+       int i;
+       enum mcp2515_MOD stat;
+
+       DEBUGMSG("mcp2515_enable_configuration\n");
+
+       can_disable_irq(chip->chip_irq);
+
+       can_spi_acquire_bus(chip,1);
+       for(i=0;i<10;i++) {
+               bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_CONFIG);
+               stat = read_reg(chip,MCP2515_CANSTAT);
+               if((stat & mcpMOD_MASK) == mcpMOD_CONFIG) {
+                       can_spi_release_bus(chip);
+                       return 0;
+               }
+               udelay(100);
+       }
+       can_spi_release_bus(chip);
+       
+       CANMSG("Failed to set cfg mode\n");
+       can_enable_irq(chip->chip_irq);
+       return -ENODEV;
+}
+
+/**
+ * mcp2515_disable_configuration - disable chip configuration mode
+ * @chip: pointer to chip state structure
+ */
+int mcp2515_disable_configuration(struct canchip_t *chip)
+{
+       int i;
+       enum mcp2515_MOD stat;
+
+       DEBUGMSG("mcp2515_disable_configuration\n");
+
+       can_spi_acquire_bus(chip,1);
+       for(i=0;i<10;i++) {
+               bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_NORM);
+               stat = read_reg(chip,MCP2515_CANSTAT);
+               if((stat & mcpMOD_MASK) == mcpMOD_NORM) {
+                       can_enable_irq(chip->chip_irq);
+                       can_spi_release_bus(chip);
+                       return 0;
+               }
+               udelay(100);
+       }
+       can_spi_release_bus(chip);
+       
+       CANMSG("Failed to set normal mode\n");
+       return -ENODEV;
+}
+
+
+/**
+ * mcp2515_chip_config: - can chip configuration
+ * @chip: pointer to chip state structure
+ *
+ * This function configures chip and prepares it for message
+ * transmission and reception. The function resets chip,
+ * resets mask for acceptance of all messages by call to
+ * mcp2515_extended_mask() function and then 
+ * computes and sets baudrate with use of function mcp2515_baud_rate().
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_chip_config(struct canchip_t *chip)
+{
+       uint8_t pat0[8]={0x00,0xff,0xaa,0x55,0x0f,0xf0,0x3c,0xc3};
+       uint8_t pat1[8];
+       int i;
+
+       DEBUGMSG("mcp2515_chip_config\n");
+
+       ((MCP2515_PRIV *)(chip->chip_data))->chip = chip;
+       
+       if (mcp2515_enable_configuration(chip))
+               return -ENODEV;
+
+       /* Acquire SPI bus */
+       can_spi_acquire_bus(chip,1);
+
+       /* Set TXnRTS pins as digital inputs */
+       write_reg(chip,MCP2515_TXRTSCTRL,0);
+
+       /* Set RXnBF pins as digital outputs, b0=low, b1=high */
+       write_reg(chip, MCP2515_BFPCTRL,
+                 mcpB0BFE | mcpB1BFE | /* mcpB0BFS | */ mcpB1BFS);
+       
+       /* Ensure, that interrupts are disabled even on the chip level now */
+       write_reg(chip, MCP2515_CANINTE, 0);
+
+       /* Configure second receive buffer for rollover */
+       bitmod_reg(chip, MCP2515_RXB0CTRL, mcpBUKT, mcpBUKT);
+
+       
+       /* Simple check for chip presence */
+       memset(pat1,0,8);
+       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;
+       }
+       if (i==10)
+               return -ENODEV;
+
+       can_spi_release_bus(chip);
+
+       CANMSG("Found mcp2515:%d\n",chip->chip_idx);
+       
+
+       if (mcp2515_extended_mask(chip,0x00000000, 0xffffffff))
+               return -ENODEV;
+       
+       if (!chip->baudrate) chip->baudrate=1000000;
+       if (mcp2515_baud_rate(chip,chip->baudrate,chip->clock,0,75,0))
+               return -ENODEV;
+
+       /* Enable hardware interrupts */
+       can_spi_acquire_bus(chip,1);
+       write_reg(chip, MCP2515_CANINTE, 0xff); 
+       can_spi_release_bus(chip);
+
+       mcp2515_disable_configuration(chip);
+       return 0;
+}
+
+/**
+ * mcp2515_extended_mask: - setup of extended mask for message filtering
+ * @chip: pointer to chip state structure
+ * @code: can message acceptance code
+ * @mask: can message acceptance mask
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_extended_mask(struct canchip_t *chip, unsigned long code, unsigned  long mask)
+{
+       DEBUGMSG("mcp2515_extended_mask\n");
+
+#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);
+
+       mcp2515_disable_configuration(chip);
+#endif
+       return 0;
+}
+
+/**
+ * mcp2515_baud_rate: - set communication parameters.
+ * @chip: pointer to chip state structure
+ * @rate: baud rate in Hz
+ * @clock: frequency of mcp2515 clock in Hz (ISA osc is 14318000)
+ * @sjw: synchronization jump width (0-3) prescaled clock cycles
+ * @sampl_pt: sample point in % (0-100) sets (TSEG1+1)/(TSEG1+TSEG2+2) ratio
+ * @flags: fields %BTR1_SAM, %OCMODE, %OCPOL, %OCTP, %OCTN, %CLK_OFF, %CBP
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw_in,
+                                                       int sampl_pt, int flags)
+{
+       int tqs; /* tbit/TQ */
+       int brp;
+       int ps1, ps2, propseg, sjw;
+                        int i;
+                        u8 readreg;
+
+       DEBUGMSG("mcp2515_baud_rate\n");
+                        DEBUGMSG("Clock = %d, rate = %d\n",clock,rate);
+
+       /* Determine the BRP value that gives the requested bit rate. */
+       for(brp = 0; brp < 8; brp++) {
+               tqs = clock / (2 * (brp + 1)) / rate;
+               if (tqs >= 5 && tqs <= 25
+                   && (clock / (2 * (brp + 1)) / tqs) == rate)
+                       break;
+       }
+       if (brp >= 8)
+               return -EINVAL;
+
+       /* The CAN bus bit time (tbit) is determined by:
+        *   tbit = (SyncSeg + PropSeg + PS1 + PS2) * TQ
+        * with:
+        *     SyncSeg = 1
+        *     sample point (between PS1 and PS2) must be at 60%-70% of the bit time
+        *     PropSeg + PS1 >= PS2
+        *     PropSeg + PS1 >= Tdelay
+        *     PS2 > SJW
+        *     1 <= PropSeg <= 8, 1 <= PS1 <=8, 2 <= PS2 <= 8
+        * SJW = 1 is sufficient in most cases.
+        * Tdelay is usually 1 or 2 TQ.
+        */
+
+       propseg = ps1 = ps2 = (tqs - 1) / 3;
+       if (tqs - (1 + propseg + ps1 + ps2) == 2)
+               ps1++;
+       if (tqs - (1 + propseg + ps1 + ps2) == 1)
+               ps2++;
+       sjw = 1;
+
+       DEBUGMSG("bit rate: BRP = %d, Tbit = %d TQ, PropSeg = %d, PS1 = %d, PS2 = %d, SJW = %d\n",
+               brp, tqs, propseg, ps1, ps2, sjw);
+
+       if (mcp2515_enable_configuration(chip))
+               return -ENODEV;
+
+#define CNF2_BTLMODE      0x80
+#define CNF3_PHSEG2_MASK  0x07
+
+       can_spi_acquire_bus(chip,1);
+       for (i=0;i<10;i++) {
+               write_reg(chip, MCP2515_CNF1, ((sjw-1) << 6) | brp);
+               write_reg(chip, MCP2515_CNF2, CNF2_BTLMODE | ((ps1-1) << 3) | (propseg-1));
+               bitmod_reg(chip, MCP2515_CNF3, CNF3_PHSEG2_MASK,(ps2-1));
+
+               readreg = read_reg(chip, MCP2515_CNF1);
+               if (readreg != (u8)(((sjw-1) << 6) | brp)){
+                       CANMSG("Wrong value in CNF1 - sent: 0x%X, received: 0x%X\n",((sjw-1) << 6) | brp,readreg);
+                       continue;
+               }
+               readreg = read_reg(chip, MCP2515_CNF2);
+               if (readreg != (u8)(CNF2_BTLMODE | ((ps1-1) << 3) | (propseg-1))){
+                       CANMSG("Wrong value in CNF2 - sent: 0x%X, received: 0x%X\n",CNF2_BTLMODE | ((ps1-1) << 3) | (propseg-1),readreg);
+                       continue;
+               }
+               readreg = read_reg(chip, MCP2515_CNF3);
+               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;
+               }
+               break;
+       }
+       can_spi_release_bus(chip);      
+       if (i==10){
+               CANMSG("Failed to set bit rate for %d times\n",i);
+               mcp2515_disable_configuration(chip);
+               return -1;
+       }
+
+       /* Calculate actual bit rate. */
+       DEBUGMSG("actual bit rate=%u\n",clock / (2 * (brp + 1)) / tqs);
+
+       mcp2515_disable_configuration(chip);
+
+       return 0;
+}
+
+/**
+ * mcp2515_pre_read_config: - prepares message object for message reception
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object state structure
+ *
+ * Return Value: negative value reports error.
+ *     Positive value indicates immediate reception of message.
+ * File: src/mcp2515.c
+ */
+int mcp2515_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj)
+{
+       DEBUGMSG("mcp2515_pre_read_config\n");
+
+       /* FIXME - error recovery here */
+       
+       return 0;
+}
+
+#define MAX_TRANSMIT_WAIT_LOOPS 10
+/**
+ * mcp2515_pre_write_config: - prepares message object for message transmission
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object state structure
+ * @msg: pointer to CAN message
+ *
+ * This function prepares selected message object for future initiation
+ * of message transmission by mcp2515_send_msg() function.
+ * The CAN message data and message ID are transfered from @msg slot
+ * into chip buffer in this function.
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, 
+                            struct canmsg_t *msg)
+{
+       int i=0; 
+       unsigned len;
+       uint8_t busy;
+       MCP2515_FRAME frame;
+
+       DEBUGMSG("mcp2515_pre_write_config: id=%u len=%u\n",
+              (unsigned)msg->id,
+              (unsigned)msg->length);
+
+       can_spi_acquire_bus(chip,1);
+
+       /* Wait until Transmit Buffer Status is released */
+       do {
+               busy  = read_reg(chip, MCP2515_TXB0CTRL) & mcpTXREQ;
+               if(!busy) break;
+               udelay(i++);
+       } while (i < MAX_TRANSMIT_WAIT_LOOPS);
+               
+
+       /* FIXME - error recovery here */
+
+       if (busy) {
+               CANMSG("Transmit timed out, cancelling\n");
+               bitmod_reg(chip, MCP2515_TXB0CTRL, mcpTXREQ, 0);
+               can_spi_release_bus(chip);
+               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.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.dlc = len | ((msg->flags & MSG_RTR) ? mcpRTR : 0);
+               memcpy(frame.data, msg->data, len);
+       }
+
+       write_block(chip, MCP2515_TXB0SIDH, len+5, &frame);
+
+       can_spi_release_bus(chip);
+
+       return 0;
+}
+
+/**
+ * mcp2515_send_msg: - initiate message transmission
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object state structure
+ * @msg: pointer to CAN message
+ *
+ * This function is called after mcp2515_pre_write_config() function,
+ * which prepares data in chip buffer.
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_send_msg(struct canchip_t *chip, struct msgobj_t *obj, 
+                                                       struct canmsg_t *msg)
+{
+       DEBUGMSG("mcp2515_send_msg\n");
+
+       can_spi_acquire_bus(chip,1);    
+       bitmod_reg(chip, MCP2515_TXB0CTRL, mcpTXREQ, mcpTXREQ);
+       can_spi_release_bus(chip);
+
+       return 0;
+}
+
+/**
+ * mcp2515_check_tx_stat: - checks state of transmission engine
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ *     Positive return value indicates transmission under way status.
+ *     Zero value indicates finishing of all issued transmission requests.
+ * File: src/mcp2515.c
+ */
+int mcp2515_check_tx_stat(struct canchip_t *chip)
+{
+       int status;
+       DEBUGMSG("mcp2515_check_tx_stat\n");
+
+       can_spi_acquire_bus(chip,1);
+       status = read_reg(chip,MCP2515_TXB0CTRL) & mcpTXREQ;
+       can_spi_release_bus(chip);
+
+       if (status) return 1;
+       else        return 0;
+}
+
+/**
+ * mcp2515_set_btregs: -  configures bitrate registers
+ * @chip: pointer to chip state structure
+ * @btr0: bitrate register 0
+ * @btr1: bitrate register 1
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_set_btregs(struct canchip_t *chip, unsigned short btr0, 
+                                                       unsigned short btr1)
+{
+       DEBUGMSG("mcp2515_set_btregs\n");
+#if 0
+       if (mcp2515_enable_configuration(chip))
+               return -ENODEV;
+
+       can_write_reg(chip, btr0, SJABTR0);
+       can_write_reg(chip, btr1, SJABTR1);
+
+       mcp2515_disable_configuration(chip);
+#endif
+       return 0;
+}
+
+/**
+ * mcp2515_start_chip: -  starts chip message processing
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_start_chip(struct canchip_t *chip)
+{
+       MCP2515_PRIV *priv=(MCP2515_PRIV *)(chip->chip_data);
+       DEBUGMSG("mcp2515_start_chip\n");
+
+       can_spi_acquire_bus(chip,1);
+       bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_NORM);
+       can_spi_release_bus(chip);
+
+       /* reset error counters */
+       memset(&(priv->errcnt),0,sizeof(MCP2515_ERRCNT));
+
+       return 0;
+}
+
+/**
+ * mcp2515_stop_chip: -  stops chip message processing
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_stop_chip(struct canchip_t *chip)
+{
+       DEBUGMSG("mcp2515_stop_chip\n");
+
+       can_spi_acquire_bus(chip,1);
+       bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_SLEEP);
+       can_spi_release_bus(chip);
+
+       return 0;
+}
+
+/**
+ * mcp2515_attach_to_chip: - attaches to the chip, setups registers and state
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_attach_to_chip(struct canchip_t *chip)
+{
+       DEBUGMSG("mcp2515_attach_to_chip\n");
+       /* Initialize delayed interrupt processing */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler), 
+                 workqueue_handler, 
+                 NULL);
+ #else
+       INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler), 
+                 workqueue_handler);
+ #endif
+
+       tasklet_init(&(((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler),
+                    tasklet_handler, 
+                    (unsigned long)chip);
+
+       return 0;
+}
+
+/**
+ * mcp2515_release_chip: - called before chip structure removal if %CHIP_ATTACHED is set
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_release_chip(struct canchip_t *chip)
+{
+       DEBUGMSG("mcp2515_release_chip\n");
+       if (chip==NULL)
+               panic("release: chip == NULL");
+       if (chip->chip_data==NULL)
+               panic("release: chip_data == NULL");
+       
+       chip->flags &= ~CHIP_ATTACHED;
+       DEBUGMSG("Flush workqueue.\n");
+       cancel_delayed_work(&((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
+       flush_scheduled_work();
+
+       DEBUGMSG("Kill tasklets.\n");
+       tasklet_kill(&((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler);
+#if 0
+       mcp2515_stop_chip(chip);
+       can_write_reg(chip, sjaDISABLE_INTERRUPTS, SJAIER);
+#endif
+       return 0;
+}
+
+/**
+ * mcp2515_remote_request: - configures message object and asks for RTR message
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_remote_request(struct canchip_t *chip, struct msgobj_t *obj)
+{
+       CANMSG("mcp2515_remote_request not implemented\n");
+       return -ENOSYS;
+}
+
+/**
+ * mcp2515_standard_mask: - setup of mask for message filtering
+ * @chip: pointer to chip state structure
+ * @code: can message acceptance code
+ * @mask: can message acceptance mask
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_standard_mask(struct canchip_t *chip, unsigned short code,
+               unsigned short mask)
+{
+       CANMSG("mcp2515_standard_mask not implemented\n");
+       return -ENOSYS;
+}
+
+/**
+ * mcp2515_clear_objects: - clears state of all message object residing in chip
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_clear_objects(struct canchip_t *chip)
+{
+       CANMSG("mcp2515_clear_objects not implemented\n");
+       return -ENOSYS;
+}
+
+/**
+ * mcp2515_config_irqs: - tunes chip hardware interrupt delivery
+ * @chip: pointer to chip state structure
+ * @irqs: requested chip IRQ configuration
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_config_irqs(struct canchip_t *chip, short irqs)
+{
+       CANMSG("mcp2515_config_irqs not implemented\n");
+       return -ENOSYS;
+}
+
+#define MAX_RETR 10
+
+
+/**
+ * mcp2515_irq_handler: - interrupt service routine
+ * @irq: interrupt vector number, this value is system specific
+ * @chip: pointer to chip state structure
+ * 
+ * Interrupt handler is activated when state of CAN controller chip changes,
+ * there is message to be read or there is more space for new messages or
+ * error occurs. The receive events results in reading of the message from
+ * CAN controller chip and distribution of message through attached
+ * message queues.
+ * File: src/mcp2515.c
+ */
+int mcp2515_irq_handler(int irq, struct canchip_t *chip)
+{
+       //DEBUGMSG("mcp2515_irq_handler\n");
+       if (chip == NULL)
+               return CANCHIP_IRQ_ACCEPTED;
+       if (~chip->flags & CHIP_ATTACHED)
+               return CANCHIP_IRQ_ACCEPTED;
+       
+       /* 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);
+       else /* do work in workqueue */
+               schedule_work(&((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
+
+       disable_irq(chip->chip_irq);
+       return CANCHIP_IRQ_HANDLED;
+}
+
+/**
+ * mcp2515_wakeup_tx: - wakeups TX processing
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object structure
+ *
+ * Function is responsible for initiating message transmition.
+ * It is responsible for clearing of object TX_REQUEST flag
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
+{
+       DEBUGMSG("mcp2515_wakeup_tx\n");
+
+       //can_preempt_disable();
+       
+       can_msgobj_set_fl(obj,TX_PENDING);
+       can_msgobj_set_fl(obj,TX_REQUEST);
+       
+       while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+               int rq;
+
+               can_msgobj_clear_fl(obj,TX_REQUEST);
+
+               can_spi_acquire_bus(chip,1);
+               rq = read_reg(chip, MCP2515_TXB0CTRL) & mcpTXREQ;
+               if (!rq) {
+                       obj->tx_retry_cnt=0;
+                       tx_handler(chip, obj);
+               }
+               can_spi_release_bus(chip);      
+
+               can_msgobj_clear_fl(obj,TX_LOCK);
+               if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
+               DEBUGMSG("TX looping in mcp2515_wakeup_tx\n");
+       }
+       //can_preempt_enable();
+
+       return 0;
+}
+
+int mcp2515_register(struct chipspecops_t *chipspecops)
+{
+       DEBUGMSG("mcp2515_register\n");
+       chipspecops->chip_config=mcp2515_chip_config;
+       chipspecops->baud_rate=mcp2515_baud_rate;
+       chipspecops->standard_mask=mcp2515_standard_mask;
+       chipspecops->extended_mask=mcp2515_extended_mask;
+       chipspecops->message15_mask=mcp2515_extended_mask;
+       chipspecops->clear_objects=mcp2515_clear_objects;
+       chipspecops->config_irqs=mcp2515_config_irqs;
+       chipspecops->pre_read_config=mcp2515_pre_read_config;
+       chipspecops->pre_write_config=mcp2515_pre_write_config;
+       chipspecops->send_msg=mcp2515_send_msg;
+       chipspecops->check_tx_stat=mcp2515_check_tx_stat;
+       chipspecops->wakeup_tx=mcp2515_wakeup_tx;
+       chipspecops->remote_request=mcp2515_remote_request;
+       chipspecops->enable_configuration=mcp2515_enable_configuration;
+       chipspecops->disable_configuration=mcp2515_disable_configuration;
+       chipspecops->attach_to_chip=mcp2515_attach_to_chip;
+       chipspecops->release_chip=mcp2515_release_chip;
+       chipspecops->set_btregs=mcp2515_set_btregs;
+       chipspecops->start_chip=mcp2515_start_chip;
+       chipspecops->stop_chip=mcp2515_stop_chip;
+       chipspecops->irq_handler=mcp2515_irq_handler;
+       chipspecops->irq_accept=NULL;
+       chipspecops->reset_chip=mcp2515_reset_chip;
+       chipspecops->get_info=mcp2515_get_info;
+       return 0;
+}
+
+/**
+ * mcp2515_fill_chipspecops - fills chip specific operations
+ * @chip: pointer to chip representation structure
+ *
+ * The function fills chip specific operations for mcp2515 (PeliCAN) chip.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int mcp2515_fill_chipspecops(struct canchip_t *chip)
+{
+       DEBUGMSG("mcp2515_fill_chipspecops\n");
+       chip->chip_type="mcp2515";
+       chip->max_objects=2;
+       mcp2515_register(chip->chipspecops);
+       return 0;
+}