📄 pcan_sja1000.c
字号:
//****************************************************************************
// Copyright (C) 2001,2002,2003 PEAK System-Technik GmbH
// part of this code is from Arnaud Westenberg
// email:arnaud@wanadoo.nl
//
// linux@peak-system.com
// www.peak-system.com
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)
//****************************************************************************
//****************************************************************************
//
// pcan_sja1000.c - all about sja1000 init and data handling
//
// $Log: pcan_sja1000.c,v $// Revision 1.26 2003/03/02 10:58:07 klaus// merged USB thread into main path//// Revision 1.25 2003/03/02 10:58:07 klaus// merged USB thread into main path//// Revision 1.24.2.5 2003/01/29 20:34:20 klaus// release_20030129_a and release_20030129_u released//// Revision 1.24.2.4 2003/01/29 20:34:20 klaus// release_20030129_a and release_20030129_u released//// Revision 1.24.2.3 2003/01/28 23:28:26 klaus// reorderd pcan_usb.c and pcan_usb_kernel.c, tidied up//// Revision 1.24.2.2 2003/01/20 21:39:26 klaus// timestamp problems solved, remove crashed solved, 1st usable release////****************************************************************************
//****************************************************************************
// INCLUDES
#include <src/pcan_common.h>
#include <linux/sched.h>
#include <asm/errno.h>
#include <asm/byteorder.h> // because of little / big endian
#include <linux/delay.h>
#include <src/pcan_main.h>
#include <pcan.h>
#include <src/pcan_fifo.h>
//****************************************************************************
// DEFINES
// sja1000 registers, only PELICAN mode - TUX like it
#define MODE 0 // mode register
#define COMMAND 1
#define CHIPSTATUS 2
#define INTERRUPT_STATUS 3
#define INTERRUPT_ENABLE 4 // acceptance code
#define TIMING0 6 // bus timing 0
#define TIMING1 7 // bus timing 1
#define OUTPUT_CONTROL 8 // output control
#define TESTREG 9
#define ARBIT_LOST_CAPTURE 11 // transmit buffer: Identifier
#define ERROR_CODE_CAPTURE 12 // RTR bit und data length code
#define ERROR_WARNING_LIMIT 13 // start byte of data field
#define RX_ERROR_COUNTER 14
#define TX_ERROR_COUNTER 15
#define ACCEPTANCE_CODE_BASE 16
#define RECEIVE_FRAME_BASE 16
#define TRANSMIT_FRAME_BASE 16
#define ACCEPTANCE_MASK_BASE 20
#define RECEIVE_MSG_COUNTER 29
#define RECEIVE_START_ADDRESS 30
#define CLKDIVIDER 31 // set bit rate and pelican mode
// important sja1000 register contents, MODE register
#define SLEEP_MODE 0x10
#define ACCEPT_FILTER_MODE 0x08
#define SELF_TEST_MODE 0x04
#define LISTEN_ONLY_MODE 0x02
#define RESET_MODE 0x01
#define NORMAL_MODE 0x00
// COMMAND register
#define CLEAR_DATA_OVERRUN 0x08
#define RELEASE_RECEIVE_BUFFER 0x04
#define ABORT_TRANSMISSION 0x02
#define TRANSMISSION_REQUEST 0x01
// CHIPSTATUS register
#define BUS_STATUS 0x80
#define ERROR_STATUS 0x40
#define TRANSMIT_STATUS 0x20
#define RECEIVE_STATUS 0x10
#define TRANS_COMPLETE_STATUS 0x08
#define TRANS_BUFFER_STATUS 0x04
#define DATA_OVERRUN_STATUS 0x02
#define RECEIVE_BUFFER_STATUS 0x01
// INTERRUPT STATUS register
#define BUS_ERROR_INTERRUPT 0x80
#define ARBIT_LOST_INTERRUPT 0x40
#define ERROR_PASSIV_INTERRUPT 0x20
#define WAKE_UP_INTERRUPT 0x10
#define DATA_OVERRUN_INTERRUPT 0x08
#define ERROR_WARN_INTERRUPT 0x04
#define TRANSMIT_INTERRUPT 0x02
#define RECEIVE_INTERRUPT 0x01
// INTERRUPT ENABLE register
#define BUS_ERROR_INTERRUPT_ENABLE 0x80
#define ARBIT_LOST_INTERRUPT_ENABLE 0x40
#define ERROR_PASSIV_INTERRUPT_ENABLE 0x20
#define WAKE_UP_INTERRUPT_ENABLE 0x10
#define DATA_OVERRUN_INTERRUPT_ENABLE 0x08
#define ERROR_WARN_INTERRUPT_ENABLE 0x04
#define TRANSMIT_INTERRUPT_ENABLE 0x02
#define RECEIVE_INTERRUPT_ENABLE 0x01
// OUTPUT CONTROL register
#define OUTPUT_CONTROL_TRANSISTOR_P1 0x80
#define OUTPUT_CONTROL_TRANSISTOR_N1 0x40
#define OUTPUT_CONTROL_POLARITY_1 0x20
#define OUTPUT_CONTROL_TRANSISTOR_P0 0x10
#define OUTPUT_CONTROL_TRANSISTOR_N0 0x08
#define OUTPUT_CONTROL_POLARITY_0 0x04
#define OUTPUT_CONTROL_MODE_1 0x02
#define OUTPUT_CONTROL_MODE_0 0x01
// TRANSMIT or RECEIVE BUFFER
#define BUFFER_EFF 0x80 // set for 29 bit identifier
#define BUFFER_RTR 0x40 // set for RTR request
#define BUFFER_DLC_MASK 0x0f
// CLKDIVIDER register
#define CAN_MODE 0x80
#define CAN_BYPASS 0x40
#define RXINT_OUTPUT_ENABLE 0x20
#define CLOCK_OFF 0x08
#define CLOCK_DIVIDER_MASK 0x07
// additional informations
#define CLOCK_HZ 16000000 // crystal frequency
// time for mode register to change mode
#define MODE_REGISTER_SWITCH_TIME 100 // msec
// some CLKDIVIDER register contents, hardware architecture dependend
#define PELICAN_SINGLE (CAN_MODE | CAN_BYPASS | 0x07 | CLOCK_OFF)
#define PELICAN_MASTER (CAN_MODE | CAN_BYPASS | 0x07 )
#define PELICAN_DEFAULT (CAN_MODE )
#define CHIP_RESET PELICAN_SINGLE
// hardware depended setup for OUTPUT_CONTROL register
#define OUTPUT_CONTROL_SETUP (OUTPUT_CONTROL_TRANSISTOR_P0 | OUTPUT_CONTROL_TRANSISTOR_N0 | OUTPUT_CONTROL_MODE_1)
// the interrupt enables
#define INTERRUPT_ENABLE_SETUP (RECEIVE_INTERRUPT_ENABLE | TRANSMIT_INTERRUPT_ENABLE | DATA_OVERRUN_INTERRUPT_ENABLE | BUS_ERROR_INTERRUPT_ENABLE | ERROR_PASSIV_INTERRUPT_ENABLE)
// the maximum number of handled messages in one interrupt
#define MAX_MESSAGES_PER_INTERRUPT 8
// the maximum number of handled sja1000 interrupts in 1 handler entry
#define MAX_INTERRUPTS_PER_ENTRY 4
// constants from Arnaud Westenberg email:arnaud@wanadoo.nl
#define MAX_TSEG1 15
#define MAX_TSEG2 7
#define BTR1_SAM (1<<1)
// a helper for fast conversion between SJA1000 data ordering and host data order
typedef union
{
u8 ucID[4];
u32 dwID;
} ID;
//****************************************************************************
// GLOBALS
//****************************************************************************
// LOCALS
//****************************************************************************
// CODE
//----------------------------------------------------------------------------
// switches the chip into reset mode
static int set_reset_mode(struct pcandev *dev)
{
u32 dwStart = get_mtime();
u8 tmp;
tmp = dev->readreg(dev, MODE);
while (!(tmp & RESET_MODE) && ((get_mtime() - dwStart) < MODE_REGISTER_SWITCH_TIME))
{
dev->writereg(dev, MODE, RESET_MODE); // force into reset mode
wmb();
schedule();
tmp = dev->readreg(dev, MODE);
}
if (!(tmp & RESET_MODE))
return -EIO;
else
return 0;
}
//----------------------------------------------------------------------------
// switches the chip back from reset mode
static int set_normal_mode(struct pcandev *dev, u8 ucModifier)
{
u32 dwStart = get_mtime();
u8 tmp;
tmp = dev->readreg(dev, MODE);
while ((tmp != ucModifier) && ((get_mtime() - dwStart) < MODE_REGISTER_SWITCH_TIME))
{
dev->writereg(dev, MODE, ucModifier); // force into normal mode
wmb();
schedule();
tmp = dev->readreg(dev, MODE);
}
if (tmp != ucModifier)
return -EIO;
else
return 0;
}
//----------------------------------------------------------------------------
// init CAN-chip
int sja1000_open(struct pcandev *dev, u16 btr0btr1, u8 bExtended, u8 bListenOnly)
{
int result = 0;
u8 clkdivider = PELICAN_DEFAULT;
u8 ucModifier = (bListenOnly) ? LISTEN_ONLY_MODE : NORMAL_MODE;
DPRINTK(KERN_DEBUG "%s: sja1000_open()\n", DEVICE_NAME);
// switch to reset
result = set_reset_mode(dev);
if (result)
goto fail;
// take a fresh status
dev->wCANStatus = 0;
// store extended mode (standard still accepted)
dev->bExtended = bExtended;
// configure clock divider register, switch into pelican mode, depended of of type
if (dev->wType == HW_PCI)
clkdivider = (dev->port.pci.ucMasterDevice == CHANNEL_MASTER) ? PELICAN_MASTER : PELICAN_SINGLE;
dev->writereg(dev, CLKDIVIDER, clkdivider);
// configure acceptance code registers
dev->writereg(dev, ACCEPTANCE_CODE_BASE, 0);
dev->writereg(dev, ACCEPTANCE_CODE_BASE + 1, 0);
dev->writereg(dev, ACCEPTANCE_CODE_BASE + 2, 0);
dev->writereg(dev, ACCEPTANCE_CODE_BASE + 3, 0);
// configure all acceptance mask registers to don't care
dev->writereg(dev, ACCEPTANCE_MASK_BASE, 0xff);
dev->writereg(dev, ACCEPTANCE_MASK_BASE + 1, 0xff);
dev->writereg(dev, ACCEPTANCE_MASK_BASE + 2, 0xff);
dev->writereg(dev, ACCEPTANCE_MASK_BASE + 3, 0xff);
// configure bus timing registers
dev->writereg(dev, TIMING0, (u8)((btr0btr1 >> 8) & 0xff));
dev->writereg(dev, TIMING1, (u8)((btr0btr1 ) & 0xff));
// configure output control registers
dev->writereg(dev, OUTPUT_CONTROL, OUTPUT_CONTROL_SETUP);
// clear any pending interrupt
dev->readreg(dev, INTERRUPT_STATUS);
// enter normal operating mode
result = set_normal_mode(dev, ucModifier);
if (result)
goto fail;
// enable CAN interrupts
dev->writereg(dev, INTERRUPT_ENABLE, INTERRUPT_ENABLE_SETUP);
fail:
return result;
}
//----------------------------------------------------------------------------
// release CAN-chip
void sja1000_release(struct pcandev *dev)
{
DPRINTK(KERN_DEBUG "%s: sja1000_release()\n", DEVICE_NAME);
// abort pending transmissions
dev->writereg(dev, COMMAND, ABORT_TRANSMISSION);
// disable CAN interrupts and set chip in reset mode
dev->writereg(dev, INTERRUPT_ENABLE, 0);
set_reset_mode(dev);
}
//----------------------------------------------------------------------------
// read CAN-data from chip, supposed a message is available
static int sja1000_read(struct pcandev *dev)
{
int i = MAX_MESSAGES_PER_INTERRUPT;
int result = 0;
TPCANRdMsg *m;
u8 rxfi; // receive frame information byte
u8 ucLen;
ID local;
int j;
// DPRINTK(KERN_DEBUG "%s: sja1000_read() %d\n", DEVICE_NAME, dev->readFifo.nStored);
do
{
// aquire a fifo element
result = pcan_fifo_claim_for_put(&dev->readFifo, (void *)&m);
if (result)
goto fail;
// fix time of reading
m->dwTime = get_mtime() - *dev->pdwInitTime;
rxfi = dev->readreg(dev, RECEIVE_FRAME_BASE);
// filter out extended messages in non extended mode
if (dev->bExtended || !(rxfi & BUFFER_EFF))
{
m->Msg.LEN = ucLen = rxfi & BUFFER_DLC_MASK;
if (ucLen > 8)
ucLen = 8;
j = 0;
if (rxfi & BUFFER_EFF)
{
m->Msg.MSGTYPE = MSGTYPE_EXTENDED;
#ifdef __LITTLE_ENDIAN
local.ucID[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
local.ucID[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
local.ucID[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 3);
local.ucID[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 4);
#else
local.ucID[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
local.ucID[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
local.ucID[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 3);
local.ucID[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 4);
#endif
local.dwID >>= 3;
while (ucLen--)
{
m->Msg.DATA[j] = dev->readreg(dev, RECEIVE_FRAME_BASE + 5 + j);
j++;
}
}
else
{
m->Msg.MSGTYPE = MSGTYPE_STANDARD;
local.dwID = 0;
#ifdef __LITTLE_ENDIAN
local.ucID[3] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
local.ucID[2] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
#else
local.ucID[0] = dev->readreg(dev, RECEIVE_FRAME_BASE + 1);
local.ucID[1] = dev->readreg(dev, RECEIVE_FRAME_BASE + 2);
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -