📄 mcp2515funcs.c
字号:
/* 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 + -