X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/blobdiff_plain/0fec21d9ba47b6b54a588dc57b24466741b59ccd..9c8ab08d7e8fca3916a7f91a3c001d151989137c:/lincan/src/hcan2.c diff --git a/lincan/src/hcan2.c b/lincan/src/hcan2.c index 96b1043..23d15ab 100644 --- a/lincan/src/hcan2.c +++ b/lincan/src/hcan2.c @@ -1,7 +1,37 @@ -/* 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 */ +/* Copyright (C) 2002-2009 Pavel Pisa */ +/* Copyright (C) 2007-2008 Martin Petera */ +/* Funded by OCERA and FRESCOR IST projects */ +/* Based on CAN driver code by Arnaud Westenberg */ +/* */ +/* 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" @@ -32,12 +62,15 @@ void hcan2_setup_mbox4read(struct msgobj_t * obj); 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) @@ -308,18 +341,19 @@ int hcan2_standard_mask(struct canchip_t *chip, unsigned short code, unsigned sh 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; } @@ -352,7 +386,7 @@ int hcan2_extended_mask(struct canchip_t *chip, unsigned long code, unsigned lon 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; } @@ -397,7 +431,6 @@ int hcan2_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, struct 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)); @@ -405,17 +438,7 @@ int hcan2_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t /* 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; } @@ -432,11 +455,25 @@ int hcan2_irq_handler(int irq, struct canchip_t *chip) 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 { @@ -445,37 +482,85 @@ int hcan2_irq_handler(int irq, struct canchip_t *chip) 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<max_objects) && rxdf; idx++) + if ((rxdf & (1<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<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); @@ -489,21 +574,16 @@ int hcan2_irq_handler(int irq, struct canchip_t *chip) 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; } @@ -549,6 +629,19 @@ int hcan2_check_tx_stat(struct canchip_t *chip) 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"); @@ -559,14 +652,18 @@ int hcan2_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj) 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(); @@ -628,22 +725,22 @@ void hcan2_irq_read_handler(struct canchip_t *chip, struct msgobj_t *obj) } 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); @@ -989,3 +1086,10 @@ int hcan2_compare_msg(struct msgobj_t * obj, struct canmsg_t * msg) 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); + } +}