/* i82527.c
* Linux CAN-bus device driver.
* Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
* This software is released under the GPL-License.
- * Version 0.7 6 Aug 2001
+ * Version lincan-0.2 9 Jul 2003
*/
-#define __NO_VERSION__
-#include <linux/module.h>
-
-#include <linux/autoconf.h>
-#if defined (CONFIG_MODVERSIONS) && !defined (MODVERSIONS)
-#define MODVERSIONS
-#endif
-
-#if defined (MODVERSIONS)
-#include <linux/modversions.h>
-#endif
-
-#include <linux/fs.h>
-
+#include "../include/can.h"
+#include "../include/can_sysdep.h"
#include "../include/main.h"
#include "../include/i82527.h"
+void i82527_irq_rtr_handler(struct chip_t *chip, struct msgobj_t *obj,
+ struct rtr_id *rtr_search, unsigned long message_id);
+
+
extern int stdmask;
extern int extmask;
extern int mo15mask;
+/* helper functions for segmented cards read and write configuration and status registers
+ above 0xf offset */
+
+void i82527_seg_write_reg(const struct chip_t *chip, unsigned char data, unsigned address)
+{
+ if((address > 0xf) && (chip->flags & CHIP_SEGMENTED))
+ canobj_write_reg(chip, chip->msgobj[(address>>4)-1],data, address & 0xf);
+ else
+ can_write_reg(chip, data, address);
+}
+
+unsigned i82527_seg_read_reg(const struct chip_t *chip, unsigned address)
+{
+ if((address > 0xf) && (chip->flags & CHIP_SEGMENTED))
+ return canobj_read_reg(chip, chip->msgobj[(address>>4)-1], address & 0xf);
+ else
+ return can_read_reg(chip, address);
+}
+
int i82527_enable_configuration(struct chip_t *chip)
{
unsigned short flags=0;
{
can_write_reg(chip,chip->int_cpu_reg,iCPU); // Configure cpu interface
can_write_reg(chip,(iCTL_CCE|iCTL_INI),iCTL); // Enable configuration
- can_write_reg(chip,chip->int_clk_reg,iCLK); // Set clock out slew rates
- can_write_reg(chip,chip->int_bus_reg,iBUS); /* Bus configuration */
+ i82527_seg_write_reg(chip,chip->int_clk_reg,iCLK); // Set clock out slew rates
+ i82527_seg_write_reg(chip,chip->int_bus_reg,iBUS); /* Bus configuration */
can_write_reg(chip,0x00,iSTAT); /* Clear error status register */
/* Check if we can at least read back some arbitrary data from the
* card. If we can not, the card is not properly configured!
*/
- can_write_reg(chip,0x25,MSG_OFFSET(1)+iMSGDAT1);
- can_write_reg(chip,0x52,MSG_OFFSET(2)+iMSGDAT3);
- can_write_reg(chip,0xc3,MSG_OFFSET(10)+iMSGDAT6);
- if ( (can_read_reg(chip,MSG_OFFSET(1)+iMSGDAT1) != 0x25) ||
- (can_read_reg(chip,MSG_OFFSET(2)+iMSGDAT3) != 0x52) ||
- (can_read_reg(chip,MSG_OFFSET(10)+iMSGDAT6) != 0xc3) ) {
+ canobj_write_reg(chip,chip->msgobj[1],0x25,iMSGDAT1);
+ canobj_write_reg(chip,chip->msgobj[2],0x52,iMSGDAT3);
+ canobj_write_reg(chip,chip->msgobj[10],0xc3,iMSGDAT6);
+ if ( (canobj_read_reg(chip,chip->msgobj[1],iMSGDAT1) != 0x25) ||
+ (canobj_read_reg(chip,chip->msgobj[2],iMSGDAT3) != 0x52) ||
+ (canobj_read_reg(chip,chip->msgobj[10],iMSGDAT6) != 0xc3) ) {
CANMSG("Could not read back from the hardware.\n");
CANMSG("This probably means that your hardware is not correctly configured!\n");
return -1;
else
DEBUGMSG("Could read back, hardware is probably configured correctly\n");
- if (baudrate == 0)
- baudrate=1000;
+ if (chip->baudrate == 0)
+ chip->baudrate=1000000;
- if (i82527_baud_rate(chip,baudrate*1000,chip->clock,0,75,0)) {
+ if (i82527_baud_rate(chip,chip->baudrate,chip->clock,0,75,0)) {
CANMSG("Error configuring baud rate\n");
return -ENODEV;
}
(100*(best_tseg-tseg2)/(best_tseg+1)));
- can_write_reg(chip, sjw<<6 | best_brp, iBT0);
+ i82527_seg_write_reg(chip, sjw<<6 | best_brp, iBT0);
can_write_reg(chip, ((flags & BTR1_SAM) != 0)<<7 | tseg2<<4 | tseg1,
iBT1);
DEBUGMSG("Writing 0x%x to iBT0\n",(sjw<<6 | best_brp));
int i82527_clear_objects(struct chip_t *chip)
{
int i=0,id=0,data=0;
+ struct msgobj_t *obj;
DEBUGMSG("Cleared all message objects on chip\n");
for (i=1; i<=15; i++) {
- can_write_reg(chip,(INTPD_RES|RXIE_RES|TXIE_RES|MVAL_RES) ,
- MSG_OFFSET(i)+iMSGCTL0);
- can_write_reg(chip,(NEWD_RES|MLST_RES|TXRQ_RES|RMPD_RES) ,
- MSG_OFFSET(i)+iMSGCTL1);
+ obj=chip->msgobj[i];
+ canobj_write_reg(chip,obj,(INTPD_RES|RXIE_RES|TXIE_RES|MVAL_RES),iMSGCTL0);
+ canobj_write_reg(chip,obj,(NEWD_RES|MLST_RES|TXRQ_RES|RMPD_RES), iMSGCTL1);
for (data=0x07; data<0x0f; data++)
- can_write_reg(chip,0x00,MSG_OFFSET(i)+data);
+ canobj_write_reg(chip,obj,0x00,data);
for (id=2; id<6; id++) {
- can_write_reg(chip,0x00,MSG_OFFSET(i)+id);
+ canobj_write_reg(chip,obj,0x00,id);
}
if (extended==0) {
- can_write_reg(chip,0x00,MSG_OFFSET(i)+iMSGCFG);
+ canobj_write_reg(chip,obj,0x00,iMSGCFG);
}
else {
- can_write_reg(chip,MCFG_XTD,MSG_OFFSET(i)+iMSGCFG);
+ canobj_write_reg(chip,obj,MCFG_XTD,iMSGCFG);
}
}
if (extended==0)
int i82527_pre_read_config(struct chip_t *chip, struct msgobj_t *obj)
{
if (extended) {
- can_write_reg(chip,MCFG_XTD,MSG_OFFSET(obj->object)+iMSGCFG);
+ canobj_write_reg(chip,obj,MCFG_XTD,iMSGCFG);
}
else {
- can_write_reg(chip,0x00,MSG_OFFSET(obj->object)+iMSGCFG);
+ canobj_write_reg(chip,obj,0x00,iMSGCFG);
}
- can_write_reg(chip ,(NEWD_RES|MLST_RES|TXRQ_RES|RMPD_RES),
- MSG_OFFSET(obj->object)+iMSGCTL1);
- can_write_reg(chip ,(MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES),
- MSG_OFFSET(obj->object)+iMSGCTL0);
+ canobj_write_reg(chip,obj,(NEWD_RES|MLST_RES|TXRQ_RES|RMPD_RES), iMSGCTL1);
+ canobj_write_reg(chip,obj,(MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES),iMSGCTL0);
return 0;
}
-int i82527_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
+int i82527_pre_write_config(struct chip_t *chip, struct msgobj_t *obj,
struct canmsg_t *msg)
{
int i=0,id0=0,id1=0,id2=0,id3=0;
+ int len;
+
+ len = msg->length;
+ if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
- can_write_reg(chip,(RMPD_RES|TXRQ_RES|CPUU_SET|NEWD_RES),
- MSG_OFFSET(obj->object)+iMSGCTL1);
- can_write_reg(chip,(MVAL_SET|TXIE_SET|RXIE_RES|INTPD_RES),
- MSG_OFFSET(obj->object)+iMSGCTL0);
- if (extended) {
- can_write_reg(chip,(msg->length<<4)+(MCFG_DIR|MCFG_XTD),
- MSG_OFFSET(obj->object)+iMSGCFG);
- }
- else {
- can_write_reg(chip,(msg->length<<4)+MCFG_DIR,
- MSG_OFFSET(obj->object)+iMSGCFG);
- }
- if (extended) {
+ canobj_write_reg(chip,obj,(RMPD_RES|TXRQ_RES|CPUU_SET|NEWD_RES),iMSGCTL1);
+ canobj_write_reg(chip,obj,(MVAL_SET|TXIE_SET|RXIE_RES|INTPD_RES),iMSGCTL0);
+
+ if (extended || (msg->flags&MSG_EXT)) {
+ canobj_write_reg(chip,obj,(len<<4)|(MCFG_DIR|MCFG_XTD),iMSGCFG);
id0 = (unsigned char) (msg->id<<3);
id1 = (unsigned char) (msg->id>>5);
id2 = (unsigned char) (msg->id>>13);
id3 = (unsigned char) (msg->id>>21);
- can_write_reg(chip,id0,MSG_OFFSET(obj->object)+iMSGID3);
- can_write_reg(chip,id1,MSG_OFFSET(obj->object)+iMSGID2);
- can_write_reg(chip,id2,MSG_OFFSET(obj->object)+iMSGID1);
- can_write_reg(chip,id3,MSG_OFFSET(obj->object)+iMSGID0);
+ canobj_write_reg(chip,obj,id0,iMSGID3);
+ canobj_write_reg(chip,obj,id1,iMSGID2);
+ canobj_write_reg(chip,obj,id2,iMSGID1);
+ canobj_write_reg(chip,obj,id3,iMSGID0);
}
else {
+ canobj_write_reg(chip,obj,(len<<4)|MCFG_DIR,iMSGCFG);
id1 = (unsigned char) (msg->id<<5);
id0 = (unsigned char) (msg->id>>3);
- can_write_reg(chip,id1,MSG_OFFSET(obj->object)+iMSGID1);
- can_write_reg(chip,id0,MSG_OFFSET(obj->object)+iMSGID0);
+ canobj_write_reg(chip,obj,id1,iMSGID1);
+ canobj_write_reg(chip,obj,id0,iMSGID0);
}
- can_write_reg(chip,0xfa,MSG_OFFSET(obj->object)+iMSGCTL1);
- for (i=0; i<msg->length; i++) {
- can_write_reg(chip,msg->data[i],MSG_OFFSET(obj->object)+
- iMSGDAT0+i);
+ canobj_write_reg(chip,obj,0xfa,iMSGCTL1);
+ for (i=0; i<len; i++) {
+ canobj_write_reg(chip,obj,msg->data[i],iMSGDAT0+i);
}
return 0;
struct canmsg_t *msg)
{
if (msg->flags & MSG_RTR) {
- can_write_reg(chip,(RMPD_RES|TXRQ_RES|CPUU_RES|NEWD_SET),
- MSG_OFFSET(obj->object)+iMSGCTL1);
+ canobj_write_reg(chip,obj,(RMPD_RES|TXRQ_RES|CPUU_RES|NEWD_SET),iMSGCTL1);
}
else {
- can_write_reg(chip,(RMPD_RES|TXRQ_SET|CPUU_RES|NEWD_SET),
- MSG_OFFSET(obj->object)+iMSGCTL1);
+ canobj_write_reg(chip,obj,(RMPD_RES|TXRQ_SET|CPUU_RES|NEWD_SET),iMSGCTL1);
}
return 0;
int i82527_remote_request(struct chip_t *chip, struct msgobj_t *obj)
{
- can_write_reg(chip, (MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES),
- MSG_OFFSET(obj->object)+iMSGCTL0);
- can_write_reg(chip, (RMPD_RES|TXRQ_SET|MLST_RES|NEWD_RES),
- MSG_OFFSET(obj->object)+iMSGCTL1);
+ canobj_write_reg(chip,obj,(MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES),iMSGCTL0);
+ canobj_write_reg(chip,obj,(RMPD_RES|TXRQ_SET|MLST_RES|NEWD_RES),iMSGCTL1);
return 0;
}
if (i82527_enable_configuration(chip))
return -ENODEV;
- can_write_reg(chip, btr0, iBT0);
- can_write_reg(chip, btr1, iBT1);
+ i82527_seg_write_reg(chip, btr0, iBT0);
+ i82527_seg_write_reg(chip, btr1, iBT1);
i82527_disable_configuration(chip);
return 0;
}
+inline void i82527_irq_write_handler(struct chip_t *chip, struct msgobj_t *obj)
+{
+ int cmd;
+
+ canobj_write_reg(chip,obj,(MVAL_RES|TXIE_RES|RXIE_RES|INTPD_RES),+iMSGCTL0);
+
+ if(obj->tx_slot){
+ /* Do local transmitted message distribution if enabled */
+ if (processlocal){
+ 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;
+ }
+
+ cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
+ if(cmd<0)
+ return;
+
+ 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;
+ }
+}
+
+inline void i82527_irq_read_handler(struct chip_t *chip, struct msgobj_t *obj,
+ unsigned long message_id)
+{
+ int i=0, tmp=1, len;
+
+ while (tmp) {
+ canobj_write_reg(chip,obj,(RMPD_RES|TXRQ_RES|MLST_RES|NEWD_RES),iMSGCTL1);
+ canobj_write_reg(chip,obj,(MVAL_SET|TXIE_RES|RXIE_SET|INTPD_RES),iMSGCTL0);
+
+ len = (canobj_read_reg(chip,obj,iMSGCFG) >> 4) & 0xf;
+ obj->rx_msg.length = len;
+ obj->rx_msg.id = message_id;
+
+ if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
+ for (i=0; i < obj->rx_msg.length; i++)
+ obj->rx_msg.data[i] = canobj_read_reg(chip,obj,iMSGDAT0+i);
+
+ canque_filter_msg2edges(obj->qends, &obj->rx_msg);
+
+ if (!((tmp=canobj_read_reg(chip,obj,iMSGCTL1)) & NEWD_SET)) {
+ break;
+ }
+
+ if (tmp & MLST_SET)
+ CANMSG("Message lost!\n");
+
+ }
+}
+
+irqreturn_t i82527_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int id0=0, id1=0, id2=0, id3=0;
+
+ unsigned irq_register;
+ unsigned object;
+ struct chip_t *chip=(struct chip_t *)dev_id;
+ struct msgobj_t *obj;
+ unsigned long message_id;
+ struct rtr_id *rtr_search;
+
+ /*put_reg=device->hwspecops->write_register;*/
+ /*get_reg=device->hwspecops->read_register;*/
+
+ irq_register = i82527_seg_read_reg(chip, iIRQ);
+
+ while (irq_register) {
+
+ if (irq_register == 0x01) {
+ DEBUGMSG("Status register: 0x%x\n",can_read_reg(chip, iSTAT));
+ return IRQ_NONE;
+ }
+
+ if (irq_register == 0x02)
+ object = 14;
+ else
+ object = irq_register-3;
+
+ obj=chip->msgobj[object];
+
+ if (canobj_read_reg(chip,obj,iMSGCFG) & MCFG_DIR) {
+ set_bit(OBJ_TX_REQUEST,&obj->flags);
+ while(!test_and_set_bit(OBJ_TX_LOCK,&obj->flags)){
+ clear_bit(OBJ_TX_REQUEST,&obj->flags);
+
+ if(canobj_read_reg(chip,obj,iMSGCTL1)&TXRQ_RES)
+ i82527_irq_write_handler(chip, obj);
+
+ clear_bit(OBJ_TX_LOCK,&obj->flags);
+ if(!test_bit(OBJ_TX_REQUEST,&obj->flags)) break;
+ }
+ }
+ else {
+
+ if (extended) {
+ id0=canobj_read_reg(chip,obj,iMSGID3);
+ id1=canobj_read_reg(chip,obj,iMSGID2)<<8;
+ id2=canobj_read_reg(chip,obj,iMSGID1)<<16;
+ id3=canobj_read_reg(chip,obj,iMSGID0)<<24;
+ message_id=(id0|id1|id2|id3)>>3;
+ }
+ else {
+ id0=canobj_read_reg(chip,obj,iMSGID1);
+ id1=canobj_read_reg(chip,obj,iMSGID0)<<8;
+ message_id=(id0|id1)>>5;
+ }
+
+ spin_lock(&hardware_p->rtr_lock);
+ rtr_search=hardware_p->rtr_queue;
+ while (rtr_search != NULL) {
+ if (rtr_search->id == message_id)
+ break;
+ rtr_search=rtr_search->next;
+ }
+ spin_unlock(&hardware_p->rtr_lock);
+ if ((rtr_search!=NULL) && (rtr_search->id==message_id))
+ i82527_irq_rtr_handler(chip, obj, rtr_search, message_id);
+ else
+ i82527_irq_read_handler(chip, obj, message_id);
+ }
+
+ irq_register=i82527_seg_read_reg(chip, iIRQ);
+ }
+ return IRQ_HANDLED;
+}
+
+void i82527_irq_rtr_handler(struct chip_t *chip, struct msgobj_t *obj,
+ struct rtr_id *rtr_search, unsigned long message_id)
+{
+ short int i=0;
+
+ canobj_write_reg(chip,obj,(MVAL_RES|TXIE_RES|RXIE_RES|INTPD_RES),iMSGCTL0);
+ canobj_write_reg(chip,obj,(RMPD_RES|TXRQ_RES|MLST_RES|NEWD_RES),iMSGCTL1);
+
+ spin_lock(&hardware_p->rtr_lock);
+
+ rtr_search->rtr_message->id=message_id;
+ rtr_search->rtr_message->length=(canobj_read_reg(chip,obj,iMSGCFG) & 0xf0)>>4;
+ for (i=0; i<rtr_search->rtr_message->length; i++)
+ rtr_search->rtr_message->data[i]=canobj_read_reg(chip,obj,iMSGDAT0+i);
+
+ spin_unlock(&hardware_p->rtr_lock);
+
+ if (waitqueue_active(&rtr_search->rtr_wq))
+ wake_up(&rtr_search->rtr_wq);
+}
+
+int i82527_wakeup_tx(struct chip_t *chip, struct msgobj_t *obj)
+{
+ /* dummy lock to prevent preemption fully portable way */
+ spinlock_t dummy_lock;
+
+ /* preempt_disable() */
+ spin_lock_init(&dummy_lock);
+ spin_lock(&dummy_lock);
+
+ set_bit(OBJ_TX_REQUEST,&obj->flags);
+ while(!test_and_set_bit(OBJ_TX_LOCK,&obj->flags)){
+ clear_bit(OBJ_TX_REQUEST,&obj->flags);
+
+ if(canobj_read_reg(chip,obj,iMSGCTL1)&TXRQ_RES)
+ i82527_irq_write_handler(chip, obj);
+
+ clear_bit(OBJ_TX_LOCK,&obj->flags);
+ if(!test_bit(OBJ_TX_REQUEST,&obj->flags)) break;
+ }
+
+ /* preempt_enable(); */
+ spin_unlock(&dummy_lock);
+ return 0;
+}
+
int i82527_register(struct chipspecops_t *chipspecops)
{
chipspecops->chip_config = i82527_chip_config;
chipspecops->pre_write_config = i82527_pre_write_config;
chipspecops->send_msg = i82527_send_msg;
chipspecops->check_tx_stat = i82527_check_tx_stat;
+ chipspecops->wakeup_tx = i82527_wakeup_tx;
chipspecops->remote_request = i82527_remote_request;
chipspecops->enable_configuration = i82527_enable_configuration;
chipspecops->disable_configuration = i82527_disable_configuration;
chipspecops->set_btregs = i82527_set_btregs;
chipspecops->start_chip = i82527_start_chip;
chipspecops->stop_chip = i82527_stop_chip;
+ chipspecops->irq_handler = i82527_irq_handler;
return 0;
}