-/* hcan2.c
- * Linux CAN-bus device driver.
- * This software is released under the GPL-License.
- */
+/**************************************************************************/
+/* File: hcan2.c - Renesas SH7760 HCAN2 controller support */
+/* */
+/* LinCAN - (Not only) Linux CAN bus driver */
+/* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz> */
+/* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz> */
+/* Copyright (C) 2007-2008 Martin Petera <peterm4@fel.cvut.cz> */
+/* Funded by OCERA and FRESCOR IST projects */
+/* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl> */
+/* */
+/* LinCAN is free software; you can redistribute it and/or modify it */
+/* under terms of the GNU General Public License as published by the */
+/* Free Software Foundation; either version 2, or (at your option) any */
+/* later version. LinCAN is distributed in the hope that it will be */
+/* useful, but WITHOUT ANY WARRANTY; without even the implied warranty */
+/* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. You should have received a */
+/* copy of the GNU General Public License along with LinCAN; see file */
+/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, */
+/* Cambridge, MA 02139, USA. */
+/* */
+/* To allow use of LinCAN in the compact embedded systems firmware */
+/* and RT-executives (RTEMS for example), main authors agree with next */
+/* special exception: */
+/* */
+/* Including LinCAN header files in a file, instantiating LinCAN generics */
+/* or templates, or linking other files with LinCAN objects to produce */
+/* an application image/executable, does not by itself cause the */
+/* resulting application image/executable to be covered by */
+/* the GNU General Public License. */
+/* This exception does not however invalidate any other reasons */
+/* why the executable file might be covered by the GNU Public License. */
+/* Publication of enhanced or derived LinCAN files is required although. */
+/**************************************************************************/
#include "../include/can.h"
#include "../include/can_sysdep.h"
void hcan2_setup_ctrl_regs(struct canmsg_t * msg, uint16_t * ctrl0, uint16_t * ctrl1, uint16_t * ctrl2);
int hcan2_compare_msg(struct msgobj_t * obj, struct canmsg_t * msg);
-
+void hcan2_notifyRXends(struct msgobj_t * obj, int what);
/* Enable folowing IRQs
* HCAN2_IRR_DFRI = Data Frame Received Interrupt Flag
* HCAN2_IRR_MBEI = Mailbox Empty Interrupt Flag
+ *
+ * and Errors:
+ * Bus Off, Error Passive, Message Overrun/Overwrite
*/
-uint16_t IRQs = ~(HCAN2_IRR_DFRI + HCAN2_IRR_MBEI);
+uint16_t IRQs = ~(HCAN2_IRR_DFRI + HCAN2_IRR_MBEI + HCAN2_IRR_BOI + HCAN2_IRR_EPI + HCAN2_IRR_MOOI);
/* 1 - mask interrupt, 0 - interrupt not masked */
int hcan2_chip_config(struct canchip_t *chip)
else
return -ENODEV;
- chip->chip_data = (void*)0; /* reset mbox number */
+ chip->chip_data = (void*)0; /* reset mbox number */
ctrl0 = ((code & 0x07ff) << 4);
lafm0 = ((mask & 0x07ff) << 4);
+ lafm0 |= 0x0003; /* ignore Ext ID 17:16 */
can_write_reg_w(chip, ctrl0, (int) obj->obj_base_addr + HCAN2_MB_CTRL0);
can_write_reg_w(chip, 0x0000, (int) obj->obj_base_addr + HCAN2_MB_CTRL1);
can_write_reg_w(chip, lafm0, (int) obj->obj_base_addr + HCAN2_MB_MASK);
can_write_reg_w(chip, 0xffff, (int) obj->obj_base_addr + HCAN2_MB_MASK + 2);
- DEBUGMSG("Set standard_mask [id:0x%04x, m:0x%04x]\n", code, mask);
+ DEBUGMSG("MB%02d: Set standard_mask [id:0x%04x, m:0x%04x]\n", obj_idx, code, lafm0);
return 0;
}
can_write_reg_w(chip, lafm0, (int) obj->obj_base_addr + HCAN2_MB_MASK);
can_write_reg_w(chip, lafm1, (int) obj->obj_base_addr + HCAN2_MB_MASK + 2);
- DEBUGMSG("Set extended_mask [id:0x%08x, m:0x%08x]\n", (uint32_t)code, (uint32_t)mask);
+ DEBUGMSG("MB%02d: Set extended_mask [id:0x%08x, m:0x%08x]\n", obj_idx, (uint32_t)code, (uint32_t)mask);
return 0;
}
int hcan2_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg)
{
unsigned obj_bit;
- int i;
int b_addr = ((obj->object - 1) / 16) * (-2);
obj_bit = (1 << ((obj->object - 1) % 16));
/* CANMSG("Sending message [obj: %d]\n", obj->object - 1); */
can_write_reg_w(chip, obj_bit, b_addr + HCAN2_TXPR0);
-
- /* Waits until chip sends message */
- i = 0;
- while (hcan2_check_tx_stat(chip) && ((i++) <= MAX_TRANSMIT_WAIT_LOOPS))
- udelay(200);
- if (i >= MAX_TRANSMIT_WAIT_LOOPS) {
- CANMSG("Failed while waiting for Transmitt finished\n");
- return -ENODEV;
- }
-
/* CANMSG("Message sent [obj: %d]\n", obj->object - 1); */
return 0;
}
uint16_t irq_reg, idx;
short loop_cnt = MAX_IRQ_WAIT_LOOPS;
uint32_t rxdf, txdf;
- struct msgobj_t * obj;
+
+/*
+ HCAN2_IRR_TCMI - Time Compare Match Register
+ HCAN2_IRR_TOI - Time Overrun Interrupt
+ HCAN2_IRR_WUBA - Wake-up on Bus Activity
+ HCAN2_IRR_MOOI - Message Overrun/Overwrite Interrupt Flag
+ HCAN2_IRR_MBEI - Messagebox Empty Interrupt Flag
+ HCAN2_IRR_OF - Overload Frame
+ HCAN2_IRR_BOI - Bus Off Interrupt Flag
+ HCAN2_IRR_EPI - Error Passive Interrupt Flag
+ HCAN2_IRR_ROWI - Receive Overload Warning Interrupt Flag
+ HCAN2_IRR_TOWI - Transmit Overload Warining Interrupt Flag
+ HCAN2_IRR_RFRI - Remote Frame Request Interrupt Flag
+ HCAN2_IRR_DFRI - Data Frame Received Interrupt Flag
+ HCAN2_IRR_RHSI - Reset/Halt/Sleep Interrupt Flag */
irq_reg = can_read_reg_w(chip, HCAN2_IRR);
DEBUGMSG("irq: %d, chip base addr: 0x%08x\n", irq, (uint32_t)chip->chip_base_addr);
-/* DEBUGMSG("IRQ Handler: HCAN2_IRR: 0x%04x\n", irq_reg); */
+ DEBUGMSG("IRQ Handler: HCAN2_IRR: 0x%04x\n", irq_reg); //*/
do {
return CANCHIP_IRQ_STUCK;
}
+ /* Received message */
if (irq_reg & HCAN2_IRR_DFRI)
- { /* Received message */
- rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
- can_read_reg_w(chip, HCAN2_RXPR0);
+ {
+ rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
+ can_read_reg_w(chip, HCAN2_RXPR0);
- DEBUGMSG("Received message [0x%08x]\n", rxdf);
+ while(rxdf) {
+ DEBUGMSG("Received message [0x%08x]\n", rxdf);
- while (rxdf) {
- /* find the message object */
- for (idx = 0; (idx < chip->max_objects) && !(rxdf & (1<<idx)); idx++) { }
+ /* find the message object */
+ for (idx = 0; (idx < chip->max_objects) && rxdf; idx++)
+ if ((rxdf & (1<<idx))) {
+ hcan2_irq_read_handler(chip, chip->msgobj[idx]);
+ /* RXPR flag for this msgobj is cleared during irq_read_handler*/
+ rxdf &= ~(1 << idx);
+ }
- /* realy i got one? */
- if (idx < chip->max_objects)
- hcan2_irq_read_handler(chip, chip->msgobj[idx]);
- else
- break;
- /* clear RXPR flag for this msgobj */
- can_write_reg_w(chip, (1 << (idx % 16)), HCAN2_RXPR0 - 2*(idx / 16));
+ DEBUGMSG("Before reset flags [0x%08x]\n", rxdf);
+ rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
+ can_read_reg_w(chip, HCAN2_RXPR0);
+ }
+ }
- rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) +
- can_read_reg_w(chip, HCAN2_RXPR0);
+ /* Error: Bus Off */
+ if (irq_reg & HCAN2_IRR_BOI) {
+ CANMSG("Error: entering BUS OFF state\nstatus register: 0x%02x irq register: 0x%02x\n",
+ can_read_reg_w(chip, HCAN2_GSR), irq_reg);
+
+ /* notify all RX/TX ends */
+ for (idx = 0; idx < chip->max_objects; idx++) {
+ /* notify TX */
+ chip->msgobj[idx]->ret=-1;
+ if(chip->msgobj[idx]->tx_slot)
+ canque_notify_inends(chip->msgobj[idx]->tx_qedge,
+ CANQUEUE_NOTIFY_ERROR);
+ /* notify RX */
+ hcan2_notifyRXends(chip->msgobj[idx], CANQUEUE_NOTIFY_ERROR);
}
-
- /* reset HCAN2_IRR_DFRI flag */
- can_write_reg_w(chip, irq_reg & ~HCAN2_IRR_DFRI, HCAN2_IRR);
+
+ /* reset flag - by writing '1' */
+ can_write_reg_w(chip, HCAN2_IRR_BOI, HCAN2_IRR);
}
- if (irq_reg & (HCAN2_IRR_MBEI))
+ /* Warning: Error Passive */
+ if (irq_reg & HCAN2_IRR_EPI) {
+ uint16_t tecrec;
+ tecrec = can_read_reg_w(chip, HCAN2_TECREC);
+
+ CANMSG("Warning: entering ERROR PASSIVE state\nTEC: %d REC: %d\n",
+ (uint16_t)((tecrec >> 8) & 0x00ff), (uint16_t)(tecrec & 0x00ff));
+
+ /* Show warning only */
+
+ /* reset flag - by writing '1' */
+ can_write_reg_w(chip, HCAN2_IRR_EPI, HCAN2_IRR);
+ }
+
+ /* Message Overrun/Overwritten */
+ if (irq_reg & HCAN2_IRR_MOOI) {
+ /* put get Unread Message Status Register */
+ rxdf = (can_read_reg_w(chip, HCAN2_UMSR1) << 16) + can_read_reg_w(chip, HCAN2_UMSR0);
+
+ /* find the message object */
+ for (idx = 0; (idx < chip->max_objects) && !(rxdf & (1<<idx)); idx++) { }
+
+ CANMSG("Error: MESSAGE OVERRUN/OVERWRITTEN [MB: %d]\n",idx);
+
+ /* notify only injured RXqueue-end */
+ if (idx < chip->max_objects)
+ hcan2_notifyRXends(chip->msgobj[idx], CANQUEUE_NOTIFY_ERROR);
+
+ /* reset flag */
+ can_write_reg_w(chip, (1 << (idx % 16)), HCAN2_UMSR0 - 2 * (idx / 16));
+ }
+
+ /* Mailbox empty - after message was sent */
+ if (irq_reg & HCAN2_IRR_MBEI)
{
- /* Mailbox empty - after message was sent */
txdf = (can_read_reg_w(chip, HCAN2_TXACK1) << 16) +
can_read_reg_w(chip, HCAN2_TXACK0);
can_write_reg_w(chip, 0xffff, HCAN2_ABACK1);
return CANCHIP_IRQ_HANDLED;
}
-
- obj = chip->msgobj[idx];
/* Clear TXACK flag */
can_write_reg_w(chip, 1 << (idx % 16), HCAN2_TXACK0 - 2 * (idx / 16));
/* sends message */
- hcan2_wakeup_tx(chip, obj);
+ hcan2_wakeup_tx(chip, chip->msgobj[idx]);
}
- irq_reg=can_read_reg_w(chip, HCAN2_IRR);
- } while(irq_reg & (HCAN2_IRR_MBEI | HCAN2_IRR_DFRI));
-
- /* reset ALL tinterrupt flags */
- can_write_reg_w(chip, irq_reg, HCAN2_IRR);
+ irq_reg = can_read_reg_w(chip, HCAN2_IRR);
+ } while(irq_reg & ~IRQs);
return CANCHIP_IRQ_HANDLED;
}
return 1;
}
+/* Note: this checks TX status of concrete messagebox */
+int hcan2_check_MB_tx_stat(struct canchip_t *chip, struct msgobj_t *obj)
+{
+ /* Transmition is complete return 0 - no error */
+
+ /* MB1-MB15 are in CANTXPR0 and MB16-MB31 are in CANTXPR1
+ CANTXPR0 = CANTXPR1 + 0x0002
+ MB0 - receive only */
+
+ char id = obj->object - 1;
+ return (can_read_reg_w(chip, HCAN2_TXPR0 - 2 * (id / 16)) & (1 << (id & 0x00ff)));
+}
+
int hcan2_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
{
DEBUGMSG("WakeUP TX\n");
can_preempt_disable();
can_msgobj_set_fl(obj,TX_REQUEST);
- while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+ if(!can_msgobj_test_and_set_fl(obj,TX_LOCK) &&
+ !hcan2_check_MB_tx_stat(chip, obj))
+ { /* enable transmition only if MB is empty */
can_msgobj_clear_fl(obj,TX_REQUEST);
hcan2_irq_write_handler(chip, obj);
can_msgobj_clear_fl(obj,TX_LOCK);
- if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
}
+ else
+ can_msgobj_clear_fl(obj,TX_REQUEST);
+
can_preempt_enable();
}
else
obj->rx_msg.id = (ctrl0 & HCAN2_MBCT0_STDID)>>4;
-
+
if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
for (i = 0; i < len; i++)
{
/* rcanqueue_ends_filt_conjuctionead 16bit data - two data bytes*/
data = can_read_reg_w(chip, (int) obj->obj_base_addr + HCAN2_MB_DATA1 + i);
- obj->rx_msg.data[i] = (data & 0xff00) >> 8;
- if (++i < len) obj->rx_msg.data[i] = data & 0x00ff;
+ obj->rx_msg.data[i] = (data & 0xff00) >> 8; // one data byte
+ if (++i < len) obj->rx_msg.data[i] = data & 0x00ff; // second data byte
}
/* Computes correct offset address of register from MSGBOX_IDX and RTR flag
* result is one of these:
* HCAN2_RXPR1, HCAN2_RXPR0, HCAN2_RFPR1, HCAN2_RFPR0
*/
- flag_addr = ((ctrl0 & HCAN2_MBCT0_RTR) << 3) + HCAN2_RXPR0 - ((obj->object - 1) / 16) * 2;
+ flag_addr = HCAN2_RXPR0 - (int)((obj->object - 1) / 16) * 2;
/* Reset flag by writing 1 to its position */
can_write_reg_w(chip, (1 << ((obj->object - 1) % 16)), flag_addr);
return ((ctrl0 ^ c0) || (ctrl2 ^ c2));
}
+
+void hcan2_notifyRXends(struct msgobj_t * obj, int what){
+ struct canque_edge_t * edge;
+ canque_for_each_inedge(obj->qends, edge){
+ canque_notify_outends(edge, what);
+ }
+}