From: ppisa Date: Tue, 9 Feb 2010 22:44:13 +0000 (+0100) Subject: Merge branch 'master' into can-usb1 X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/commitdiff_plain/a1a6972ab91d9315cc9a8ce2d728573d140d3628?hp=12721485d94ade1e34c99c3f7e8e9a71c3c7467e Merge branch 'master' into can-usb1 --- diff --git a/lincan/include/mpc5200.h b/lincan/include/mpc5200.h new file mode 100644 index 0000000..e709699 --- /dev/null +++ b/lincan/include/mpc5200.h @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* File: mpc5200.h - Freescale MPC5200 MSCAN 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. */ +/**************************************************************************/ + +#ifndef LINCAN_MPC5200_H +#define LINCAN_MPC5200_H + + +/* MPC5200 has two CAN controlers + * however MIDAM board uses only one controler + * RYU board uses both + */ +#define NR_82527 0 +#define NR_SJA1000 0 +#define NR_ALL (NR_82527 + NR_SJA1000 + 2) +#define NR_ALL_MIDAM (NR_ALL - 1) + + + +/* IRQ is read from OpenFirmware nodes */ + +#define MPC5200_CAN_CHIP_OFFSET 0x80 +#define IO_RANGE 0x80 + + +/* Clock frequency - used for baudrate */ +#define MPC5200_SHARK_SYS_XTAL_FREQ 396000000 /* 396 MHz */ +#define MPC5200_SHARK_IPB_FREQ 132000000 /* derived from XTAL by dividing: 132 MHz */ +/* got from /proc/device-tree/cpus/PowerPC,5200@0/ bus-frequency and system-frequency */ + +/* Determine which clock source to use */ +/* 0 - use IP Bus clock */ +/* 1 - use SYS_XTAL_IN frequency */ +#define MPC5200_CLKSRC 1 + +#if MPC5200_CLKSRC + #define MPC5200_CLK_FREQ (MPC5200_SHARK_SYS_XTAL_FREQ/12) /* 33MHz */ +#else + #define MPC5200_CLK_FREQ (MPC5200_SHARK_IPB_FREQ/4) /* 33MHz */ +#endif + +/* ----------- Debugging options for MPC5200 ----------- */ + +#define MPC5200_DBG 0 + +#if MPC5200_DBG + /* standard LinCAN core debug - used only for MPC5200 driver part */ + #define DEBUGMSG(fmt,args...) can_printk(KERN_ERR "lincan (debug): " fmt,##args) +#endif /* MPC5200_DBG */ + + +int mpc5200_request_io(struct candevice_t *candev); +int mpc5200_release_io(struct candevice_t *candev); +int mpc5200_reset(struct candevice_t *candev); +int mpc5200_init_hw_data(struct candevice_t *candev); +int mpc5200_init_chip_data(struct candevice_t *candev, int chipnr); +int mpc5200_init_obj_data(struct canchip_t *chip, int objnr); +int mpc5200_program_irq(struct candevice_t *candev); +void mpc5200_write_register(unsigned data, can_ioptr_t address); +unsigned mpc5200_read_register(can_ioptr_t address); + +#endif /* LINCAN_MPC5200_H */ diff --git a/lincan/include/mscan.h b/lincan/include/mscan.h new file mode 100644 index 0000000..a85a291 --- /dev/null +++ b/lincan/include/mscan.h @@ -0,0 +1,365 @@ +/**************************************************************************/ +/* File: mscan.h - Freescale MPC5200 MSCAN 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. */ +/**************************************************************************/ + +#ifndef LINCAN_MSCAN_H +#define LINCAN_MSCAN_H + + + +#define MPC5200_DBG 0 + +/* Debug - coarse approach */ +#if MPC5200_DBG + /* standard LinCAN core debug - used only for MPC5200 driver part */ + #define DEBUGMSG(fmt,args...) can_printk(KERN_ERR "lincan (debug): " fmt,##args) + + /* dump specific parts of chip memory */ + #define DUMPREGS(canchip) dump_regs(canchip) + #define DUMPBUFF(canchip, offset) dump_buff(canchip, offset) + #define DUMPFLT(canchip) dump_filter(canchip) + + /* Debug Tx Rx operations */ + #define DEBUGTX(fmt,args...) can_printk(KERN_ERR "lincan (debug): " fmt,##args) + #define DEBUGRX(fmt,args...) can_printk(KERN_ERR "lincan (debug): " fmt,##args) +#else + #define DUMPREGS(canchip) + #define DUMPBUFF(canchip, offset) + #define DUMPFLT(canchip) + #define DEBUGTX(fmt,args...) + #define DEBUGRX(fmt,args...) +#endif /* MPC5200_DBG */ + + + + +/* MSCAN register size */ +#define reg_t uint8_t + + +/* Determine which clock source to use */ +/* 0 - use IP Bus clock */ +/* 1 - use SYS_XTAL_IN frequency */ +#define MPC5200_CLKSRC 1 + +#if MPC5200_CLKSRC + #define MPC5200_CLK_FREQ (MPC5200_SHARK_SYS_XTAL_FREQ/12) /* 33MHz */ +#else + #define MPC5200_CLK_FREQ (MPC5200_SHARK_IPB_FREQ/4) /* 33MHz */ +#endif + + +int mscan_chip_config(struct canchip_t *chip); +int mscan_enable_configuration(struct canchip_t *chip); +int mscan_disable_configuration(struct canchip_t *chip); + +int mscan_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw, int sampl_pt, int flags); +int mscan_set_btregs(struct canchip_t *chip, unsigned short bcr0, unsigned short bcr1); + +int mscan_start_chip(struct canchip_t *chip); +int mscan_stop_chip(struct canchip_t *chip); +int mscan_attach_to_chip(struct canchip_t *chip); +int mscan_release_chip(struct canchip_t *chip); + +int mscan_standard_mask(struct canchip_t *chip, unsigned short code, unsigned short mask); +int mscan_extended_mask(struct canchip_t *chip, unsigned long code, unsigned long mask); +/* int mscan_message15_mask(int irq, struct canchip_t *chip); */ + +int mscan_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj); +int mscan_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg); +int mscan_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg); +int mscan_remote_request(struct canchip_t *chip, struct msgobj_t *obj); + +int mscan_irq_handler(int irq, struct canchip_t *chip); +/* int mscan_irq_accept(int irq, struct canchip_t *chip); */ +int mscan_config_irqs(struct canchip_t *chip, short irqs); + +int mscan_clear_objects(struct canchip_t *chip); +int mscan_check_tx_stat(struct canchip_t *chip); +int mscan_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj); +int mscan_filtch_rq(struct canchip_t *chip, struct msgobj_t * obj); + +int mscan_register(struct chipspecops_t *chipspecops); +int mscan_fill_chipspecops(struct canchip_t *chip); + +int mscan_reset_chip(struct canchip_t * chip); + +extern inline void can_write_reg_w(const struct canchip_t *pchip, uint16_t data, unsigned reg) +{ + can_ioptr_t address = pchip->chip_base_addr + reg; + #ifndef CONFIG_OC_LINCAN_DYNAMICIO + writew(data,address); + #else /*CONFIG_OC_LINCAN_DYNAMICIO*/ + pchip->write_register(data, address); + #endif /*CONFIG_OC_LINCAN_DYNAMICIO*/ +} + +extern inline uint16_t can_read_reg_w(const struct canchip_t *pchip, unsigned reg) +{ + can_ioptr_t address = pchip->chip_base_addr + reg; + #ifndef CONFIG_OC_LINCAN_DYNAMICIO + return readw(address); + #else /*CONFIG_OC_LINCAN_DYNAMICIO*/ + return pchip->read_register(address); + #endif /*CONFIG_OC_LINCAN_DYNAMICIO*/ +} + + +/* BasicCAN mode address map */ +#define MSCAN_CTL0 0x00 /* Control Register 0 */ +#define MSCAN_CTL1 0x01 /* Control Register 1 */ +#define MSCAN_BTR0 0x04 /* Bus Timing Register 0 */ +#define MSCAN_BTR1 0x05 /* Bus Timing Register 1 */ +#define MSCAN_RFLG 0x08 /* Receive Flag Register */ +#define MSCAN_RIER 0x09 /* Receiver Interrupt Enable Register */ +#define MSCAN_TFLG 0x0c /* Transmitter Flag Register */ +#define MSCAN_TIER 0x0d /* Transmitter Interrupt Enable Register */ +#define MSCAN_TARQ 0x10 /* Transmitter Message Abort Request Register */ +#define MSCAN_TAAK 0x11 /* Transmitter Message Abort Acknowledge Register */ +#define MSCAN_TBSEL 0x14 /* Transmitter Buffer Selection */ +#define MSCAN_IDAC 0x15 /* Identifier Acceptance Control Register */ +/* Reserved 0x18, 0x19 */ +#define MSCAN_RXERR 0x1c /* Receive Error Counter Register */ +#define MSCAN_TXERR 0x1d /* Transmitt Error Counter Register */ + +/* Acceptance Filters */ +#define MSCAN_IDAR0 0x20 /* Identifier Acceptance Register 0 */ +#define MSCAN_IDAR1 0x21 /* Identifier Acceptance Register 1 */ +#define MSCAN_IDAR2 0x24 /* Identifier Acceptance Register 2 */ +#define MSCAN_IDAR3 0x25 /* Identifier Acceptance Register 3 */ +#define MSCAN_IDMR0 0x28 /* Identifier Mask Register 0 */ +#define MSCAN_IDMR1 0x29 /* Identifier Mask Register 1 */ +#define MSCAN_IDMR2 0x2c /* Identifier Mask Register 2 */ +#define MSCAN_IDMR3 0x2d /* Identifier Mask Register 3 */ + +#define MSCAN_IDAR4 0x30 /* Identifier Acceptance Register 4 */ +#define MSCAN_IDAR5 0x31 /* Identifier Acceptance Register 5 */ +#define MSCAN_IDAR6 0x34 /* Identifier Acceptance Register 6 */ +#define MSCAN_IDAR7 0x35 /* Identifier Acceptance Register 7 */ +#define MSCAN_IDMR4 0x38 /* Identifier Mask Register 4 */ +#define MSCAN_IDMR5 0x39 /* Identifier Mask Register 5 */ +#define MSCAN_IDMR6 0x3c /* Identifier Mask Register 6 */ +#define MSCAN_IDMR7 0x3d /* Identifier Mask Register 7 */ + +/* Buffers */ +#define MSCAN_RXFG 0x40 /* Foregroung Receive Buffer 0x40-0x5f */ +#define MSCAN_TXFG 0x60 /* Foregroung Receive Buffer 0x60-0x7f */ + + +/* BaudRate minimal and maximal TSEG values */ +#define MSCAN_TSEG1_MIN 4 /* means 1 Tq clock cycle */ +#define MSCAN_TSEG1_MAX 16 /* means 16 Tq clock cycles */ +#define MSCAN_TSEG2_MIN 2 /* means 1 Tq clock cycle */ +#define MSCAN_TSEG2_MAX 8 /* means 8 Tq clock cycles */ +#define MSCAN_TSEG_MIN (MSCAN_TSEG1_MIN + MSCAN_TSEG2_MIN) +#define MSCAN_TSEG_MAX (MSCAN_TSEG1_MAX + MSCAN_TSEG2_MAX) + +#define MSCAN_BRP_MIN 1 +#define MSCAN_BRP_MAX 64 + +enum mscan_ctl0 { + MSCAN_CTL0_RXFRM = 1 << 7, /* Received Frame Flag - rw */ + MSCAN_CTL0_RXACT = 1 << 6, /* Receiver Active Status - ro */ + MSCAN_CTL0_CSWAI = 1 << 5, /* CAN Stops in Wait Mode - rw */ + MSCAN_CTL0_SYNCH = 1 << 4, /* Synchronized Status - ro */ + MSCAN_CTL0_TIME = 1 << 3, /* Timer Enable - rw */ + MSCAN_CTL0_WUPE = 1 << 2, /* WakeUp Enable - rw */ + MSCAN_CTL0_SLPRQ = 1 << 1, /* Sleep Request - rw */ + MSCAN_CTL0_INITRQ = 1 << 0, /* Initialization Mode Request - rw */ +}; + +enum mscan_ctl1 { + MSCAN_CTL1_CANE = 1 << 7, /* MSCAN Enable rw */ + MSCAN_CTL1_CLKSRC = 1 << 6, /* MSCAN Clock Source rw */ + MSCAN_CTL1_LOOPB = 1 << 5, /* Loob-Back Self Test Mode rw */ + MSCAN_CTL1_LISTEN = 1 << 4, /* Listen-Only Mode rw */ + MSCAN_CTL1_WUPM = 1 << 2, /* WakeUp Mode rw */ + MSCAN_CTL1_SLPAK = 1 << 1, /* Sleep Mode Acknowledge - ro */ + MSCAN_CTL1_INITAK = 1 << 0, /* Initialization Mode Acknowledge - ro */ + + MSCAN_CTL1_RSVD = 1 << 3, /* Reserved */ +}; + +enum mscan_btr0 { + MSCAN_BTR0_SJW = 0xa0, /* Synchronization Jump Width: 0 to 3 (1 to 4 Tq) */ + MSCAN_BTR0_BRP = 0x3f, /* Baud Rate Prescaler: 0 to 63 (1 to 64) */ +}; + +enum mscan_btr1 { + MSCAN_BTR1_SAMP = 1 << 7, /* Sampling: 0-One sample, 1-Three samples per bit */ + MSCAN_BTR1_TSEG2 = 0x70, /* TSEG2: 3 bits */ + MSCAN_BTR1_TSEG1 = 0x0f, /* TSEG1: 4 bits */ +}; + +/* Interupt & Interrupt Enable Registers */ +enum mscan_rflg { + MSCAN_RFLG_WUPIF = 1 << 7, /* WakeUp Interrupt Flag - rw */ + MSCAN_RFLG_CSCIF = 1 << 6, /* CAN Status Change Interrupt Flag - rw */ + MSCAN_RFLG_RSTAT = 0x30, /* Receiver Status Bits: 0-RxOK, 1-RxWRN, 2-RxERR, 3-BusOff - ro */ + MSCAN_RFLG_TSTAT = 0x0c, /* Transmitter Status Bits: 0-TxOK, 1-TxWRN, 2-TxErr, 3-BusOff - ro */ + MSCAN_RFLG_OVRIF = 1 << 1, /* Overrun Interrupt Flag - rw */ + MSCAN_RFLG_RXF = 1 << 0, /* Receive Buffer Full - rw */ +}; + +enum mscan_rier { + MSCAN_RIER_WUPIE = 1 << 7, /* WakeUp Interrupt Enable - rw */ + MSCAN_RIER_CSCIE = 1 << 6, /* CAN Status Change Interrupt Enable - rw */ + MSCAN_RIER_RSTATE = 0x30, /* Receiver Status Change Enable: 0-Never, 1-BusOff, 2-BusOff+RxErr, 3-All - rw */ + MSCAN_RIER_TSTATE = 0x0c, /* Transmitter Status Change Enable: 0-Never, 1-BusOff, 2-BusOff+TxErr, 3-All - rw */ + MSCAN_RIER_OVRIE = 1 << 1, /* Overrun Interrupt Enable - rw */ + MSCAN_RIER_RXFIE = 1 << 0, /* Receive Buffer Full Interrupt Enable - rw */ +}; + +enum mscan_tflg { + MSCAN_TFLG_TXE = 0x07, /* Transmitter Buffer Empty */ + + MSCAN_TFLG_RSVD = 0xf8, /* Reserved */ +}; + +enum mscan_tier { + MSCAN_TIER_TXE = 0x07, /* Transmitter Empty Interrupt Enable */ + + MSCAN_TIER_RSVD = 0xf8, /* Reserved */ +}; +/* IRQ end */ + +enum mscan_tarq { + MSCAN_TARQ_ABTRQ = 0x07, /* Abort Request */ + + MSCAN_TARQ_RSVD = 0xf8, /* Reserved */ +}; + +enum mscan_taak { + MSCAN_TAAK_ABTAK = 0x07, /* Abort Acknowledge */ + + MSCAN_TAAK_RSVD = 0xf8, /* Reserved */ +}; + +enum mscan_tbsel { + MSCAN_TBSEL_TX = 0x07, /* Transmitt Buffer Select - prioritize lower */ + + MSCAN_TBSEL_RSVD = 0xf8, /* Reserved */ +}; + +enum mscan_idac { + MSCAN_IDAC_IDAM = 0x30, /* Identifier Acceptance Mode: 0-Two 32bit, 1-Four 16-bit. 2-Eight 8bit, 3-Closed */ + MSCAN_IDAC_IDHIT = 0x07, /* Identifier Acceptance Hit Indicator */ + + MSCAN_IDAC_RSVD = 0xa8, /* Reserved */ +}; + +/* Message Buffer Organization */ +#define MSCAN_MSGBUFF_ID0 0x00 /* Identifier Register 0 */ +#define MSCAN_MSGBUFF_ID1 0x01 /* Identifier Register 1 */ +#define MSCAN_MSGBUFF_ID2 0x04 /* Identifier Register 2 */ +#define MSCAN_MSGBUFF_ID3 0x05 /* Identifier Register 3 */ + +#define MSCAN_MSGBUFF_DATA0 0x08 /* Data Segment Register 0 */ +#define MSCAN_MSGBUFF_DATA1 0x09 /* Data Segment Register 1 */ +#define MSCAN_MSGBUFF_DATA2 0x0c /* Data Segment Register 2 */ +#define MSCAN_MSGBUFF_DATA3 0x0d /* Data Segment Register 3 */ +#define MSCAN_MSGBUFF_DATA4 0x10 /* Data Segment Register 4 */ +#define MSCAN_MSGBUFF_DATA5 0x11 /* Data Segment Register 5 */ +#define MSCAN_MSGBUFF_DATA6 0x14 /* Data Segment Register 6 */ +#define MSCAN_MSGBUFF_DATA7 0x15 /* Data Segment Register 7 */ + +#define MSCAN_MSGBUFF_DLEN 0x18 /* Data Length Register */ +#define MSCAN_MSGBUFF_TXPRIO 0x19 /* Transmitt Buffer Priority - Not Applicable for Rx buffers */ +#define MSCAN_MSGBUFF_TSH 0x1c /* Time Stamp Register High */ +#define MSCAN_MSGBUFF_TSL 0x1d /* Time Stamp Register Low */ + +struct mscan_msg_buffer{ + + uint8_t id_0; /* CAN identifier: ID_EXT {28:21} OR ID_STD {10:3} */ + uint8_t id_1; /* CAN identifier: ID_EXT {20:18},SRR,IDE,ID_EXT{17:15} OR ID_STD{2:0},RTR,IDE, reserved */ + uint16_t spacer_0; /* spacer for compatibility */ + uint8_t id_2; /* CAN identifier: IDE_EXT{14:7} OR reserved for ID_STD */ + uint8_t id_3; /* CAN identifier: ID_EXT{6:0},RTR OR reserved for ID_STD */ + uint16_t spacer_1; /* spacer for compatibility */ + uint8_t data_0; /* CAN data byte 0 */ + uint8_t data_1; /* CAN data byte 1 */ + uint16_t spacer_2; /* spacer for compatibility */ + uint8_t data_2; /* CAN data byte 2 */ + uint8_t data_3; /* CAN data byte 3 */ + uint16_t spacer_3; /* spacer for compatibility */ + uint8_t data_4; /* CAN data byte 4 */ + uint8_t data_5; /* CAN data byte 5 */ + uint16_t spacer_4; /* spacer for compatibility */ + uint8_t data_6; /* CAN data byte 6 */ + uint8_t data_7; /* CAN data byte 7 */ + uint16_t spacer_5; /* spacer for compatibility */ + uint8_t data_len; /* CAN data length: 0xf0 reserved, DLC{3:0} */ + uint8_t local_prio; /* MSCAN TX local priority - unused fro TX buffer */ + uint16_t spacer_6; /* spacer for compatibility */ + uint8_t timestamp_h; /* local timestamp - High byte - Read Only */ + uint8_t timestamp_l; /* local timestamp - Low byte - Read Only */ +}; + +/* structure for memory layout of acceptance + * filter registers + */ +struct mscan_flt_regs{ + uint8_t acp_id_0; + uint8_t acp_id_1; + uint16_t spacer_0; + + uint8_t acp_id_2; + uint8_t acp_id_3; + uint16_t spacer_1; + + uint8_t acp_mask_0; + uint8_t acp_mask_1; + uint16_t spacer_2; + + uint8_t acp_mask_2; + uint8_t acp_mask_3; + uint16_t spacer_3; + + uint8_t acp_id_4; + uint8_t acp_id_5; + uint16_t spacer_4; + + uint8_t acp_id_6; + uint8_t acp_id_7; + uint16_t spacer_5; + + uint8_t acp_mask_4; + uint8_t acp_mask_5; + uint16_t spacer_6; + + uint8_t acp_mask_6; + uint8_t acp_mask_7; +}; + +#endif /* LINCAN_MSCAN_H */ diff --git a/lincan/src/Makefile.omk b/lincan/src/Makefile.omk index a273b78..e9eecd6 100644 --- a/lincan/src/Makefile.omk +++ b/lincan/src/Makefile.omk @@ -2,7 +2,7 @@ lincan_cards_NAMES = pip pccan smartcan nsi cc_can104 ems_cpcpci \ pc_i03 pcm3680 aim104 m437 pcccan ssv bfadcan gensja1000io gensja1000mm eb8245 \ kv_pcican msmcan oscar adlink7841 pcan_pci esdpci200 unican usbcan virtual template -lincan_morecards_NAMES = esdpci266 hms30c7202_can ns_dev_can ipci165 pimx1 tscan1 ts7kv nsi_canpci sh7760 +lincan_morecards_NAMES = esdpci266 hms30c7202_can ns_dev_can ipci165 pimx1 tscan1 ts7kv nsi_canpci sh7760 mpc5200 default_CONFIG = CONFIG_OC_LINCAN=y CONFIG_OC_LINCANRTL=n CONFIG_OC_LINCANVME=n default_CONFIG += CONFIG_OC_LINCAN_PORTIO_ONLY=n CONFIG_OC_LINCAN_MEMIO_ONLY=n @@ -46,6 +46,11 @@ $(warning Not finished C_CAN support requested) lincan_cards_SOURCES += c_can.c c_can_irq.c endif +# both cannot be selected, full MPC5200 has precedence +ifeq ($(CONFIG_OC_LINCAN_CARD_mpc5200),y) +lincan_cards_SOURCES += mscan.c +endif + ifneq ($(CONFIG_OC_LINCANRTL),y) kernel_MODULES = lincan diff --git a/lincan/src/boardlist.c b/lincan/src/boardlist.c index aa6fa05..7623ea3 100644 --- a/lincan/src/boardlist.c +++ b/lincan/src/boardlist.c @@ -1,5 +1,5 @@ /**************************************************************************/ -/* File: boardslist.c - list to translate hardware option to board struct*/ +/* File: boardslist.c - list to translate hardware option to board struct */ /* */ /* LinCAN - (Not only) Linux CAN bus driver */ /* Copyright (C) 2002-2009 DCE FEE CTU Prague */ @@ -77,6 +77,8 @@ extern int pcan_pci_register(struct hwspecops_t *hwspecops); extern int esdpci200_register(struct hwspecops_t *hwspecops); extern int esdpci266_register(struct hwspecops_t *hwspecops); extern int sh7760_register(struct hwspecops_t *hwspecops); +extern int mpc5200_register(struct hwspecops_t *hwspecops); +extern int mpc5200_midam_register(struct hwspecops_t *hwspecops); const struct boardtype_t can_boardtypes[]={ #ifdef CONFIG_OC_LINCAN_CARD_template @@ -202,7 +204,11 @@ const struct boardtype_t can_boardtypes[]={ #if defined(CONFIG_OC_LINCAN_CARD_usbcan) {"usbcan", usbcan_register, 0}, #endif - {NULL} + #if defined(CONFIG_OC_LINCAN_CARD_mpc5200) + {"mpc5200", mpc5200_register, 0}, + {"mpc5200_midam", mpc5200_midam_register, 0}, + #endif + {NULL} }; const struct boardtype_t* boardtype_find(const char *str) diff --git a/lincan/src/mpc5200.c b/lincan/src/mpc5200.c new file mode 100644 index 0000000..4f67d24 --- /dev/null +++ b/lincan/src/mpc5200.c @@ -0,0 +1,243 @@ +/**************************************************************************/ +/* File: mpc5200.c - Freescale MPC5200 MSCAN 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" +#include "../include/main.h" +#include "../include/mpc5200.h" +#include "../include/mscan.h" +#include "linux/of.h" +#include "linux/of_platform.h" + +int mpc5200_init_device_node(struct canchip_t * chip, struct device_node * devnode); + +/* in time when release_io should take place, we dont know the + * base addresses that has to be free. + * This variable keeps those addresses + */ +unsigned long * chips_addr; + + +int mpc5200_request_io(struct candevice_t *candev) +{ + int chipnr; + struct device_node * dn; + const char * of_devname = "can"; + + /* Workaround for OpenFormware */ + + /* Select device by chipnr: + * first dev is @ 900 + * second dev is @ 980 + * + * There are only two devices on MPC5200 */ + + /* initialize internal address storage */ + chips_addr = (unsigned long*)kmalloc(candev->nr_all_chips * sizeof(unsigned long), GFP_KERNEL); + + /* DEBUGMSG("Looking for device node '%s'\n", of_devname); */ + + chipnr = 0; + for_each_node_by_name(dn, of_devname) + { + /* DEBUGMSG(" got OF node (%s)...\n", of_devname); */ + + if (!of_device_is_available(dn)) + { + DEBUGMSG("\tdevice not available\n"); + continue; + } + + + if (mpc5200_init_device_node(candev->chip[chipnr], dn)) + return -ENODEV; + + /* fill received address to internal storage */ + chips_addr[chipnr] = (unsigned long)candev->chip[chipnr]->chip_base_addr; + + /* original MIDAM Shark board use only one CAN chip - this handles similar cases */ + if (++chipnr >= candev->nr_all_chips) + break; + } + + + DEBUGMSG("Succesfully mapped %d can devices\n", chipnr); + return 0; +} + +int mpc5200_release_io(struct candevice_t *candev) +{ + int i; + + /*can_release_io_region(candev->io_addr, candev->nr_all_chips * IO_RANGE);*/ + + /* free all chips memorey space - using internal address storage */ + for (i = 0; i < candev->nr_all_chips; i++) + iounmap((void*)chips_addr[i]); + + kfree(chips_addr); + + return 0; +} + +int mpc5200_reset(struct candevice_t *candev) +{ + int i; + DEBUGMSG("Resetting MSCAN chips ...\n"); + + for (i = 0; i < candev->nr_all_chips; i++) + { + /* !!! Assuming this card has ONLY MSCAN chips !!! */ + if (mscan_reset_chip(candev->chip[i])) + return -ENODEV; + } + + return 0; +} + +int mpc5200_init_hw_data(struct candevice_t *candev) +{ + /* candev->res_addr = RESET_ADDR; */ + candev->nr_82527_chips = NR_82527; + candev->nr_sja1000_chips = NR_SJA1000; + candev->nr_all_chips = NR_ALL; + /* candev->flags |= CANDEV_PROGRAMMABLE_IRQ; */ + + return 0; +} + +/* special function for midam board */ +int mpc5200_midam_init_hw_data(struct candevice_t *candev) +{ + /* use same init routine */ + mpc5200_init_hw_data(candev); + + /* modify chip count */ + candev->nr_all_chips = NR_ALL_MIDAM; + + return 0; +} + +int mpc5200_init_chip_data(struct candevice_t *candev, int chipnr) +{ + mscan_fill_chipspecops(candev->chip[chipnr]); + + candev->chip[chipnr]->clock = MPC5200_CLK_FREQ; + candev->chip[chipnr]->hostdevice = candev; + +/* + candev->chip[chipnr]->chip_base_addr = can_ioport2ioptr(candev->io_addr) + chipnr * MPC5200_CAN_CHIP_OFFSET; one chip with 2 interfaces + candev->chip[chipnr]->chip_irq = MPC5200_CAN_IRQ + chipnr; +*/ + DEBUGMSG("mpc5200_init_chip_data\n"); + + return 0; +} + +int mpc5200_init_obj_data(struct canchip_t *chip, int objnr) +{ + /* we have only two chips with only one mailbox each */ + chip->msgobj[objnr]->obj_base_addr = (can_ioptr_t) MSCAN_CTL0 + MPC5200_CAN_CHIP_OFFSET * objnr; + + return 0; +} + +int mpc5200_program_irq(struct candevice_t *candev) +{ + /* we don't use programmable interrupt on MPC5200 */ + return 0; +} + + +void mpc5200_write_register(unsigned data, can_ioptr_t address) +{ + /* address is an absolute address */ + /* DEBUGMSG("write register\n"); */ + writeb(data, address); /* regs in PowerPC (5200) are one-byte length */ +} + +unsigned mpc5200_read_register(can_ioptr_t address) +{ + /* address is an absolute address */ + /* DEBUGMSG("read register\n"); */ + return readb(address); /* regs in PowerPC (5200) are one-byte length */ +} + +int mpc5200_register(struct hwspecops_t *hwspecops) +{ + hwspecops->request_io = mpc5200_request_io; + hwspecops->release_io = mpc5200_release_io; + hwspecops->reset = mpc5200_reset; + hwspecops->init_hw_data = mpc5200_init_hw_data; + hwspecops->init_chip_data = mpc5200_init_chip_data; + hwspecops->init_obj_data = mpc5200_init_obj_data; + hwspecops->program_irq = NULL; /* mpc5200_program_irq; */ + hwspecops->write_register = mpc5200_write_register; + hwspecops->read_register = mpc5200_read_register; + return 0; +} + +int mpc5200_midam_register(struct hwspecops_t *hwspecops) +{ + /* use same register routine for MIDAM board */ + mpc5200_register(hwspecops); + + /* use specific init_hw_data */ + hwspecops->init_hw_data = mpc5200_midam_init_hw_data; + + return 0; +} + +int mpc5200_init_device_node(struct canchip_t * chip, struct device_node * devnode) +{ + chip->chip_base_addr = of_iomap(devnode, 0); + if (!chip->chip_base_addr) + { + DEBUGMSG("\tcannot map IO - of_iomap\n"); + return -ENODEV; + } + + chip->chip_irq = irq_of_parse_and_map(devnode, 0); + if (!chip->chip_irq) + { + DEBUGMSG("\tcannot map IRQ\n"); + iounmap(chip->chip_base_addr); + return -ENODEV; + } + + DEBUGMSG("Bound to io-addr: 0x%08x IRQ: %d\n", (unsigned int)chip->chip_base_addr, chip->chip_irq); + + return 0; +} diff --git a/lincan/src/mscan.c b/lincan/src/mscan.c new file mode 100644 index 0000000..74c086a --- /dev/null +++ b/lincan/src/mscan.c @@ -0,0 +1,1442 @@ +/**************************************************************************/ +/* File: mscan.c - Freescale MPC5200 MSCAN 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" +#include "../include/main.h" +#include "../include/mscan.h" +//#include "../include/mpc5200.h" + + +#define MSCAN_MAX_TRANSMIT_WAIT_LOOPS 20 +#define MSCAN_MAX_SETTING_WAIT_LOOPS 25 /* maximal loop count while checking Chip reply to action ~ 5 ms */ +#define MSCAN_MAX_IRQ_WAIT_LOOPS 25 + +CAN_DEFINE_SPINLOCK(mscan_prewr_lock); + +void mscan_irq_read_handler(struct canchip_t *chip, struct msgobj_t *obj); +void mscan_irq_write_handler(struct canchip_t *chip, struct msgobj_t *obj); + +int mscan_clear_irq_flags(struct canchip_t *chip); +void mscan_setup_ctrl_regs(struct canmsg_t * msg, uint16_t * ctrl0, uint16_t * ctrl1, uint16_t * ctrl2); + +void mscan_msg_from_rxbuffer(struct canchip_t * chip, struct canmsg_t * msg); +void mscan_setup_txbuffer(struct canchip_t * chip, struct canmsg_t * msg); + +void mscan_notifyRXends(struct msgobj_t * obj, int what); +int mscan_check_txbuff_stat(struct canchip_t *chip, int buffer); + +static int mscan_update_samplept(int sampl_pt, int tseg, int *tseg1, int *tseg2); + +static int mscan_init_mode_active(struct canchip_t *chip); +static int mscan_sleep_mode_active(struct canchip_t *chip); + +static int mscan_enter_init_mode(struct canchip_t * chip); +static int mscan_enter_sleep_mode(struct canchip_t * chip); + +static reg_t mscan_get_flags(struct canchip_t * chip, reg_t flags, unsigned reg_addr); +static void mscan_set_flags(struct canchip_t * chip, reg_t flags, unsigned reg_addr); +static void mscan_clear_flags(struct canchip_t * chip, reg_t flags, unsigned reg_addr); +static uint16_t mscan_get_irq_flags(struct canchip_t * chip); + +static void mscan_clear_buffer(struct canchip_t * chip, unsigned start_addr); +static int mscan_abort_msg(struct canchip_t * chip, reg_t buffer_mask); + +#if MPC5200_DBG + static void dump_regs(struct canchip_t * chip) + { + CANMSG("MSCAN reg dump\n"); + CANMSG("CTL0 0x%02x\tCTL1 0x%02x\tBTR0 0x%02x\tBTR1 0x%02x\n", + mscan_get_flags(chip, 0xff, MSCAN_CTL0), + mscan_get_flags(chip, 0xff, MSCAN_CTL1), + mscan_get_flags(chip, 0xff, MSCAN_BTR0), + mscan_get_flags(chip, 0xff, MSCAN_BTR1)); + + CANMSG("RFLG 0x%02x\tRIER 0x%02x\tTFLG 0x%02x\tTIER 0x%02x\n", + mscan_get_flags(chip, 0xff, MSCAN_RFLG), + mscan_get_flags(chip, 0xff, MSCAN_RIER), + mscan_get_flags(chip, 0xff, MSCAN_TFLG), + mscan_get_flags(chip, 0xff, MSCAN_TIER)); + + CANMSG("TARQ 0x%02x\tTAAK 0x%02x\tTBSEL 0x%02x\tIDAC 0x%02x\n", + mscan_get_flags(chip, 0xff, MSCAN_TARQ), + mscan_get_flags(chip, 0xff, MSCAN_TAAK), + mscan_get_flags(chip, 0xff, MSCAN_TBSEL), + mscan_get_flags(chip, 0xff, MSCAN_IDAC)); + + CANMSG("RXERR 0x%02x\tTXERR 0x%02x\n", + mscan_get_flags(chip, 0xff, MSCAN_RXERR), + mscan_get_flags(chip, 0xff, MSCAN_TXERR)); + + } + + static void dump_buff(struct canchip_t * chip, unsigned offset_addr) + { + volatile struct mscan_msg_buffer * buff = (struct mscan_msg_buffer *)(chip->chip_base_addr + offset_addr); + + CANMSG("MSCAN buffer dump\n"); + + /* structural access */ + CANMSG("Data0 0x%02x Data1 0x%02x Data2 0x%02x Data3 0x%02x Data4 0x%02x Data5 0x%02x Data6 0x%02x Data7 0x%02x\n", + buff->data_0, buff->data_1, buff->data_2, buff->data_3, buff->data_4, buff->data_5, buff->data_6, buff->data_7); + CANMSG("Data Len %d\tPriority 0x%02x\n", + buff->data_len, buff->local_prio); + CANMSG("IDR0 0x%02x\tIDR1 0x%02x\tIDR2 0x%02x\tIDR3 0x%02x\n", + buff->id_0, buff->id_1, buff->id_2, buff->id_3); + + } + + static void dump_filter(struct canchip_t * chip) + { + volatile struct mscan_flt_regs * flt = (struct mscan_flt_regs *)(chip->chip_base_addr + MSCAN_IDAR0); + + CANMSG("MSCAN Acceptance filter dump\n"); + + CANMSG("IDAC 0x%02x\n", mscan_get_flags(chip, MSCAN_IDAC_IDAM | MSCAN_IDAC_IDHIT, MSCAN_IDAC)); + + CANMSG("IDAR0 0x%02x\tIDAR1 0x%02x\tIDAR2 0x%02x\tIDAR3 0x%02x\n", + flt->acp_id_0, flt->acp_id_1, flt->acp_id_2, flt->acp_id_3); + CANMSG("IDMR0 0x%02x\tIDMR1 0x%02x\tIDMR2 0x%02x\tIDMR3 0x%02x\n", + flt->acp_mask_0, flt->acp_mask_1, flt->acp_mask_2, flt->acp_mask_3); + + CANMSG("IDAR4 0x%02x\tIDAR5 0x%02x\tIDAR6 0x%02x\tIDAR7 0x%02x\n", + flt->acp_id_4, flt->acp_id_5, flt->acp_id_6, flt->acp_id_7); + CANMSG("IDMR4 0x%02x\tIDMR5 0x%02x\tIDMR6 0x%02x\tIDMR7 0x%02x\n", + flt->acp_mask_4, flt->acp_mask_5, flt->acp_mask_6, flt->acp_mask_7); + } +#endif /* MPC5200_DBG */ + + +/* macro for standardized CAN Bus Status change report */ +#define MSCAN_STAT_CHANGE(msg,idx) CANMSG("MSCAN chip %d %s\n", idx, msg) + +/* Enable folowing IRQs + * MSCAN_TIER_TXE - Transmit Empty Interrupt - Set and Cleared during TX proccess not during init + * MSCAN_RIER_RXFIE - Receive Buffer Full + * MSCAN_RIER_RSTATE - Receiver Status Change Enable: 0-Never, 1-BusOff, 2-BusOff+RxErr, 3-All + * MSCAN_RIER_TSTATE - Transmitter Status Change Enable: 0-Never, 1-BusOff, 2-BusOff+TxErr, 3-All + * MSCAN_RIER_OVRIE - Overrun + * MSCAN_RIER_CSCIE - CAN Status Change + */ +uint16_t mscan_IRQs = MSCAN_RIER_RXFIE | + MSCAN_RIER_RSTATE | MSCAN_RIER_TSTATE | + MSCAN_RIER_OVRIE | MSCAN_RIER_CSCIE; /* TX interrupt flag is held shifted */ +/* 1 - enable interrupt, 0 - interrupt is masked */ + +/* used to distinguish which direction (TX/RX) caused Can Bus State change interrupt */ +reg_t rxstat = 0; +reg_t txstat = 0; + +/* initial acceptance filter settings */ +#define INIT_STD_ID 0x0000 +#define INIT_STD_MASK 0x0fff /* accept all messages - 11 mask bits and LSB is RTR */ + + + +/* ************************************************************************************************************************************* */ + + +int mscan_chip_config(struct canchip_t *chip) +{ + /* Do not clear buffers here, they may contain valid data ! */ + int err; + + DEBUGMSG("Configuring chip...\n"); + + if ((err = mscan_enable_configuration(chip))) + return err; + + if (!chip->baudrate) + chip->baudrate=1000000; /* default baudrate set to 1Mbit */ + if ((err = mscan_baud_rate(chip, chip->baudrate, chip->clock, 0, 750, 0))) + return err; + + if ((err = mscan_disable_configuration(chip))) + return err; + + /* IRQ mask could be set only when NOT in INIT mode */ + if ((err = mscan_config_irqs(chip, mscan_IRQs))) + return err; + + return 0; +} + +/* enter initialization AND sleep mode */ +int mscan_enable_configuration(struct canchip_t *chip) +{ + /* Do not clear buffers here, they may contain valid data ! */ + + DEBUGMSG("config ENABLE\n"); + + if (mscan_init_mode_active(chip)) /* chip is already in config mode */ + { + DEBUGMSG("config ENABLE ... already in INIT mode\n"); + goto econf_exit_ok; + } + + /* Disable interrupt */ + can_disable_irq(chip->chip_irq); + + + /* Sleep mode - disable CAN activity after completing current operation */ + /* if controler not synced to bus, skip sleep mode */ + if (!mscan_sleep_mode_active(chip) && + mscan_get_flags(chip, MSCAN_CTL0_SYNCH, MSCAN_CTL0)) + if (mscan_enter_sleep_mode(chip)) + { + /* cannot enter sleep mode */ + DUMPREGS(chip); + DEBUGMSG("Forcig INIT\n"); + } + + + /* now we can enter Init mode */ + if(mscan_enter_init_mode(chip)) + goto econf_exit_busy; + +econf_exit_ok: + /* DEBUGMSG("config ENABLE ... OK\n"); */ + return 0; + +econf_exit_busy: + /* DEBUGMSG("config ENABLE ... failed !\n"); */ + can_enable_irq(chip->chip_irq); + return -EBUSY; +} + +int mscan_disable_configuration(struct canchip_t *chip) +{ + /* Do not clear buffers here, they may contain valid data ! */ + int i = 0; + + DEBUGMSG("config DISABLE\n"); + + if (!mscan_init_mode_active(chip) && !mscan_sleep_mode_active(chip)) /* chip is already in normal mode */ + { + DEBUGMSG("config DISABLE ... not in INIT mode\n"); + return 0; + } + + + /* disable Init mode */ + mscan_clear_flags(chip, MSCAN_CTL0_INITRQ, MSCAN_CTL0); + + /* Waits until chip leaves Sleep mode */ + while (mscan_get_flags(chip, MSCAN_CTL1_INITAK, MSCAN_CTL1) && i++ < MSCAN_MAX_SETTING_WAIT_LOOPS) + udelay(200); + + if (i >= MSCAN_MAX_SETTING_WAIT_LOOPS) { + DEBUGMSG("Error leaving Init mode (disable configuration) \n"); + return -EBUSY; + } + + + /* disable Sleep mode */ + mscan_clear_flags(chip, MSCAN_CTL0_SLPRQ, MSCAN_CTL0); + + i = 0; + /* Waits until chip leaves Init mode */ + while (mscan_get_flags(chip, MSCAN_CTL1_SLPAK, MSCAN_CTL1) && i++ < MSCAN_MAX_SETTING_WAIT_LOOPS) + udelay(200); + + if (i >= MSCAN_MAX_SETTING_WAIT_LOOPS) { + DEBUGMSG("Error leaving Sleep mode (disable configuration) \n"); + return -EBUSY; + } + + + /* Enable interrupt */ + can_enable_irq(chip->chip_irq); + + /* DEBUGMSG("config DISABLE ... OK\n"); */ + + return 0; +} + + +/* ************************************************************************************************************************************* */ + + +int mscan_baud_rate(struct canchip_t *chip, int bitrate, int clock, int sjw, int sampl_pt, int flags) +{ + /* Set communication parameters. + * param rate baud rate in Hz + * param clock frequency of mscan clock in Hz + * param sjw synchronization jump width (0-3) prescaled clock cycles + * param sampl_pt sample point (0-1000) sets (TSEG1 + 1)/(TSEG1 + TSEG2 + 2) ratio + * flags not used + */ + + + /* rate = clock / ((tseg1 + tseg2 + 1) * brp ) */ + + long rate, best_rate = 0; + long best_error = 1000000000, error = 0; + int best_tseg = 0, best_brp = 0, brp = 0; + int tsegall, tseg = 0, tseg1 = 0, tseg2 = 0; + int spt = 0, spt_error = best_error; + reg_t btr0, btr1; + + + /* Use CIA recommended sample points */ + if (!sampl_pt) + { + if (bitrate > 800000) + sampl_pt = 750; + else if (bitrate > 500000) + sampl_pt = 800; + else + sampl_pt = 875; + } + + /* tseg even = round down, odd = round up */ + for (tseg = MSCAN_TSEG_MAX * 2 + 1; tseg >= MSCAN_TSEG_MIN * 2; tseg--) + { + tsegall = 1 + tseg / 2; + /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ + brp = clock / (tsegall * bitrate) + tseg % 2; + + if ((brp < MSCAN_BRP_MIN) || (brp > MSCAN_BRP_MAX)) + continue; + + /* tseg brp biterror */ + rate = clock / (brp * tsegall); + error = bitrate - rate; + + if (error < 0) + error = -error; + if (error > best_error) + continue; + best_error = error; + + if (error == 0) { + spt = mscan_update_samplept(sampl_pt, tseg / 2, &tseg1, &tseg2); + error = sampl_pt - spt; + + if (error < 0) + error = -error; + if (error > spt_error) + continue; + spt_error = error; + } + + best_tseg = tseg / 2; + best_brp = brp; + best_rate = rate; + if (error == 0) + break; + } + + if (best_error) { + /* Error in one-tenth of a percent */ + error = (best_error * 1000) / bitrate; + DEBUGMSG("Baudrate error %lu\n", error); + } + + + /* recompute with best values */ + spt = mscan_update_samplept(sampl_pt, best_tseg, &tseg1, &tseg2); + + DEBUGMSG("Setting %lu bps.\n", best_rate); + + /* all values has offset -1 in MSCAN memory */ + best_brp--; tseg1--; tseg2--; + + btr0 = (best_brp & MSCAN_BTR0_BRP) | ((sjw << 6) & MSCAN_BTR0_SJW); + btr1 = (tseg1 & MSCAN_BTR1_TSEG1) | ((tseg2 << 4) & MSCAN_BTR1_TSEG2); + + mscan_set_btregs(chip, btr0, btr1); + + mscan_disable_configuration(chip); + +/* DEBUGMSG("Baud rate set successfully\n"); */ + return 0; +} + +int mscan_set_btregs(struct canchip_t *chip, unsigned short bcr0, unsigned short bcr1) +{ +/* DEBUGMSG("Seting BCR0 and BCR1.\n"); */ + reg_t btr0, btr1; + + btr0 = (reg_t)bcr0; + btr1 = (reg_t)bcr1; + + btr1 &= ~MSCAN_BTR1_SAMP; /* use one-point sample, not three smaples per bit */ + + DEBUGMSG("BTR0 0x%02x BTR1 0x%02x\n", btr0, btr1); + + can_write_reg(chip, btr0, MSCAN_BTR0); + can_write_reg(chip, btr1, MSCAN_BTR1); + +/* DEBUGMSG("BCR0 and BCR1 successfully set.\n"); */ + return 0; +} + + +/* ************************************************************************************************************************************* */ + + +int mscan_start_chip(struct canchip_t *chip) +{ +/* DEBUGMSG("Starting chip %d...\n", chip->chip_idx); */ + + /* Stop chip turns chip to Sleep&Init mode - traffic on CAN bus is ignored after completing curent operation. + * Start chip only turn chip back from Sleep&Init state - using disable_config + */ + mscan_disable_configuration(chip); + + DEBUGMSG("Chip [%d] started\n", chip->chip_idx); + return 0; +} + +int mscan_stop_chip(struct canchip_t *chip) +{ +/* DEBUGMSG("Stopping chip %d...\n", chip->chip_idx); */ + + /* Stop chip turns chip to Sleep&Init mode - traffic on CAN bus is ignored after completing curent operation. + * - using enable_config + */ + mscan_enable_configuration(chip); + + DEBUGMSG("Chip [%d] stopped\n", chip->chip_idx); + return 0; +} + +int mscan_attach_to_chip(struct canchip_t *chip) +{ +/* DEBUGMSG("Attaching to chip %d.\n", chip->chip_idx); */ + reg_t ctl1; + + + /* Clear all TX and RX buffers */ + if (mscan_clear_objects(chip)) + return -ENODEV; + + + /* Transmitt Abort Request Register (TARQ) is clean after INIT mode - no need to clear it explicitly */ + /* Control Register 0 (CTL0) is clean after INIT too (excepts fro WUPE, SLRQ and INITRQ) */ + + /* initialize chip by entering Sleep & Init mode */ + if (mscan_enable_configuration(chip)) + return -ENODEV; + + /* reset Control Register 1 (CTL1) */ + ctl1 = MSCAN_CTL1_CANE | + (MPC5200_CLKSRC ? MSCAN_CTL1_CLKSRC : 0x00) ; + /* MSCAN_CTL1_LOOPB | LoopBack mode not used */ + /* MSCAN_CTL1_LISTEN | Listen-Only mode not used */ + /* MSCAN_CTL1_WUPM | WakeUp mode not used */ + /* MSCAN_CTL1_SLPAK | ReadOnly */ + /* MSCAN_CTL1_INITAK | ReadOnly */ + + can_write_reg(chip, ctl1, MSCAN_CTL1); + + + /* set filter to accept all STD messages */ + if (mscan_standard_mask(chip, INIT_STD_ID, INIT_STD_MASK)) + { + DEBUGMSG("Failed to set initial filter ID: %d Mask: 0x%04x\n", INIT_STD_ID, INIT_STD_MASK); + } + + /* set Baudrate and Interrupts */ + if (mscan_chip_config(chip)) + return -ENODEV; + + /* not neccessary, but just to be sure */ + if (mscan_disable_configuration(chip)) + return -ENODEV; + + /* Enable interrupt - called in mpc5200_request_io */ + /* can_enable_irq(chip->chip_irq); */ + + DEBUGMSG("Successfully attached to chip [%02d].\n", chip->chip_idx); + + return 0; +} + +int mscan_release_chip(struct canchip_t *chip) +{ + /* IRQ unmapped in lincan driver core */ + can_disable_irq(chip->chip_irq); + + mscan_clear_objects(chip); /* Cannot be called in INIT mode */ + + + mscan_stop_chip(chip); /* we are in INIT mode */ + + /* disable chip */ + mscan_clear_flags(chip, MSCAN_CTL1_CANE, MSCAN_CTL1); + + DEBUGMSG("Chip released [%02d]\n", chip->chip_idx); + return 0; +} + + +/* ************************************************************************************************************************************* */ + +/* has to be called in Init & Sleep mode */ +int mscan_standard_mask(struct canchip_t *chip, unsigned short code, unsigned short mask) +{ + /* code - contains 11bit ID and as LSB the RTR flag */ + /* code - contains 11bit mask and RTR mask as LSB */ + + reg_t idr0, idr1, mr0, mr1; /* ID register 0,1, Mask register 0, 1 */ + + if (code & 0x1ffff800) + return mscan_extended_mask(chip, code, mask); + + + /* we use two 32-bit acceptance filters */ + mscan_clear_flags(chip, MSCAN_IDAC_IDAM, MSCAN_IDAC); + + + idr0 = (reg_t)((code & 0x0ff0) >> 4); /* 8 most significant bits */ + idr1 = (reg_t)((code & 0x000f) << 4); /* 3 last bits, RTR as LSB, IDE=0 doesnt have to be set explicitly */ + mr0 = (reg_t)((mask & 0x0ff0) >> 4); + mr1 = (reg_t)(((mask & 0x000f) << 4) & 0xf0); /* to be sure IDE=0 */ + + + can_write_reg(chip, idr0, MSCAN_IDAR0); + can_write_reg(chip, idr1, MSCAN_IDAR1); + + can_write_reg(chip, mr0, MSCAN_IDMR0); + can_write_reg(chip, mr1, MSCAN_IDMR1); + + /* use same filtering settings for second filter */ + can_write_reg(chip, idr0, MSCAN_IDAR4); + can_write_reg(chip, idr1, MSCAN_IDAR5); + + can_write_reg(chip, mr0, MSCAN_IDMR4); + can_write_reg(chip, mr1, MSCAN_IDMR5); + + + DEBUGMSG("Set standard_mask [id:0x%04x RTR=%d, m:0x%04x RTR=%d]\n", code >> 1, code & 0x0001, mask >> 1, mask & 0x0001); + DUMPFLT(chip); + + return 0; +} + +/* has to be called in Init & Sleep mode */ +int mscan_extended_mask(struct canchip_t *chip, unsigned long code, unsigned long mask) +{ + /* code - contains 11bit ID and as LSB the RTR flag */ + /* code - contains 11bit mask and RTR mask as LSB */ + + reg_t idr0, idr1, idr2, idr3; /* ID registers 0,1,2,3 */ + reg_t mr0, mr1, mr2, mr3; /* Mask registers 0,1,2,3 */ + + + /* we use two 32-bit acceptance filters */ + mscan_clear_flags(chip, MSCAN_IDAC_IDAM, MSCAN_IDAC); + + + idr0 = (reg_t)((code & 0x7fa00000) >> 22); /* EXT_ID {29:21} */ + + idr1 = (reg_t)((code & 0x00380000) >> 14); /* EXT_ID {20:18} */ + idr1|= 0x18; /* SRR and IDE */ + idr1|= (reg_t)((code & 0x00070000) >> 16); /* EXT_ID {17:15} */ + + idr2 = (reg_t)((code & 0x0000ff00) >> 8); /* EXT_ID {14:7} */ + idr3 = (reg_t) (code & 0x000000ff); /* EXT_ID {6:0} and RTR */ + + + mr0 = (reg_t)((mask & 0x7fa00000) >> 22); /* EXT_ID {29:21} */ + + mr1 = (reg_t)((mask & 0x00380000) >> 14); /* EXT_ID {20:18} */ + /* SRR=0 and IDE=0 - do not ignore */ + mr1|= (reg_t)((mask & 0x00070000) >> 16); /* EXT_ID {17:15} */ + + mr2 = (reg_t)((mask & 0x0000ff00) >> 8); /* EXT_ID {14:7} */ + mr3 = (reg_t) (mask & 0x000000ff); /* EXT_ID {6:0} and RTR */ + + + can_write_reg(chip, idr0, MSCAN_IDAR0); + can_write_reg(chip, idr1, MSCAN_IDAR1); + can_write_reg(chip, idr2, MSCAN_IDAR2); + can_write_reg(chip, idr3, MSCAN_IDAR3); + + can_write_reg(chip, mr0, MSCAN_IDMR0); + can_write_reg(chip, mr1, MSCAN_IDMR1); + can_write_reg(chip, mr2, MSCAN_IDMR2); + can_write_reg(chip, mr3, MSCAN_IDMR3); + + /* use same filtering settings for second filter */ + can_write_reg(chip, idr0, MSCAN_IDAR4); + can_write_reg(chip, idr1, MSCAN_IDAR5); + can_write_reg(chip, idr2, MSCAN_IDAR6); + can_write_reg(chip, idr3, MSCAN_IDAR7); + + can_write_reg(chip, mr0, MSCAN_IDMR4); + can_write_reg(chip, mr1, MSCAN_IDMR5); + can_write_reg(chip, mr2, MSCAN_IDMR6); + can_write_reg(chip, mr3, MSCAN_IDMR7); + + + DEBUGMSG("Set extended_mask [id:0x%08x RTR=%lu, m:0x%08x RTR=%lu]\n", (uint32_t)(code >> 1), code & 0x00000001, (uint32_t)(mask >> 1), mask & 0x00000001); + DUMPFLT(chip); + + return 0; +} + + +/* ************************************************************************************************************************************* */ + + +int mscan_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj) +{ + DEBUGRX("Pre read config\n"); + + /* MSCAN has only one buffer, which is already initialized */ + + return 0; +} + +int mscan_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg) +{ + reg_t txf; + reg_t bf; + int buff_no; + + can_spin_lock(&mscan_prewr_lock); + can_preempt_disable(); + DEBUGTX("Pre write config\n"); + + /* find free buffer */ + txf = mscan_get_flags(chip, MSCAN_TFLG_TXE, MSCAN_TFLG); + for (buff_no = 0; buff_no < 3 && !(txf & (0x01 << buff_no)); buff_no++) { } + + if (buff_no >= 3) + { + DEBUGTX("no free buffer found [0x%02x]\n", txf); + goto pwrite_exit_fail; /* no free buffer found */ + } + + + /* Select buffer */ + bf = (reg_t)(0x01 << buff_no); + can_write_reg(chip, bf & MSCAN_TBSEL_TX, MSCAN_TBSEL); + + DEBUGTX("Buffer %d\n", buff_no); + + mscan_setup_txbuffer(chip, msg); + + /* store buffer flag into canmsg_t->cob for future + * use in send_msg + * canmsg_t->cob is not used according to Lincan documentation */ + msg->cob = bf; + + + can_preempt_enable(); + can_spin_unlock(&mscan_prewr_lock); + return 0; + +pwrite_exit_fail: + can_preempt_enable(); + can_spin_unlock(&mscan_prewr_lock); + return -ENODEV; /* no free buffer found */ +} + +int mscan_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg) +{ + DEBUGTX("Send Message\n"); + + /* turn on IRQ for this buffer */ + can_write_reg(chip, msg->cob, MSCAN_TIER); + + /* clear TX Buffer Empty flag (by writing '1') to initiate trasmition */ + can_write_reg(chip, msg->cob, MSCAN_TFLG); + + return 0; +} + +int mscan_remote_request(struct canchip_t *chip, struct msgobj_t *obj) +{ + DEBUGMSG("mscan_remote_request not implemented\n"); + return -ENOSYS; +} + + +/* ************************************************************************************************************************************* */ + + +int mscan_irq_handler(int irq, struct canchip_t *chip) +{ +/* + MSCAN_RFLG_CSCIF - CAN Status Change Interrupt Flag + MSCAN_RFLG_RSTAT - Receiver Status Bits: 0-RxOK, 1-RxWRN, 2-RxERR, 3-BusOff + MSCAN_RFLG_TSTAT - Transmitter Status Bits: 0-TxOK, 1-TxWRN, 2-TxErr, 3-BusOff + MSCAN_RFLG_OVRIF - Overrun Interrupt Flag + MSCAN_RFLG_RXF - Receive Buffer Full + + MSCAN_TFLG_TXE - Transmitter Buffer Empty */ + + /* get IRQs */ + uint16_t irq_reg, tx_irq; + reg_t irq_rstat, irq_tstat; + short loop_cnt = MSCAN_MAX_IRQ_WAIT_LOOPS; + + tx_irq = (MSCAN_TFLG_TXE << 8); /* to spare few tacts in IRQ loop */ + + irq_reg = mscan_get_irq_flags(chip); + DEBUGMSG("IRQ Handler chip %d (%d)\n", chip->chip_idx, irq); + + + do { + if(!loop_cnt--) + goto irqhnd_exit_stuck; + + DEBUGMSG("IRR: 0x%04x\n", irq_reg); + + /* Received message */ + if (irq_reg & MSCAN_RFLG_RXF) + { + DEBUGRX("Received message\n"); + + mscan_irq_read_handler(chip, chip->msgobj[0]); + /* RXPR flag for this msgobj is cleared during irq_read_handler*/ + + /* reset flag */ + mscan_set_flags(chip, MSCAN_RFLG_RXF, MSCAN_RFLG); + } + + + /* Can Status change - due to RX/TX error counters */ + if(irq_reg & MSCAN_RFLG_CSCIF) + { + irq_rstat = (irq_reg >> 4) & 0x03; + irq_tstat = (irq_reg >> 2) & 0x03; + + + /* Receive bus off/error/warning */ + if (irq_rstat ^ rxstat) /* check whether RSTAT changed */ + { + switch (irq_rstat) + { + case 3: /* Bus off */ + MSCAN_STAT_CHANGE("RX: BUS OFF", chip->chip_idx); + + chip->msgobj[0]->ret=-1; + + /* notify RX */ + mscan_notifyRXends(chip->msgobj[0], CANQUEUE_NOTIFY_ERROR); + break; + + case 2: /* bus - error passive */ + MSCAN_STAT_CHANGE("RX: ERROR PASSIVE", chip->chip_idx); + /* Show warning only */ + break; + + case 1: /* bus - warning */ + MSCAN_STAT_CHANGE("RX: Bus Warning", chip->chip_idx); + /* Show warning only */ + break; + + case 0: /* bus - OK */ + MSCAN_STAT_CHANGE("RX: Bus OK", chip->chip_idx); + /* Show warning only */ + break; + } + rxstat = irq_rstat; /* update static RX status field */ + } + + /* Transmit bus off/error/warning */ + if (irq_tstat ^ txstat) + { + switch (irq_tstat) + { + case 3: /* Bus off */ + MSCAN_STAT_CHANGE("TX: BUS OFF", chip->chip_idx); + + /* notify TX end */ + if(chip->msgobj[0]->tx_slot) + canque_notify_inends(chip->msgobj[0]->tx_qedge, CANQUEUE_NOTIFY_ERROR); + break; + + case 2: /* bus - error passive */ + MSCAN_STAT_CHANGE("TX: ERROR PASSIVE", chip->chip_idx); + /* Show warning only */ + break; + + case 1: /* bus - warning */ + MSCAN_STAT_CHANGE("TX: Bus Warning", chip->chip_idx); + /* Show warning only */ + break; + + case 0: /* bus - OK */ + MSCAN_STAT_CHANGE("TX: Bus OK", chip->chip_idx); + /* Show warning only */ + break; + } + txstat = irq_tstat; /* update static TX status field */ + } + + /* reset flag */ + mscan_set_flags(chip, MSCAN_RFLG_CSCIF, MSCAN_RFLG); + } + + /* Message Overrun/Overwritten */ + if (irq_reg & MSCAN_RFLG_OVRIF) + { + CANMSG("Error: MESSAGE OVERRUN/OVERWRITTEN"); + + /* notify only injured RXqueue-end */ + mscan_notifyRXends(chip->msgobj[0], CANQUEUE_NOTIFY_ERROR); + + /* reset flag */ + mscan_set_flags(chip, MSCAN_RFLG_OVRIF, MSCAN_RFLG); + } + + /* Mailbox empty - after message was sent */ + if (irq_reg & tx_irq) + { + DEBUGTX("Buffer empty interrupt\n"); + + /* clear propriate TXE IRE Enabled flag */ + can_spin_lock(&mscan_prewr_lock); + mscan_clear_flags(chip, (irq_reg >> 8) & MSCAN_TIER_TXE, MSCAN_TIER); + can_spin_unlock(&mscan_prewr_lock); + + + /* sends message */ + mscan_wakeup_tx(chip, chip->msgobj[0]); + } + + irq_reg = mscan_get_irq_flags(chip); + + /* omit RSTAT and TSTAT - they're used only in CSCIF handler */ + } while(irq_reg & + mscan_IRQs & + ~(MSCAN_RFLG_RSTAT | MSCAN_RFLG_TSTAT)); + + + return CANCHIP_IRQ_HANDLED; + +irqhnd_exit_stuck: + DEBUGMSG("mscan_irq_handler IRQ %d stuck\n", irq); + return CANCHIP_IRQ_STUCK; +} + +int mscan_irq_accept(int irq, struct canchip_t *chip) +{ + DEBUGMSG("mscan_irq_accept NOT IMPLEMENTED\n"); + return -ENOSYS; +} + +int mscan_config_irqs(struct canchip_t *chip, short irqs) +{ + int err; + reg_t rier, tier; + + DEBUGMSG("config IRQs\n"); + + if (mscan_init_mode_active(chip)) + { + DEBUGMSG("MSCAN: Setting Up IRQs while INIT active \n"); + return -EBUSY; + } + + rier = (reg_t)(irqs & 0x00ff); + tier = (reg_t)((irqs >> 8) & 0x00ff); + + if((err = mscan_clear_irq_flags(chip))) + return err; + + can_write_reg(chip, rier, MSCAN_RIER); + can_write_reg(chip, tier, MSCAN_TIER); + + DEBUGMSG("IRQ Mask set [rier: 0x%02x]\n", rier); + return 0; +} + + +/* ************************************************************************************************************************************* */ + + +int mscan_clear_objects(struct canchip_t *chip) +{ + /* clear RX buffer */ + mscan_clear_buffer(chip, MSCAN_RXFG); + + /* clear TX buffers, need to set CANTBSEL register */ + can_write_reg(chip, 0x01, MSCAN_TBSEL); + mscan_clear_buffer(chip, MSCAN_TXFG); + + can_write_reg(chip, 0x02, MSCAN_TBSEL); + mscan_clear_buffer(chip, MSCAN_TXFG); + + can_write_reg(chip, 0x04, MSCAN_TBSEL); + mscan_clear_buffer(chip, MSCAN_TXFG); + + return 0; +} + +int mscan_check_tx_stat(struct canchip_t *chip) +{ +/* DEBUGMSG("Check TX stat\n"); */ + /* If Transmition is complete return 0 - no error */ + if (mscan_get_flags(chip, MSCAN_TFLG_TXE, MSCAN_TFLG) == MSCAN_TFLG_TXE) /* all buffers empty */ + return 0; + else + return 1; +} + +/* Note: this checks TX status of particular buffer */ +int mscan_check_txbuff_stat(struct canchip_t *chip, int buffer) +{ + /* Transmition is complete return 0 - no error */ + reg_t txf = mscan_get_flags(chip, MSCAN_TFLG_TXE & (0x01 << buffer), MSCAN_TFLG); + return (txf ^ (0x01 << buffer)); +} + +int mscan_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj) +{ + can_preempt_disable(); + DEBUGMSG("WakeUP TX\n"); + + can_msgobj_set_fl(obj, TX_REQUEST); + while(!can_msgobj_test_and_set_fl(obj, TX_LOCK)) { + can_msgobj_clear_fl(obj, TX_REQUEST); + + /* enable transmition only if any TX buffer is empty */ + if (mscan_get_flags(chip, MSCAN_TFLG_TXE, MSCAN_TFLG)) + mscan_irq_write_handler(chip, obj); + else + { + can_msgobj_clear_fl(obj, TX_LOCK); + break; + } + + + can_msgobj_clear_fl(obj, TX_LOCK); + + if(!can_msgobj_test_fl(obj, TX_REQUEST)) + break; + } + + + can_preempt_enable(); + + DEBUGMSG("WakeUP TX - END\n"); + return 0; +} + +int mscan_filtch_rq(struct canchip_t *chip, struct msgobj_t * obj) +{ + struct canfilt_t filter; + uint32_t mask; + + +#if MPC5200_DBG + int num = canqueue_ends_filt_conjuction(obj->qends, &filter); +#else + canqueue_ends_filt_conjuction(obj->qends, &filter); +#endif + + DEBUGMSG("CNT: %d ID: 0x%08x MASK: 0x%08x\n", num, (uint32_t) (filter.id) & 0x1fffffff, (uint32_t) (~filter.mask) & 0x1fffffff); + + /* MSCAN uses oposite logic (compared to IP) for LAFM: 1-ignore bit, 0-use bit as mask */ + mask = (~filter.mask) << 1; + + /* RTR is LSB of mask */ + if (filter.flags & MSG_RTR) + mask |= 0x00000001; + + if (filter.flags & MSG_EXT) /* Extended ID */ + return mscan_extended_mask(chip, filter.id, mask); + else /* Standard ID */ + return mscan_standard_mask(chip, filter.id, mask); +} + + +/* ************************************************************************************************************************************* */ + + +void mscan_irq_read_handler(struct canchip_t *chip, struct msgobj_t *obj) +{ + /* check RX Flag */ + if (!mscan_get_flags(chip, MSCAN_RFLG_RXF, MSCAN_RFLG)) + { + obj->ret=-1; + DEBUGRX("Receive buffer empty\n"); + return; + } + + mscan_msg_from_rxbuffer(chip, &(obj->rx_msg)); + + /* fill CAN message timestamp */ + can_filltimestamp(&obj->rx_msg.timestamp); + + canque_filter_msg2edges(obj->qends, &obj->rx_msg); +} + +void mscan_irq_write_handler(struct canchip_t *chip, struct msgobj_t *obj) +{ + int cmd; + + DEBUGTX("Irq write handler\n"); + + 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; + } + + 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; + } +} + + +/* ************************************************************************************************************************************* */ + + +int mscan_register(struct chipspecops_t *chipspecops) +{ + chipspecops->chip_config = mscan_chip_config; + chipspecops->enable_configuration = mscan_enable_configuration; + chipspecops->disable_configuration = mscan_disable_configuration; + + chipspecops->baud_rate = mscan_baud_rate; + chipspecops->set_btregs = mscan_set_btregs; + + chipspecops->start_chip = mscan_start_chip; + chipspecops->stop_chip = mscan_stop_chip; + chipspecops->attach_to_chip = mscan_attach_to_chip; + chipspecops->release_chip = mscan_release_chip; + + chipspecops->standard_mask = mscan_standard_mask; + chipspecops->extended_mask = mscan_extended_mask; + chipspecops->message15_mask = NULL; /* mscan_message15_mask; */ + + chipspecops->pre_read_config = mscan_pre_read_config; + chipspecops->pre_write_config = mscan_pre_write_config; + chipspecops->send_msg = mscan_send_msg; + chipspecops->remote_request = mscan_remote_request; + + chipspecops->irq_handler = mscan_irq_handler; + chipspecops->irq_accept = NULL; /* mscan_irq_accept; */ + chipspecops->config_irqs = mscan_config_irqs; + + chipspecops->clear_objects = mscan_clear_objects; + chipspecops->check_tx_stat = mscan_check_tx_stat; + chipspecops->wakeup_tx = mscan_wakeup_tx; + chipspecops->filtch_rq = mscan_filtch_rq; + return 0; +} + +int mscan_fill_chipspecops(struct canchip_t *chip) +{ + chip->chip_type = "mscan"; + chip->max_objects = 1; + chip->write_register = chip->hostdevice->hwspecops->write_register; + chip->read_register = chip->hostdevice->hwspecops->read_register; + + /* + chip->flags; + chip->baudrate; + chip->msgobj; + chip->chip_data; + chip->chip_lock; + + chip->sja_cdr_reg; + chip->sja_ocr_reg; + chip->int_cpu_reg; + chip->int_clk_reg; + chip->int_bus_reg; + + #ifdef CAN_WITH_RTL + chip->worker_thread; + chip->pend_flags; + #endif + */ + + mscan_register(chip->chipspecops); + return 0; +} + + +/* ************************************************************************************************************************************* */ + + +int mscan_reset_chip(struct canchip_t * chip) +{ + DEBUGMSG("chip reset \n"); + + /* reset chip by turning MSCAN off and on in INIT mode */ + if (mscan_enable_configuration(chip)) + return -ENODEV; + + mscan_clear_flags(chip, MSCAN_CTL1_CANE, MSCAN_CTL1); + mscan_set_flags(chip, MSCAN_CTL1_CANE, MSCAN_CTL1); + + if (mscan_disable_configuration(chip)) + return -ENODEV; + + /* Abort all pending messages */ + mscan_abort_msg(chip, MSCAN_TARQ_ABTRQ); + + DEBUGMSG("chip reset DONE\n"); + + return 0; +} + +/* mscan_clear_irq_flags should be called only when not in INIT mode */ +int mscan_clear_irq_flags(struct canchip_t *chip) +{ + /* do not clear TXE flags -> it forces chip to send messages in buffers */ + reg_t rflg; + + if (mscan_init_mode_active(chip)) + { + DEBUGMSG("MSCAN: Clearing IRQs while INIT active \n"); + return -EBUSY; + } + + DEBUGMSG("Clearing IRQ flags...\n"); + + rflg = MSCAN_RFLG_WUPIF | MSCAN_RFLG_CSCIF | MSCAN_RFLG_RSTAT | + MSCAN_RFLG_TSTAT | MSCAN_RFLG_OVRIF | MSCAN_RFLG_RXF; + + /* reset flags by writing '1' */ + can_write_reg(chip, rflg, MSCAN_RFLG); + + return 0; +} + + +/* ************************************************************************************************************************************* */ + + +void mscan_notifyRXends(struct msgobj_t * obj, int what) +{ + struct canque_edge_t * edge; + canque_for_each_inedge(obj->qends, edge){ + canque_notify_outends(edge, what); + } +} + +/* Fill message content to one transmit buffers */ +/* Note: Check whether buffer is empy has to be done before calling this function */ +void mscan_setup_txbuffer(struct canchip_t * chip, struct canmsg_t * msg) +{ + volatile struct mscan_msg_buffer * txb = (struct mscan_msg_buffer *)(chip->chip_base_addr + MSCAN_TXFG); + reg_t rtr; + + + if (msg->flags & MSG_RTR) + rtr = 0x01; + else + rtr = 0x00; + + /* set Can mesage ID and local priority */ + if (msg->flags & MSG_EXT) + { + /* Extended ID */ + txb->id_0 = (reg_t)((msg->id >> 21) & 0x000000ff); + txb->id_1 = (reg_t)( + ((msg->id >> 18) & 0x000000e0) | + 0x00000018 | /* SRR & IDE */ + ((msg->id >> 15) & 0x00000003)); + txb->id_2 = (reg_t)((msg->id >> 7) & 0x000000ff); + txb->id_3 = (reg_t)(((msg->id << 1) & 0x000000fe) | rtr); + + /* local priority is 7 MSB bits followed by IDE flag */ + txb->local_prio = (reg_t)((txb->id_0 & 0xfe) | 0x01); /* IDE=1 */ + } + else + { + /* Standard ID */ + txb->id_0 = (reg_t)((msg->id >> 3) & 0x000000ff); + txb->id_1 = (reg_t)(((msg->id << 5) & 0x000000e0) | (rtr << 4)); /* IDE=0 */ + txb->id_2 = 0x00; + txb->id_3 = 0x00; + + /* local priority are 7 MSB bits folowed by IDE flag */ + txb->local_prio = (reg_t)(txb->id_0 & 0xfe); /* IDE=0 */ + } + + /* set data */ + switch (msg->length) + { + case 8: txb->data_7 = msg->data[7]; + case 7: txb->data_6 = msg->data[6]; + case 6: txb->data_5 = msg->data[5]; + case 5: txb->data_4 = msg->data[4]; + case 4: txb->data_3 = msg->data[3]; + case 3: txb->data_2 = msg->data[2]; + case 2: txb->data_1 = msg->data[1]; + case 1: txb->data_0 = msg->data[0]; + } + + /* data length */ + txb->data_len = (reg_t)(msg->length & 0x1f); + + DUMPBUFF(chip, MSCAN_TXFG); +} + +/* Fill message content from receive buffer */ +void mscan_msg_from_rxbuffer(struct canchip_t * chip, struct canmsg_t * msg) +{ + struct mscan_msg_buffer * rxb = (struct mscan_msg_buffer *)(chip->chip_base_addr + MSCAN_RXFG); + + /* retrieve Can mesage ID */ + msg->flags = 0; /* clear all */ + + /* check buffer IDE flag */ + if (mscan_get_flags(chip, (reg_t)0x08, MSCAN_RXFG + MSCAN_MSGBUFF_ID1)) /* 0x08 - IDE flag in ID1 */ + { + DEBUGMSG("Ext ID\n"); + /* Extended ID */ + msg->id = (((uint32_t)rxb->id_0 << 21) & 0x3fa00000) | + (((uint32_t)rxb->id_1 << 18) & 0x00380000) | + (((uint32_t)rxb->id_1 << 15) & 0x00070000) | + (((uint32_t)rxb->id_2 << 7 ) & 0x0000ff00) | + (((uint32_t)rxb->id_3 >> 1 ) & 0x000000ff); + + /* RTR flag */ + if (rxb->id_3 & 0x01) + msg->flags |= MSG_RTR; + + /* EXT flag */ + msg->flags |= MSG_EXT; + } + else + { + DEBUGMSG("std ID\n"); + /* Standard ID */ + msg->id = (((uint32_t)rxb->id_0 << 3) & 0x000007f8) | + (((uint32_t)rxb->id_1 >> 5) & 0x00000007); + + /* RTR flag */ + if (rxb->id_1 & 0x08) + msg->flags |= MSG_RTR; + + /* no EXT flag is set here */ + } + + /* retrieve data */ + switch (rxb->data_len) + { + case 8: msg->data[7] = rxb->data_7; + case 7: msg->data[6] = rxb->data_6; + case 6: msg->data[5] = rxb->data_5; + case 5: msg->data[4] = rxb->data_4; + case 4: msg->data[3] = rxb->data_3; + case 3: msg->data[2] = rxb->data_2; + case 2: msg->data[1] = rxb->data_1; + case 1: msg->data[0] = rxb->data_0; + } + + /* data length */ + msg->length = (reg_t)(rxb->data_len & 0x1f); +} +/* updates the TSEG1 and TSEG2 according to overall TSEG and Sample Point (0-1000) */ +static int mscan_update_samplept(int sampl_pt, int tseg, int *tseg1, int *tseg2) +{ + /* tseg = tseg1 + tseg2, its NOT tsegall ! + * difference between tseg and tsegall is: + * tsegall = tseg + 1 + */ + + *tseg2 = tseg + 1 - (sampl_pt * (tseg + 1)) / 1000; + if (*tseg2 < MSCAN_TSEG2_MIN) + *tseg2 = MSCAN_TSEG2_MIN; + if (*tseg2 > MSCAN_TSEG2_MAX) + *tseg2 = MSCAN_TSEG2_MAX; + *tseg1 = tseg - *tseg2; + if (*tseg1 > MSCAN_TSEG1_MAX) { + *tseg1 = MSCAN_TSEG1_MAX; + *tseg2 = tseg - *tseg1; + } + return 1000 * (*tseg1 + 1) / (tseg + 1); +} + + +static int mscan_init_mode_active(struct canchip_t *chip) +{ + /* Init request AND Init ACK */ + /* DEBUGMSG(" is Init active?\n"); */ + return mscan_get_flags(chip, MSCAN_CTL0_INITRQ, MSCAN_CTL0) && + mscan_get_flags(chip, MSCAN_CTL1_INITAK, MSCAN_CTL1); +} +static int mscan_sleep_mode_active(struct canchip_t *chip) +{ + /* Sleep Request AND Sleep Ack */ + return mscan_get_flags(chip, MSCAN_CTL0_SLPRQ, MSCAN_CTL0) && + mscan_get_flags(chip, MSCAN_CTL1_SLPAK, MSCAN_CTL1); +} + +static int mscan_enter_init_mode(struct canchip_t * chip) +{ + int i = MSCAN_MAX_SETTING_WAIT_LOOPS + 1; + + mscan_set_flags(chip, MSCAN_CTL0_INITRQ, MSCAN_CTL0); + + /* Waits until chip enters Init mode */ + while (!mscan_get_flags(chip, MSCAN_CTL1_INITAK, MSCAN_CTL1) && --i) + udelay(200); + + if (!i) + { + DEBUGMSG("Error entering Init mode (enable configuration) \n"); + return -ENODEV; + } + + return 0; +} +static int mscan_enter_sleep_mode(struct canchip_t * chip) +{ + int i = MSCAN_MAX_SETTING_WAIT_LOOPS + 1; + + if (mscan_sleep_mode_active(chip)) + { + DEBUGMSG("Already in sleep mode or not synced to Bus\n"); + return 0; + } + + /* DEBUGMSG("isSynced: 0x%02x\n",mscan_get_flags(chip, MSCAN_CTL0_SYNCH, MSCAN_CTL0)); */ + + mscan_set_flags(chip, MSCAN_CTL0_SLPRQ, MSCAN_CTL0); + + /* Waits until chip enters Sleep mode - finishes current TX/RX operation */ + while (!mscan_get_flags(chip, MSCAN_CTL1_SLPAK, MSCAN_CTL1) && --i) + udelay(200); + + if (!i) + { + DEBUGMSG("Error entering Sleep mode (enable configuration) \n"); + return -ENODEV; + } + + return 0; +} + + + +static reg_t mscan_get_flags(struct canchip_t * chip, reg_t flags, unsigned reg_addr) +{ + /* DEBUGMSG(" get flags [%u]\n", reg_addr); */ + return (reg_t)can_read_reg(chip, reg_addr) & flags; +} +static void mscan_set_flags(struct canchip_t * chip, reg_t flags, unsigned reg_addr) +{ + reg_t r = can_read_reg(chip, reg_addr); + can_write_reg(chip, r | flags, reg_addr); +} +static void mscan_clear_flags(struct canchip_t * chip, reg_t flags, unsigned reg_addr) +{ + reg_t r = can_read_reg(chip, reg_addr); + can_write_reg(chip, r & ~flags, reg_addr); +} +static uint16_t mscan_get_irq_flags(struct canchip_t * chip) +{ + /* Transmit Buffer Empty only if enabled */ + return ((mscan_get_flags(chip, MSCAN_TIER_TXE, MSCAN_TIER) & + mscan_get_flags(chip, MSCAN_TFLG_TXE, MSCAN_TFLG)) << 8) | + (mscan_get_flags(chip, (reg_t)(mscan_IRQs & 0xff), MSCAN_RFLG)); +} + + +static void mscan_clear_buffer(struct canchip_t * chip, unsigned start_addr) +{ + /* clear 25 registers of buffer, others are reset to 0-no need to clean them */ + unsigned addr; + for (addr = start_addr; addr < 26; addr++) + can_write_reg(chip, 0x00, addr); +} + +static int mscan_abort_msg(struct canchip_t * chip, reg_t buffer_mask) +{ + reg_t m; + int i = MSCAN_MAX_SETTING_WAIT_LOOPS + 1; + + /* check input */ + m = buffer_mask & MSCAN_TARQ_ABTRQ; + + /* It's not neccessary to abort empy message */ + m &= (~mscan_get_flags(chip, MSCAN_TFLG_TXE, MSCAN_TFLG)); + + if(!m) + { + DEBUGMSG("nothing to abort\n"); + goto abortmsg_exit_ok; + } + + + /* set TARQ - Transmitt Abort Request */ + mscan_set_flags(chip, m, MSCAN_TARQ); + + + /* Waits until chip acknowleges abortition */ + while (mscan_get_flags(chip, MSCAN_TAAK_ABTAK, MSCAN_TAAK) != m && --i) + udelay(200); + + if (!i) + { + DEBUGMSG("Error waiting for Message Abort [requested: 0x%02x]\n", m); + goto abortmsg_exit_fail; + } + + +abortmsg_exit_ok: + return 0; + +abortmsg_exit_fail: + return -ENODEV; +} +