--- /dev/null
+/* 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;
+}