#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 */
}
+/*****************************************************************************/
+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 */
/*****************************************************************************/
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);
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);
}
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 */
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);
// bitmod_reg(chip, MCP2515_CANINTF, flags, 0);
}
-
enable_irq(chip->chip_irq);
+#endif /* MCP2515_SPI_ASYNC */
}
/*****************************************************************************/
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;
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))
*/
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;
}
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;
}
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
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);
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;
{
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),
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);
mcp2515_stop_chip(chip);
can_write_reg(chip, sjaDISABLE_INTERRUPTS, SJAIER);
#endif
+ destroy_workqueue(mcp2515_wq);
+
return 0;
}
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;
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);
{
DEBUGMSG("mcp2515_fill_chipspecops\n");
chip->chip_type="mcp2515";
- chip->max_objects=2;
+ chip->max_objects=3;
mcp2515_register(chip->chipspecops);
return 0;
}
#include <plat/dmtimer.h>
#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
+static const char* omap2_spican_defaulttype = "mcp2515";
+
+static struct workqueue_struct *omap2_spican_wq;
+
/*******************************************************************/
/*
* We can't use the standard synchronous wrappers for file I/O; we
*/
static void omap2_spican_spi_complete(void *arg)
{
+ DEBUGMSG("SPICAN sync: complete\n");
complete(arg);
}
-static ssize_t omap2_spican_spi_sync(struct candevice_t *candev, struct spi_message *message)
+ssize_t omap2_spican_spi_sync(struct candevice_t *candev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
- struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+ struct omap2_spican_platform_data *pdata;
int status;
+
+ if (candev == NULL)
+ return -ESHUTDOWN;
+ if (message == NULL)
+ panic("SPICAN: trying to send null pointered message");
+ pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
if (pdata == NULL)
return -ESHUTDOWN;
spin_lock_irq(&pdata->spi_lock);
if (pdata->spi == NULL)
status = -ESHUTDOWN;
- else
+ else{
+ DEBUGMSG("SPICAN sync: transfer\n");
status = spi_async(pdata->spi, message);
+ }
spin_unlock_irq(&pdata->spi_lock);
if (status == 0) {
- wait_for_completion(&done);
+ while (!wait_for_completion_interruptible_timeout(&done, msecs_to_jiffies(10))) {
+ DEBUGMSG("SPICAN sync: timeout 10 msecs\n");
+ }
+// do { ;} while (wait_for_completion_interruptible(&done));
status = message->status;
if (status == 0)
status = message->actual_length;
spi_message_add_tail(&t, &m);
status = omap2_spican_spi_sync(candev, &m);
-/* struct spi_transfer *t;
- struct spi_message m;
- int i,status;
+ return status;
+}
+static void hi_async_tasklet_handler(unsigned long data)
+{
+ struct omap2_spican_platform_data *pdata=(struct omap2_spican_platform_data *)data;
+ int n;
+
if (pdata == NULL)
- return -1;
- if(len > SPI_BUF_LEN) panic("long CAN spi transfer: %u",len);
+ return;
- spi_message_init(&m);
+
+ do {
+ n = find_first_bit(pdata->hi_async_done_map,OMAP2_SPICAN_PRE_ASYNC);
+ if (n == OMAP2_SPICAN_PRE_ASYNC)
+ break;
+
+ DEBUGMSG("SPICAN async %d: high priority tasklet\n",n);
+ if (pdata->async[n].complete)
+ pdata->async[n].complete(pdata->async[n].data,pdata->async[n].count);
+
+ clear_bit(n,pdata->hi_async_done_map);
+ clear_bit(n,pdata->async_req_map);
+ } while (n < OMAP2_SPICAN_PRE_ASYNC);
+}
+
+static void lo_async_workqueue_handler(struct work_struct *work)
+{
+ struct omap2_spican_platform_data *pdata = container_of(work,struct omap2_spican_platform_data,lo_async_workqueue);
+ struct omap2_spican_async_t *async;
+ int n;
+
+ if (pdata == NULL)
+ return;
+
+ do {
+ n = find_first_bit(pdata->lo_async_done_map,OMAP2_SPICAN_PRE_ASYNC);
+ if (n >= OMAP2_SPICAN_PRE_ASYNC)
+ break;
+
+ DEBUGMSG("SPICAN async %d: low priority workqueue\n",n);
+ async = pdata->async + n;
+
+ if (async->complete)
+ async->complete(async->data,async->count);
+
+ clear_bit(n,pdata->lo_async_done_map);
+ clear_bit(n,pdata->async_req_map);
+ } while (n < OMAP2_SPICAN_PRE_ASYNC);
+}
- t = can_checked_malloc(len * sizeof(struct spi_transfer));
- for (i=0;i<len;i++){
- (*(t+i)).tx_buf = tx+i;
- (*(t+i)).rx_buf = rx+i;
- (*(t+i)).len = 1;
- (*(t+i)).cs_change = 0;
- (*(t+i)).delay_usecs = 0;
- (*(t+i)).speed_hz = pdata->speed_hz;
- (*(t+i)).bits_per_word = 8;
- spi_message_add_tail(t+i, &m);
+static void omap2_spican_spi_async_complete(void *arg)
+{
+ struct omap2_spican_async_t *as = (struct omap2_spican_async_t *)arg;
+ struct omap2_spican_platform_data *pdata = container_of(as,struct omap2_spican_platform_data,async[as->bitmap_bit]);
+ int i;
+
+// DEBUGMSG("SPICAN async: complete\n");
+ for (i=1;i<as->count;i++)
+ can_filltimestamp(&as->data[i].timestamp);
+ // Serve the fast handlers ASAP
+ if ((as->fasthandler) && (as->complete)){
+// DEBUGMSG("SPICAN async: fasthandler\n");
+/* set_bit(as->bitmap_bit,pdata->hi_async_done_map);
+ tasklet_hi_schedule(&pdata->hi_async_tasklet);*/
+ if (as->complete){
+ as->complete(as->data,as->count);
+ }
+ clear_bit(as->bitmap_bit,pdata->lo_async_done_map);
+ clear_bit(as->bitmap_bit,pdata->async_req_map);
+// DEBUGMSG("SPICAN async: fasthandler done\n");
+ return;
}
- status = omap2_spican_spi_sync(candev, &m);
- can_checked_free(t);*/
- return status;
+
+ DEBUGMSG("SPICAN async %d: slowhandler register\n",as->bitmap_bit);
+ set_bit(as->bitmap_bit,pdata->lo_async_done_map);
+ queue_work(omap2_spican_wq,&pdata->lo_async_workqueue);
}
+ssize_t omap2_spican_spi_async_transfer(struct candevice_t *candev, void (*callback)(void *data), struct can_spi_async_t *data, uint8_t count, uint8_t fasthandler)
+{
+ struct omap2_spican_platform_data *pdata;
+ struct omap2_spican_async_t *async;
+ int status,i;
+ int n;
+
+ if (candev == NULL)
+ return -ESHUTDOWN;
+
+ pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+ if (pdata == NULL)
+ return -ESHUTDOWN;
+
+ if (count > OMAP2_SPICAN_MAX_TRANSFERS)
+ return -ENOBUFS;
+
+ do {
+ n = find_first_zero_bit(pdata->async_req_map, OMAP2_SPICAN_PRE_ASYNC);
+ // There's no free transfer slot
+ if (n == OMAP2_SPICAN_PRE_ASYNC)
+ return -ENOBUFS;
+ } while (test_and_set_bit(n,pdata->async_req_map));
+
+ async = pdata->async+n;
+
+ memset(async,0,sizeof(struct omap2_spican_async_t));
+
+ for (i=0;i<count;i++){
+ if(data[i].len > SPI_MESSAGE_LENGTH)
+ panic("too long CAN spi transfer: %u",data[i].len);
+ }
+
+ memcpy(async->data,data,count*sizeof(struct can_spi_async_t));
+ async->count = count;
+ async->bitmap_bit = n;
+ async->complete = callback;
+ async->fasthandler = fasthandler;
+
+ spi_message_init(&async->m);
+
+ for (i=0;i<count;i++){
+ if ((async->data[i].tx_buf == NULL) && (async->data[i].rx_buf == NULL) && async->data[i].len != 0)
+ panic("Trying to send NULL pointered buffers");
+
+ async->t[i].tx_buf = async->data[i].tx_buf;
+ async->t[i].rx_buf = async->data[i].rx_buf;
+ async->t[i].len = async->data[i].len;
+ async->t[i].cs_change = 1;
+ async->t[i].speed_hz = pdata->speed_hz;
+ async->t[i].bits_per_word = 8;
+
+ spi_message_add_tail(&async->t[i], &async->m);
+ }
+
+ async->m.complete = omap2_spican_spi_async_complete;
+ async->m.context = async;
+ spin_lock_irq(&pdata->spi_lock);
+ if (pdata->spi == NULL)
+ status = -ESHUTDOWN;
+ else{
+ DEBUGMSG("SPICAN async %d: transfer\n",n);
+ status = spi_async(pdata->spi, &async->m);
+ }
+ spin_unlock_irq(&pdata->spi_lock);
+
+ if (status){
+ clear_bit(n,pdata->async_req_map);
+ }
-/// ---------------------- EVERYTHING'S DONE AFTER THIS POINT -----------------------------------------------
+ return status;
+}
/**
* omap2_spican_irq_handler: - GPT (general purpose timer) and waiting
int i;
if (!dev_id)
- return IRQ_HANDLED;
-
+ return IRQ_NONE;
+
pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
if (!pdata)
- return IRQ_HANDLED;
+ return IRQ_NONE;
+/* if (((pdata->trigger == OMAP2_SPICAN_TRIG_GPT) && (irq != pdata->timer_irq))
+ || ((pdata->trigger == OMAP2_SPICAN_TRIG_IRQ) && (irq != pdata->irq))
+ )
+ return IRQ_NONE;*/
+ if ((irq != pdata->timer_irq) && (irq != pdata->irq))
+ return IRQ_NONE;
+
+ DEBUGMSG("SPICAN: Got interrupt");
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+// if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ if (irq == pdata->timer_irq){
// reset the timer interrupt status
omap_dm_timer_write_status(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
omap_dm_timer_read_status(pdata->timer_ptr); // YES, you really need to do this 'wasteful' read
int omap2_spican_register_chip_data(struct canchip_t *ch,void *data){
struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)data;
- ch->clock = pdata->mcp2515_clk;
+ ch->clock = pdata->clk;
ch->baudrate = pdata->baudrate;
int ret = 0;
/*******************************************************************/
int omap2_spican_spi_acquire_bus(struct candevice_t *candev, short channel, int block){
+ /* Subsystem is always ready to accept messages */
return 1;
}
// SPI specific functions
hwspecops->spi_transfer = omap2_spican_spi_transfer;
+ hwspecops->spi_async_transfer = omap2_spican_spi_async_transfer;
hwspecops->spi_acquire_bus = omap2_spican_spi_acquire_bus;
hwspecops->spi_release_bus = omap2_spican_spi_release_bus;
static int omap2_spican_probe(struct spi_device *spi)
{
- struct omap2_spican_platform_data *pdata = spi->dev.platform_data;
+ struct lincan_spican_platform_data *spidata = spi->dev.platform_data;
+ struct omap2_spican_platform_data *pdata;
struct candevice_t *dev;
int allocated = 0;
-
- static struct omap2_spican_platform_data mypdata;
- static const char* omap2_spican_defaulttype = "mcp2515";
DEBUGMSG("Starting probe for omap2_spican...\n");
// Structure checks and initialization
- if (pdata == NULL){
-/* pdata = (struct omap2_spican_platform_data *)can_checked_malloc(sizeof(struct omap2_spican_platform_data));
- if (pdata)
- memset(pdata,0,sizeof(struct omap2_spican_platform_data));
- else
- return -ENOMEM;
- allocated = 1;*/
- pdata = &mypdata; // No need to free up
-
+ pdata = (struct omap2_spican_platform_data *)can_checked_malloc(sizeof(struct omap2_spican_platform_data));
+ if (pdata)
+ memset(pdata,0,sizeof(struct omap2_spican_platform_data));
+ else
+ return -ENOMEM;
+
+ if (spidata)
+ memcpy(pdata,spidata,sizeof(struct lincan_spican_platform_data));
+ else{
if (!pdata->cs_change) pdata->cs_change = OMAP2_SPICAN_CS_CHANGE;
if (!pdata->delay_usecs) pdata->delay_usecs = OMAP2_SPICAN_DELAY_USECS;
}
- if (!pdata->mcp2515_clk) pdata->mcp2515_clk = OMAP2_SPICAN_MCP_CLK;
+ if (!pdata->clk) pdata->clk = OMAP2_SPICAN_MCP_CLK;
if (!pdata->baudrate) pdata->baudrate = OMAP2_SPICAN_BAUDRATE;
if (!pdata->speed_hz) pdata->speed_hz = OMAP2_SPICAN_SPEED_HZ;
if (!pdata->chiptype) pdata->chiptype = omap2_spican_defaulttype;
-
spin_lock_init(&pdata->spi_lock);
pdata->spi = spi;
pdata->trigger = 0;
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ INIT_WORK(&pdata->lo_async_workqueue,
+ lo_async_workqueue_handler,
+ &pdata->lo_async_workqueue);
+ #else
+ INIT_WORK(&pdata->lo_async_workqueue,
+ lo_async_workqueue_handler);
+ #endif
+ tasklet_init(&pdata->hi_async_tasklet,
+ hi_async_tasklet_handler,
+ (unsigned long)pdata);
+
if (spi->irq) {
DEBUGMSG("Interrupt line number %d provided.\n",spi->irq);
pdata->trigger = OMAP2_SPICAN_TRIG_IRQ;
if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ) {
int status = 0;
DEBUGMSG("Requesting interrupt line %d.\n",spi->irq);
- status = set_irq_type(pdata->irq,IRQ_TYPE_EDGE_BOTH);
+ status = set_irq_type(pdata->irq,IRQ_TYPE_EDGE_FALLING);
if(status){
- CANMSG("Setting low level trigger on irq %d failed.\n", spi->irq);
+ CANMSG("Setting falling edge trigger on irq %d failed.\n", spi->irq);
goto release;
}
status = request_irq(pdata->irq, omap2_spican_irq_handler, IRQF_DISABLED , "omap2_spican", dev);
}
}
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == 0){
+// if (pdata->trigger == 0){
+ {
struct clk *gt_fclk;
uint32_t gt_rate;
int status = 0;
gt_rate = clk_get_rate(gt_fclk)/(1<<(prescaler+1));
// set preload, and autoreload
- // we set it to the clock rate in order to get 1 overflow every 10 usecs
- omap_dm_timer_set_load(pdata->timer_ptr, 1, 0xFFFFFFFF - (uint32_t)(gt_rate / 100));
+ // we set it to the clock rate in order to get 1 overflow every 1 msec
+ omap_dm_timer_set_load(pdata->timer_ptr, 1, 0xFFFFFFFF - (uint32_t)(gt_rate / 1000));
// Request for the irq
pdata->timer_irq = omap_dm_timer_get_irq(pdata->timer_ptr);
// setup timer to trigger our IRQ on the overflow event
omap_dm_timer_set_int_enable(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
- pdata->trigger = OMAP2_SPICAN_TRIG_GPT;
+// pdata->trigger = OMAP2_SPICAN_TRIG_GPT;
}
#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
spi_set_drvdata(spi, dev);
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
- // Everything's ready, let's roll!
- DEBUGMSG("Starting OMAP GPT\n");
- omap_dm_timer_start(pdata->timer_ptr);
- }
+// if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ {
+ // Everything's ready, let's roll!
+ DEBUGMSG("Starting OMAP GPT\n");
+ omap_dm_timer_start(pdata->timer_ptr);
+ }
#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
DEBUGMSG("Device omap2_spican successfully configured. Have fun!\n");
return 0;
release:
- if (allocated)
- can_checked_free(pdata);
+ can_checked_free(pdata);
return -ENODEV;
}
free_irq(pdata->irq, candev);
}
#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
- if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+// if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ {
DEBUGMSG("Stopping OMAP GPT\n");
omap_dm_timer_stop(pdata->timer_ptr);
// release the IRQ handler
DEBUGMSG("Removing omap2_spican from device structure...");
// make sure ops on existing fds can abort cleanly
+ deregister_hotplug_dev(candev);
cleanup_hotplug_dev(candev);
spin_lock_irq(&pdata->spi_lock);
// candev->sysdevptr.anydev = NULL;
spin_unlock_irq(&pdata->spi_lock);
+ DEBUGMSG("Kill tasklets.\n");
+ tasklet_kill(&pdata->hi_async_tasklet);
+
+ can_checked_free(pdata);
DEBUGMSG(" done.\n");
return 0;
};
int omap2_spican_init(void){
+ omap2_spican_wq = create_singlethread_workqueue("omap2_spican");
+ if (omap2_spican_wq == NULL)
+ return -1;
return spi_register_driver(&omap2_spican_driver);
}
void omap2_spican_exit(void){
spi_unregister_driver(&omap2_spican_driver);
+ destroy_workqueue(omap2_spican_wq);
}
#ifdef MODULE_ALIAS