⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mcp2515funcs.c

📁 can4linux-3.5.3.gz can4 linux
💻 C
📖 第 1 页 / 共 3 页
字号:
/* mcp2515funcs * CAN bus driver for Microchip 251x CAN Controller with SPI Interface * * can4linux -- LINUX CAN device driver source *  * This file is subject to the terms and conditions of the GNU General Public * License.  See the file "COPYING" in the main directory of this archive * for more details. * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html * *  * Copyright (c) 2008 port GmbH Halle/Saale * (c) 2008 Heinz-Juergen Oertel (oe@port.de) * * Large parts are derived from code based on * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved. * Written under contract by: *   Chris Elston, Katalix Systems, Ltd. * which in turn is based on * Microchip MCP251x CAN controller driver written by * David Vrabel, Copyright 2006 Arcom Control Systems Ltd. **------------------------------------------------------------------* $Header: /z2/cvsroot/products/0530/software/can4linux/src/mcp2515funcs.c,v 1.1 2008/11/23 12:06:04 oe Exp $**--------------------------------------------------------------------------*** modification history* --------------------* $Log: mcp2515funcs.c,v $* Revision 1.1  2008/11/23 12:06:04  oe* - Update and released for 3.5.3** Revision 1.3  2008/10/07 07:55:35  oe* vorerst abschlus rx und tx, kuemmern um Bittiming***//* * Notes: * This driver interacts with the SPI subsystem. * - To the SPI subsystem it is a 'protocol driver' * * Because it is an SPI device, it's probing is handled via the SPI device * mechanisms (which are very similar to platform devices). * *//* * Platform file must specify something like: * *  	static struct mcp251x_platform_data mcp251x_info = { *		.oscillator_frequency = 19000000, *		.board_specific_setup = myboard_mcp251x_initfunc, *		.device_reset = myboard_mcp251x_reset, *		.transceiver_enable = NULL, *	}; * *	static struct spi_board_info spi_board_info[] __initdata = { *	{ *		.modalias	= "mcp251x", *		.platform_data	= &mcp251x_info, *		.irq		= 10, *		.max_speed_hz	= 8000000, *		.bus_num	= 1, *		.chip_select	= 0 *	}, *	}; * * (See Documentation/spi/spi-summary for more info) * * The first implementation was done for an ATMEL AT91SAm9263 board * see at arch/arm/mach-at91/board-sbc9263.c *///#define CANSPI_USEKTHREAD  1#include "defs.h"#include "linux/delay.h"#include <asm/arch/at91_pmc.h>#include "atmel_spi.h"#define CPUMCLK         96      /* 96 MHZ */#define SPICANCLK       10      /* max 10 MHZ */#undef SPIDEBUG#ifndef CANSPI_USEKTHREAD    #define up(x)           while (0) {};    #define down(x)         while (0) {};#endif/* bit timing table */u16 CanTiming[10][2]={	/*    CNF1    ,  CNF3 << 8 +CNF2 */	{ CAN_BCR1_10K,  CAN_BCR2_10K  },	{ CAN_BCR1_20K,  CAN_BCR2_20K  },	{ CAN_BCR1_50K,  CAN_BCR2_50K  },	{ CAN_BCR1_100K, CAN_BCR2_100K },	{ CAN_BCR1_125K, CAN_BCR2_125K },	{ CAN_BCR1_250K, CAN_BCR2_250K },	{ CAN_BCR1_500K, CAN_BCR2_500K },	{ CAN_BCR1_800K, CAN_BCR2_800K },	{ CAN_BCR1_1000K,CAN_BCR2_1000K} };struct spi_device *spidevice[MAX_CHANNELS]; int listenmode = 0;		/* true if listen only mode (mcp2515 only) *//* We need possibly a structure for the driverdata */struct mcp251x_priv realone;#define SPIIOMEM ((&realone)->spi_iomem)struct device_driver mcp251x_priv_drivername = { "canspi" };int at91_spihw_init(void){    struct mcp251x_priv *priv = &realone;    /* init for device printk */    (&priv->dev)->driver =  &mcp251x_priv_drivername;    strcpy((&priv->dev)->bus_id, "spihw0.1");    at91_set_gpio_input(AT91_PIN_PB24, 1);  /* input with pull-up */    at91_set_deglitch(AT91_PIN_PB24, 1);    /* deglitch is_on */    at91_set_B_periph(AT91_PIN_PA0, 0);     /* SPI0_MISO */    at91_set_B_periph(AT91_PIN_PA1, 0);     /* SPI0_MOSI */    at91_set_B_periph(AT91_PIN_PA2, 0);     /* SPI0_SPCK */    at91_set_gpio_output(AT91_PIN_PA3, 1);  /* SPI0_CS1 */    at91_set_gpio_output(AT91_PIN_PA5, 1);  /* SPI0_CS0 */    at91_sys_write(AT91_PMC_PCER, 1 << 14);    SPIIOMEM = ioremap(0xfffa4000, 0x4000);    if (!SPIIOMEM) {        dev_err(&priv->dev, "ioremap() for SPI mem failed.\n");        return ~0;    }    /* dev_info(&priv->dev, "ioremap: 0x%p\n", SPIIOMEM); */    spi_writel(SPIIOMEM, CR, SPI_BIT(SPIEN));    spi_writel(SPIIOMEM, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS));    spi_writel(SPIIOMEM, CSR0, SPI_BIT(NCPHA) |                               SPI_BIT(CSAAT) |                               SPI_BF(SCBR, CPUMCLK / SPICANCLK + 1)                               | SPI_BF(BITS, 0)); /* 8bit tx */    spi_writel(SPIIOMEM, CSR1, SPI_BIT(NCPHA) |                               SPI_BIT(CSAAT) |                               SPI_BF(SCBR, CPUMCLK / SPICANCLK + 1)                               | SPI_BF(BITS, 8)); /* 16bit tx */    return 0;}void at91_spihw_deinit(void){    if (SPIIOMEM) {        iounmap(SPIIOMEM);    }}static inline unsigned char at91_spihw_rw_data8(unsigned char data){#ifdef SPIDEBUG        int status;#endif#ifdef SPIDEBUG        do {            status = spi_readl(SPIIOMEM, SR);            printk("at91_spihw_rw_data8(): SPI_BIT(TDRE): 0x%08X\n", status);        } while (!(status & SPI_BIT(TDRE)));#else        while (!(spi_readl(SPIIOMEM, SR) & SPI_BIT(TDRE))) ;#endif        spi_writel(SPIIOMEM, TDR, data & 0xFF);#ifdef SPIDEBUG        do {            status = spi_readl(SPIIOMEM, SR);            printk("at91_spihw_rw_data8(): SPI_BIT(RDRF): 0x%08X\n", status);        } while (!(status & SPI_BIT(RDRF)));#else        while (!(spi_readl(SPIIOMEM, SR) & SPI_BIT(RDRF))) ;#endif        data = spi_readl(SPIIOMEM, RDR) & 0xff;        return data;}static inline void at91_spihw_rwn(		    unsigned char *out,		    unsigned char *in,		    unsigned char len		    ){        int i = 0;        at91_set_gpio_output(AT91_PIN_PA3, 0);        for (i = 0; i < len; i++) {            in[i] = at91_spihw_rw_data8(out[i]);        }        at91_set_gpio_output(AT91_PIN_PA3, 1);}static inline void at91_spihw_wr8(unsigned char out){        at91_set_gpio_output(AT91_PIN_PA3, 0);        at91_spihw_rw_data8(out);        at91_set_gpio_output(AT91_PIN_PA3, 1);}static u8 mcp251x_read_reg(uint8_t reg){	struct mcp251x_priv *priv = &realone;	u8 val = 0;	/* DBGin(); */	down(&priv->spi_lock);	priv->spi_tx_buf[0] = INSTRUCTION_READ;	priv->spi_tx_buf[1] = reg;        at91_spihw_rwn(priv->spi_tx_buf, priv->spi_rx_buf, 3);	val = priv->spi_rx_buf[2];	up(&priv->spi_lock);	/* DBGout(); */	return val;}static void mcp251x_write_reg(u8 reg, uint8_t val){	struct mcp251x_priv *priv = &realone;	down(&priv->spi_lock);	priv->spi_tx_buf[0] = INSTRUCTION_WRITE;	priv->spi_tx_buf[1] = reg;	priv->spi_tx_buf[2] = val;        at91_spihw_rwn(priv->spi_tx_buf, priv->spi_rx_buf, 3);	up(&priv->spi_lock);}static void mcp251x_write_bits(u8 reg, u8 mask, uint8_t val){        struct mcp251x_priv *priv =  &realone;	/* DBGin(); */	down(&priv->spi_lock);	priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY;	priv->spi_tx_buf[1] = reg;	priv->spi_tx_buf[2] = mask;	priv->spi_tx_buf[3] = val;        at91_spihw_rwn(priv->spi_tx_buf, priv->spi_rx_buf, 4);	up(&priv->spi_lock);	/* DBGout(); */}static void mcp251x_hw_reset(struct spi_device *spi){#ifdef CANSPI_USEKTHREAD        struct mcp251x_priv *priv = &realone;#endif        down(&priv->spi_lock);        at91_spihw_wr8(INSTRUCTION_RESET);        up(&priv->spi_lock);}#ifdef DEBUGint CAN_ShowStat (int board){    if (dbgMask && (dbgMask & DBG_DATA)) {    printk(KERN_INFO " %s: CTRL 0x%x,", __func__, CANin(board, CANCTRL));    printk(          " STAT 0x%x,",  CANin(board, CANSTAT));    printk(          " INTF 0x%x,",  CANin(board, CANINTF));    printk(          " INTE 0x%x\n", CANin(board, CANINTE));    printk(KERN_INFO "mask   %0x %0x %0x %0x\n",			CANin(board, RXM0SIDH),			CANin(board, RXM0SIDL),			CANin(board, RXM0EID8),			CANin(board, RXM0EID0));    printk(KERN_INFO "filter %0x %0x %0x %0x\n",			CANin(board, RXF0SIDH),			CANin(board, RXF0SIDL),			CANin(board, RXF0EID8),			CANin(board, RXF0EID0));    }    return 0;}#endif/* can_GetStat - read back as many status information as possible ** Because the CAN protocol itself describes different kind of information* already, and the driver has some generic information* (e.g about it's buffers)* we can define a more or less hardware independent format.*** exception:* ERROR WARNING LIMIT REGISTER (EWLR)* The SJA1000 defines a EWLR, reaching this Error Warning Level* an Error Warning interrupt can be generated.* The default value (after hardware reset) is 96. In reset* mode this register appears to the CPU as a read/write* memory. In operating mode it is read only.* Note, that a content change of the EWLR is only possible,* if the reset mode was entered previously. An error status* change (see status register; Table 14) and an error* warning interrupt forced by the new register content will not* occur until the reset mode is cancelled again.*/int can_GetStat(	struct inode *inode,	struct file *file,	CanStatusPar_t *stat	){unsigned int board = iminor(inode);	msg_fifo_t *Fifo;unsigned long flags;int rx_fifo = ((struct _instance_data *)(file->private_data))->rx_index;    stat->type = CAN_TYPE_MCP2515;    stat->baud = Baud[board];    /* printk(" STAT ST %d\n", CANin(board, canstat)); */    stat->status = CANin(board, CANSTAT);    /* printk(" STAT EWL %d\n", CANin(board, errorwarninglimit)); */    stat->error_warning_limit = 0;    stat->rx_errors = CANin(board, REC);    stat->tx_errors = CANin(board, TEC);    stat->error_code= 0; //CANin(board, errorcode);    /* Disable CAN (All !!) Interrupts */    /* !!!!!!!!!!!!!!!!!!!!! */    /* FIXME: better use spin lock ??? */    /* save_flags(flags); cli(); */    local_irq_save(flags);    Fifo = &Rx_Buf[board][rx_fifo];    stat->rx_buffer_size = MAX_BUFSIZE;	/**< size of rx buffer  */    /* number of messages */    stat->rx_buffer_used =    	(Fifo->head < Fifo->tail)    	? (MAX_BUFSIZE - Fifo->tail + Fifo->head) : (Fifo->head - Fifo->tail);    Fifo = &Tx_Buf[board];    stat->tx_buffer_size = MAX_BUFSIZE;	/* size of tx buffer  */    /* number of messages */    stat->tx_buffer_used =     	(Fifo->head < Fifo->tail)    	? (MAX_BUFSIZE - Fifo->tail + Fifo->head) : (Fifo->head - Fifo->tail);    /* Enable CAN Interrupts */    /* !!!!!!!!!!!!!!!!!!!!! */    /* restore_flags(flags); */    local_irq_restore(flags);    return 0;}int CAN_ChipReset(int minor){struct spi_device *spi = spidevice[minor];    DBGin();    /* Reset CAN controller */    mcp251x_hw_reset(spi);    udelay(100);    /* Put device into config mode */    mcp251x_write_reg(CANCTRL, CANCTRL_REQOP_CONF);    /* Wait for the device to enter config mode */    while ((mcp251x_read_reg(CANSTAT) & 0x80) == 0)	    udelay(10);    /* Set initial baudrate */    CAN_SetTiming(minor, Baud[minor]);    CAN_SetMask(minor, AccCode[minor], AccMask[minor] );    /* Enable RX0->RX1 buffer roll over */    /* RXM  Receive Buffer Operating Mode bitsi are 0            -  Receive all valid messages using either standard or            extended identifiers that meet filter criteria     */           mcp251x_write_bits(RXBCTRL(0), RXBCTRL_BUKT, RXBCTRL_BUKT);    mcp251x_write_reg(CANCTRL, CANCTRL_REQOP_NORMAL);    while (mcp251x_read_reg(CANSTAT) & 0xE0)	    udelay(10);    /* Reset error conditions */    mcp251x_write_reg(EFLG, 0);    DBGout();    return 0;}/* * Configures bit timing registers directly. Chip must be in bus off state. * MCP2515 specific *      btr0 == CNF1 *      btr1 == (CNF3 << 8) + CNF2  */int CAN_SetBTR (int minor, int btr0, int btr1){int isopen;    DBGin();    isopen = atomic_read(&Can_isopen[minor]);    if (isopen > 1)  {	DBGprint(DBG_DATA, ("refused to change bit rate, driver already in use"	"by another process"));	return -1;    }    DBGprint(DBG_DATA, ("[%d] btr0=%d, btr1=%d", minor, btr0, btr1));    mcp251x_write_reg(CNF1, (u8)btr0);    mcp251x_write_reg(CNF2, (u8)btr1);    mcp251x_write_reg(CNF3, (u8)(btr1 >> 8));    DBGout();    return 0;}/* * Configures bit timing. Chip must be in configuration state. */int CAN_SetTiming(int minor, int baud){struct mcp251x_priv *priv = &realone;int i      = 4;int custom = 0;int isopen;    DBGin();    isopen = atomic_read(&Can_isopen[minor]);    if ((isopen > 1) && (Baud[minor] != baud)) {	DBGprint(DBG_DATA, ("isopen = %d", isopen));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -