int nr_all_chips;
int nr_82527_chips;
int nr_sja1000_chips;
+ int nr_mcp2515_chips;
can_spinlock_t device_lock;
struct canchip_t *chip[MAX_HW_CHIPS];
void (*write_register)(unsigned data, can_ioptr_t address);
unsigned (*read_register)(can_ioptr_t address);
+ /* SPI / mcp2515 specific */
+ int (*spi_acquire_bus)(struct candevice_t *candev, short channel, int block);
+ void (*spi_release_bus)(struct candevice_t *candev, short channel);
+ int (*spi_transfer)(struct candevice_t *candev, void *tx, void *rx, uint16_t len);
+ short spi_channel;
+
+
void *chip_data;
unsigned short sja_cdr_reg; /* sja1000 only! */
int (*program_irq)(struct candevice_t *candev);
void (*write_register)(unsigned data, can_ioptr_t address);
unsigned (*read_register)(can_ioptr_t address);
+ int (*spi_acquire_bus)(struct candevice_t *candev, short channel, int block);
+ void (*spi_release_bus)(struct candevice_t *candev, short channel);
+ int (*spi_transfer)(struct candevice_t *candev, void *tx, void *rx, uint16_t len);
};
/**
* @stop_chip: stops chip message processing
* @irq_handler: interrupt service routine
* @irq_accept: optional fast irq accept routine responsible for blocking further interrupts
+ * @get_info: retrieve chp-specifc info for display in proc fs
*/
struct chipspecops_t {
int (*chip_config)(struct canchip_t *chip);
int (*stop_chip)(struct canchip_t *chip);
int (*irq_handler)(int irq, struct canchip_t *chip);
int (*irq_accept)(int irq, struct canchip_t *chip);
+ int (*reset_chip)(struct canchip_t *chip);
+ int (*get_info)(struct canchip_t *chip, char *buf);
};
struct mem_addr {
return chip->read_register(address_to_read);
}
+extern inline int can_spi_transfer(struct canchip_t *chip, void *tx, void *rx, uint16_t len)
+{
+ return chip->spi_transfer(chip->hostdevice, tx, rx, len);
+}
+
+extern inline int can_spi_acquire_bus(struct canchip_t *chip, int block)
+{
+ return chip->spi_acquire_bus(chip->hostdevice, chip->spi_channel, block);
+}
+
+extern inline void can_spi_release_bus(struct canchip_t *chip)
+{
+ chip->spi_release_bus(chip->hostdevice, chip->spi_channel);
+}
+
+
+
extern inline void canobj_write_reg(const struct canchip_t *chip, const struct msgobj_t *obj,
unsigned char data, unsigned reg_offs)
{
--- /dev/null
+#ifndef MCP2515_H
+#define MCP2515_H
+/* mcp2515.h
+ * Header file for the Linux CAN-bus driver.
+ * Written by Sergei Sharonov sharonov@halliburton.com
+ * sja1000p was used as a prototype
+ * This software is released under the GPL-License.
+ * Version lincan-0.3 Feb 2006
+ */
+
+// Fixup for old kernels - always defined for 2.6
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+
+int mcp2515_chip_config(struct canchip_t *chip);
+int mcp2515_extended_mask(struct canchip_t *chip, unsigned long code, unsigned long mask);
+int mcp2515_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags);
+int mcp2515_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj);
+int mcp2515_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+int mcp2515_send_msg(struct canchip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg);
+int mcp2515_fill_chipspecops(struct canchip_t *chip);
+int mcp2515_irq_handler(int irq, struct canchip_t *chip);
+
+int mcp2515_disp(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data);
+
+
+
+typedef struct {
+ uint8_t sidh;
+ uint8_t sidl;
+ uint8_t eid8;
+ uint8_t eid0;
+ uint8_t dlc;
+ uint8_t data[8];
+} __attribute__((packed)) MCP2515_FRAME;
+
+#define SPI_BUF_LEN 16 /* 13+2 bytes max transfer len, 1 spare */
+
+typedef struct {
+ uint32_t rx1ovr;
+ uint32_t rx0ovr;
+ uint32_t txbo;
+ uint32_t txep;
+ uint32_t rxep;
+ uint32_t txwar;
+ uint32_t rxwar;
+ uint32_t ewarn;
+ uint32_t merre;
+} MCP2515_ERRCNT;
+
+#define MCP2515_STATUS_SHUTDOWN (1)
+
+typedef struct {
+ struct canchip_t *chip;
+ uint8_t status;
+ uint8_t spi_buf[SPI_BUF_LEN];
+ struct work_struct workqueue_handler;
+ struct tasklet_struct tasklet_handler;
+ MCP2515_ERRCNT errcnt;
+ uint32_t wakeint_cnt;
+} MCP2515_PRIV;
+
+
+
+/* PeliCAN mode */
+enum MCP2515_regs {
+ /// TXBnCTRL - TRANSMIT BUFFER n CONTROL REGISTERS
+ MCP2515_TXB0CTRL = 0x30,
+ MCP2515_TXB1CTRL = 0x40,
+ MCP2515_TXB2CTRL = 0x50,
+
+ /// TXRTSCTRL - TXnRTS PIN CONTROL AND STATUS REGISTER
+ MCP2515_TXRTSCTRL = 0x0d,
+
+ /// TXBnSIDH - TRANSMIT BUFFER n STANDARD IDENTIFIER HIGH
+ MCP2515_TXB0SIDH = 0x31,
+ MCP2515_TXB1SIDH = 0x41,
+ MCP2515_TXB2SIDH = 0x51,
+
+ /// TXBnSIDL - TRANSMIT BUFFER n STANDARD IDENTIFIER LOW
+ MCP2515_TXB0SIDL = 0x32,
+ MCP2515_TXB1SIDL = 0x42,
+ MCP2515_TXB2SIDL = 0x52,
+
+ /// TXBnEID8 - TRANSMIT BUFFER n EXTENDED IDENTIFIER HIGH
+ MCP2515_TXB0EID8 = 0x33,
+ MCP2515_TXB1EID8 = 0x43,
+ MCP2515_TXB2EID8 = 0x53,
+
+ /// TXBnEID0 - TRANSMIT BUFFER n EXTENDED IDENTIFIER LOW
+ MCP2515_TXB0EID0 = 0x34,
+ MCP2515_TXB1EID0 = 0x44,
+ MCP2515_TXB2EID0 = 0x54,
+
+ /// TXBnDLC - TRANSMIT BUFFER n DATA LENGTH CODE
+ MCP2515_TXB0DLC = 0x35,
+ MCP2515_TXB1DLC = 0x45,
+ MCP2515_TXB2DLC = 0x55,
+
+ /// TXBnDm - TRANSMIT BUFFER n DATA
+ MCP2515_TXB0DATA = 0x36, /* 0x36-0x3d */
+ MCP2515_TXB1DATA = 0x46, /* 0x46-0x4d */
+ MCP2515_TXB2DATA = 0x56, /* 0x56-0x5d */
+
+ /// RXB0CTRL - RECEIVE BUFFER CONTROL
+ MCP2515_RXB0CTRL = 0x60,
+ MCP2515_RXB1CTRL = 0x70,
+
+ /// BFPCTRL - RXnBF PIN CONTROL AND STATUS
+ MCP2515_BFPCTRL = 0x0c,
+
+ /// RXBnSIDH - RECEIVE BUFFER n STANDARD IDENTIFIER HIGH
+ MCP2515_RXB0SIDH = 0x61,
+ MCP2515_RXB1SIDH = 0x71,
+
+ /// RXBnSIDL - RECEIVE BUFFER n STANDARD IDENTIFIER LOW
+ MCP2515_RXB0SIDL = 0x62,
+ MCP2515_RXB1SIDL = 0x72,
+
+ /// RXBnEID8 - RECEIVE BUFFER n EXTENDED IDENTIFIER HIGH
+ MCP2515_RXB0EID8 = 63,
+ MCP2515_RXB1EID8 = 73,
+
+ /// RXBnEID0 - RECEIVE BUFFER n EXTENDED IDENTIFIER LOW
+ MCP2515_RXB0EID0 = 64,
+ MCP2515_RXB1EID0 = 74,
+
+ /// RXBnDLC - RECEIVE BUFFER n DATA LENGHT CODE
+ MCP2515_RXB0DLC = 65,
+ MCP2515_RXB1DLC = 75,
+
+ /// RXBnDM - RECEIVE BUFFER n DATA
+ MCP2515_RXB0DATA = 0x66, /* 0x66-0x6d */
+ MCP2515_RXB1DATA = 0x76, /* 0x76-0x7d */
+
+ /// RXFnSIDH - FILTER n STANDARD IDENTIFIER HIGH
+ MCP2515_RXF0SIDH = 0x00,
+ MCP2515_RXF1SIDH = 0x04,
+ MCP2515_RXF2SIDH = 0x08,
+ MCP2515_RXF3SIDH = 0x10,
+ MCP2515_RXF4SIDH = 0x14,
+ MCP2515_RXF5SIDH = 0x18,
+
+ /// RXFnSIDL - FILTER n STANDARD IDENTIFIER LOW
+ MCP2515_RXF0SIDL = 0x01,
+ MCP2515_RXF1SIDL = 0x05,
+ MCP2515_RXF2SIDL = 0x09,
+ MCP2515_RXF3SIDL = 0x11,
+ MCP2515_RXF4SIDL = 0x15,
+ MCP2515_RXF5SIDL = 0x19,
+
+ /// RXFnEID8 - FILTER n EXTENDED IDENTIFIER HIGH
+ MCP2515_RXF0EID8 = 0x02,
+ MCP2515_RXF1EID8 = 0x06,
+ MCP2515_RXF2EID8 = 0x0a,
+ MCP2515_RXF3EID8 = 0x12,
+ MCP2515_RXF4EID8 = 0x16,
+ MCP2515_RXF5EID8 = 0x1a,
+
+ /// RXFnEID0 - FILTER n EXTENDED IDENTIFIER LOW
+ MCP2515_RXF0EID0 = 0x03,
+ MCP2515_RXF1EID0 = 0x07,
+ MCP2515_RXF2EID0 = 0x0b,
+ MCP2515_RXF3EID0 = 0x13,
+ MCP2515_RXF4EID0 = 0x17,
+ MCP2515_RXF5EID0 = 0x1b,
+
+ /// RXMnSIDH - MASK n STANDARD IDENTIFIER HIGH
+ MCP2515_RXM0SIDH = 0x20,
+ MCP2515_RXM1SIDH = 0x24,
+
+ /// RXMnSIDL - MASK n STANDARD IDENTIFIER LOW
+ MCP2515_RXM0SIDL = 0x21,
+ MCP2515_RXM1SIDL = 0x25,
+
+ /// RXMnEID8 - MASK n EXTENDED IDENTIFIER HIGH
+ MCP2515_RXM0EID8 = 0x22,
+ MCP2515_RXM1EID8 = 0x26,
+
+ /// RXMnEID0 - MASK n EXTENDED IDENTIFIER LOW
+ MCP2515_RXM0EID0 = 0x23,
+ MCP2515_RXM1EID0 = 0x27,
+
+ /// CNFn - CONFIGURATION REGS
+ MCP2515_CNF1 = 0x2a,
+ MCP2515_CNF2 = 0x29,
+ MCP2515_CNF3 = 0x28,
+
+ /// TEC - TRANSMIT ERROR COUNTER
+ MCP2515_TEC = 0x1c,
+
+ /// REC - RECEIVER ERROR COUNTER
+ MCP2515_REC = 0x1d,
+
+ /// EFLG - ERROR FLAG
+ MCP2515_EFLG = 0x2d,
+
+ /// CANINTE - INTERRUPT ENABLE
+ MCP2515_CANINTE = 0x2b,
+
+ /// CANINTF - INTERRUPT FLAG
+ MCP2515_CANINTF = 0x2c,
+
+ /// CANCTRL - CAN CONTROL REGISTER
+ MCP2515_CANCTRL = 0x0f,
+
+ /// CANSTAT - CAN STATUS REGISTER
+ MCP2515_CANSTAT = 0x0e
+};
+
+
+/** Mode Register Bits */
+enum mcp2515_MOD {
+ mcpMOD_NORM = 0<<5, // Normal Operation Mode
+ mcpMOD_SLEEP = 1<<5, // Sleep Mode
+ mcpMOD_LOOPBACK = 2<<5, // Loopback Mode
+ mcpMOD_LISTEN = 3<<5, // Listen-only Mode
+ mcpMOD_CONFIG = 4<<5, // Configuration Mode
+ mcpMOD_MASK = 7<<5 // Mask for mod bits
+};
+
+/* BFPCTRL - RXnBF PIN CONTROL AND STATUS */
+enum mcp2515_RXBF {
+ mcpB0BFE = 1<<2,
+ mcpB1BFE = 1<<3,
+ mcpB0BFS = 1<<4,
+ mcpB1BFS = 1<<5
+};
+
+/* RXBnSIDL bits */
+enum mcp2515_RXBSIDL {
+ mcpIDE = 1 << 3,
+ mcpSRR = 1 << 4
+};
+
+/* RXBnDLC bits */
+enum mcp2515_RXBDLC {
+ mcpDLC_MASK = 0x0f,
+ mcpRTR = 1 << 6
+};
+
+/* RXB0CTRL */
+enum mcp2515_RXB0CTRL {
+ mcpFILHIT0 = 1 << 0,
+ mcpBUKT = 1 << 2,
+ mcpRXRTR = 1 << 3,
+ mcpRXM_MASK = 3 << 5
+};
+
+enum mcp2515_ICOD {
+ mcpICOD_RXB0 = 7,
+ mcpICOD_RXB1 = 6,
+ mcpICOD_TXB2 = 5,
+ mcpICOD_TXB1 = 4,
+ mcpICOD_TXB0 = 3,
+ mcpICOD_WAKE = 2,
+ mcpICOD_ERR = 1
+};
+
+enum mcp2515_TXBCTRL {
+ mcpTXREQ = 1 << 3,
+ mcpTXERR = 1 << 4,
+ mcpMLOA = 1 << 5,
+ mcpABTF = 1 << 6
+};
+
+enum mcp2515_TXBSIDL {
+ mcpEXIDE = 1 << 3,
+ mcpEID_MASK = 0x3
+};
+
+enum mcp2515_INT {
+ mcpRX0INT = 1 << 0,
+ mcpRX1INT = 1 << 1,
+ mcpTX0INT = 1 << 2,
+ mcpTX1INT = 1 << 3,
+ mcpTX2INT = 1 << 4,
+ mcpERRINT = 1 << 5,
+ mcpWAKINT = 1 << 6,
+ mcpMERREINT = 1 << 7
+};
+
+/* Error flags EFLG */
+enum mcp2515_EFLG {
+ mcpEWARN = 1 << 0,
+ mcpRXWAR = 1 << 1,
+ mcpTXWAR = 1 << 2,
+ mcpRXEP = 1 << 3,
+ mcpTXEP = 1 << 4,
+ mcpTXBO = 1 << 5,
+ mcpRX0OVR = 1 << 6,
+ mcpRX1OVR = 1 << 7
+};
+
+#endif
--- /dev/null
+/* spican.h
+ * Header file for the Linux CAN-bus driver - LinCAN.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * Adapted for spican by Jan Kriz
+ * email:devel@jojen.net
+ * This software is released under the GPL-License.
+ * Version lincan-0.3.4-r1 20 Aug 2010
+ */
+
+#define OMAP2_SPICAN_NCHIPS 1 // Since the device is initiated from SPI, it's applies to 1 chip (and 1 irq) at a time
+
+int spican_request_io(struct candevice_t *candev);
+int spican_release_io(struct candevice_t *candev);
+int spican_reset(struct candevice_t *candev);
+int spican_init_hw_data(struct candevice_t *candev);
+int spican_init_chip_data(struct candevice_t *candev, int chipnr);
+int spican_init_obj_data(struct canchip_t *chip, int objnr);
+void spican_write_register(unsigned data, can_ioptr_t address);
+unsigned spican_read_register(can_ioptr_t address);
+int spican_program_irq(struct candevice_t *candev);
+
+#define OMAP2_SPICAN_TRIG_IRQ (1<<0)
+#define OMAP2_SPICAN_TRIG_GPT (1<<1)
+#define OMAP2_SPICAN_TRIG_SYS (1<<2)
+
+#define OMAP2_SPICAN_MCP_CLK (20000000)
+#define OMAP2_SPICAN_BAUDRATE (1000000)
+
+#define OMAP2_SPICAN_CS_CHANGE (1)
+#define OMAP2_SPICAN_DELAY_USECS (0)
+#define OMAP2_SPICAN_SPEED_HZ (10000000)
+
+struct omap2_spican_platform_data {
+ uint32_t mcp2515_clk; /* Clock frequency of MCP2515 */
+ uint32_t baudrate; /* Requested can communication speed */
+
+ unsigned cs_change; /* Cycle ~CS between transmissions? */
+ u16 delay_usecs; /* Time gap between transmissions */
+ u32 speed_hz; /* Speed of SPI communication */
+
+ const char *chiptype;
+
+ /* Following options are set at runtime */
+ struct spi_device *spi; /* SPI device */
+ u8 trigger;
+ u32 irq;
+#ifdef CONFIG_OMAP_DM_TIMER
+ struct omap_dm_timer *timer_ptr; // timer object
+ int32_t timer_irq; // the IRQ # for our gp timer
+#endif /* CONFIG_OMAP_DM_TIMER */
+ spinlock_t spi_lock;
+};
+
+int omap2_spican_init(void);
+void omap2_spican_exit(void);
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
+ kv_pcican msmcan oscar adlink7841 pcan_pci esdpci200 unican usbcan omap2_spican virtual template
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
default_CONFIG += CONFIG_OC_LINCAN_DETAILED_ERRORS=y
+default_CONFIG += CONFIG_OC_LINCAN_OMAP_DM_TIMER=n
default_CONFIG += $(foreach n, $(lincan_cards_NAMES), CONFIG_OC_LINCAN_CARD_$(n)=y)
default_CONFIG += $(foreach n, $(lincan_morecards_NAMES), CONFIG_OC_LINCAN_CARD_$(n)=n)
-
lincan_cards_SELECTED = $(filter %.y, $(foreach x, $(lincan_cards_NAMES) $(lincan_morecards_NAMES), $(x).$(CONFIG_OC_LINCAN_CARD_$(x))))
lincan_cards_SELECTED := $(lincan_cards_SELECTED:%.y=%)
lincan_SOURCES = can_queue.c can_quekern.c main.c modparms.c \
devcommon.c setup.c finish.c irq.c sysdep_lnx.c boardlist.c \
- sja1000p.c sja1000.c i82527.c hcan2.c \
+ sja1000p.c sja1000.c i82527.c hcan2.c mcp2515.c \
open.c close.c read.c write.c ioctl.c select.c fasync.c \
proc.c ioctl_query.c ioctl_remote.c \
$(lincan_cards_SOURCES) $(lincan_rtl_SOURCES)
extern int hms30c7202_register(struct hwspecops_t *hwspecops);
extern int nsi_canpci_register(struct hwspecops_t *hwspecops);
extern int usbcan_register(struct hwspecops_t *hwspecops);
+extern int omap2_spican_register(struct hwspecops_t *hwspecops);
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);
#if defined(CONFIG_OC_LINCAN_CARD_usbcan)
{"usbcan", usbcan_register, 0},
#endif
+ #if defined(CONFIG_OC_LINCAN_CARD_omap2_spican)
+ {"omap2_spican", omap2_spican_register, 0},
+ #endif
#if defined(CONFIG_OC_LINCAN_CARD_mpc5200)
{"mpc5200", mpc5200_register, 0},
{"mpc5200_midam", mpc5200_midam_register, 0},
}
can_checked_free(chip->chipspecops);
+ can_checked_free(chip->chip_data);
+ chip->chip_data = NULL;
chip->chipspecops=NULL;
}
#if defined(CONFIG_OC_LINCAN_CARD_usbcan)
#include "../include/usbcan.h"
#endif
+#if defined(CONFIG_OC_LINCAN_CARD_omap2_spican)
+ #include "../include/omap2_spican.h"
+#endif
can_spinlock_t canuser_manipulation_lock;
goto memory_error;
}
#endif
+#if defined(CONFIG_OC_LINCAN_CARD_omap2_spican)
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
+ res = omap2_spican_init();
+ if (res){
+ CANMSG("omap2_spican_register for omap2_spican failed. Error number %d.\n", res);
+ goto memory_error;
+ }
+ #else
+ #error There's no support for OMAP2 SPI subsystem in kernel versions before 2.6.23
+ #endif
+#endif
return 0;
+
struct candevice_t* register_hotplug_dev(const char *hwname,int (*chipdataregfnc)(struct canchip_t *ch,void *data),void *devdata){
int i=0, j, board=0;
struct candevice_t *candev;
DEBUGMSG("Unregistering usbcan.\n");
usbcan_exit();
#endif
-
+#if defined(CONFIG_OC_LINCAN_CARD_omap2_spican)
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
+ DEBUGMSG("Unregistering omap2_spican.\n");
+ omap2_spican_exit();
+ #endif
+#endif
DEBUGMSG("Continuing with coldplug cleanup.\n");
#ifdef CONFIG_PROC_FS
if (can_delete_procdir())
#endif /*CAN_WITH_RTL*/
DEBUGMSG("Can hardware cleanup done, freeing memory.\n");
-
+
if ( can_del_mem_list() )
CANMSG("Error deallocating memory\n");
--- /dev/null
+/* mcp2515.c
+ * Linux CAN-bus device driver.
+ * Written by Sergei Sharonov email:sharonov@halliburton.com
+ * sja1000p was used as a prototype
+ * This software is released under the GPL-License.
+ * Version lincan-0.3.2 15 Feb 2006
+ */
+#include "../include/can.h"
+#include "../include/can_sysdep.h"
+#include "../include/main.h"
+#include "../include/mcp2515.h"
+
+#define DEBUG 0
+
+#define RESET_CMD 0xc0
+#define READ_CMD 0x03
+#define WRITE_CMD 0x02
+#define BITMOD_CMD 0x05
+
+
+/*****************************************************************************/
+/* SPI ACCESS FUNCTIONS */
+/*****************************************************************************/
+static unsigned char read_reg(struct canchip_t *chip, unsigned addr)
+{
+ uint8_t *command, val;
+
+ command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+ command[0] = READ_CMD;
+ command[1] = addr;
+ can_spi_transfer(chip,command,command,3);
+ val = command[2];
+
+#if DEBUG
+ DEBUGMSG("reg[0x%02x]=>0x%02x\n",addr,(unsigned)val);
+#endif
+ return val;
+}
+
+/*****************************************************************************/
+static int read_block(struct canchip_t *chip, unsigned startAddr,
+ size_t len, void *data)
+{
+ uint8_t *command;
+
+ command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+ memset(command,0,SPI_BUF_LEN);
+ command[0] = READ_CMD;
+ command[1] = startAddr;
+ can_spi_transfer(chip, command, command, len+2);
+ memcpy(data, command+2, len);
+
+#if DEBUG
+ {
+ int i;
+ DEBUGMSG("reg[0x%02x..]=>",startAddr);
+ for(i=0;i<len;i++) DEBUGMSG(" 0x%02x",(unsigned)((uint8_t*)data)[i]);
+ DEBUGMSG("\n");
+ }
+#endif
+ return len;
+}
+
+/*****************************************************************************/
+static void write_reg(struct canchip_t *chip, unsigned addr, unsigned char data)
+{
+ unsigned char *command;
+
+ command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+ command[0] = WRITE_CMD;
+ command[1] = addr;
+ command[2] = data;
+ can_spi_transfer(chip,command,command,3);
+
+#if DEBUG
+ DEBUGMSG("reg[0x%02x]<=0x%02x\n",addr,(unsigned)data);
+#endif
+}
+
+
+/*****************************************************************************/
+static int write_block(struct canchip_t *chip, unsigned startAddr,
+ size_t len, void *data)
+{
+ uint8_t *command;
+
+ command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+ command[0] = WRITE_CMD;
+ command[1] = startAddr;
+ memcpy(command+2,data,len);
+ can_spi_transfer(chip, command, command, len+2);
+
+#if DEBUG
+ {
+ int i;
+ DEBUGMSG("reg[0x%02x..]<=",startAddr);
+ for(i=0;i<len;i++)
+ DEBUGMSG(" 0x%02x", (unsigned)((uint8_t*)data)[i]);
+ DEBUGMSG("\n");
+ }
+#endif
+
+ return len;
+}
+
+/*****************************************************************************/
+static void bitmod_reg(struct canchip_t *chip, unsigned addr,
+ unsigned char mask, unsigned char data)
+{
+ unsigned char *command;
+
+ command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+ command[0] = BITMOD_CMD;
+ command[1] = addr;
+ command[2] = mask;
+ command[3] = data;
+ can_spi_transfer(chip,command,command,4);
+
+#if DEBUG
+ DEBUGMSG("reg[0x%02x]<=0x%02x mask=0x%02x\n",addr,(unsigned)data,(unsigned)mask);
+#endif
+}
+
+
+/*****************************************************************************/
+/* PROC INTERFACE */
+/*****************************************************************************/
+int mcp2515_get_info(struct canchip_t *chip, char *buf)
+{
+ MCP2515_PRIV *priv=(MCP2515_PRIV *)(chip->chip_data);
+ int len=0;
+ uint8_t opmode;
+
+ can_spi_acquire_bus(chip, 1);
+ opmode = read_reg(chip,MCP2515_CANSTAT) & mcpMOD_MASK;
+ can_spi_release_bus(chip);
+ len += sprintf(buf+len,"opmode : %s%s%s%s%s\n",
+ (opmode == mcpMOD_NORM) ? "norm" : "",
+ (opmode == mcpMOD_SLEEP) ? "sleep" : "",
+ (opmode == mcpMOD_LOOPBACK) ? "loopback" : "",
+ (opmode == mcpMOD_LISTEN) ? "listen" : "",
+ (opmode == mcpMOD_CONFIG) ? "config" : "");
+
+ len += sprintf(buf+len,"spi ch : %d\n",chip->spi_channel);
+ len += sprintf(buf+len,"rx0ovr : %u\n",priv->errcnt.rx0ovr);
+ len += sprintf(buf+len,"rx1ovr : %u\n",priv->errcnt.rx1ovr);
+ len += sprintf(buf+len,"txbo : %u\n",priv->errcnt.txbo);
+ len += sprintf(buf+len,"txep : %u\n",priv->errcnt.txep);
+ len += sprintf(buf+len,"rxep : %u\n",priv->errcnt.rxep);
+ len += sprintf(buf+len,"txwar : %u\n",priv->errcnt.txwar);
+ len += sprintf(buf+len,"rxwar : %u\n",priv->errcnt.rxwar);
+ len += sprintf(buf+len,"ewarn : %u\n",priv->errcnt.ewarn);
+ len += sprintf(buf+len,"merre : %u\n",priv->errcnt.merre);
+ len += sprintf(buf+len,"wakeup : %u\n",priv->wakeint_cnt);
+
+ return len;
+}
+
+
+/*****************************************************************************/
+/* IRQ FUNCTIONS */
+/*****************************************************************************/
+static void rx_handler(struct canchip_t *chip, int bufNo, struct msgobj_t *obj)
+{
+ int len;
+ MCP2515_FRAME frame;
+
+ if(chip == NULL) panic("rx_handler: chip==NULL");
+ if(obj == NULL) panic("rx_handler: obj==NULL");
+
+
+ /* get all frame data */
+ if(bufNo == 0){
+ read_block(chip,MCP2515_RXB0SIDH,sizeof(MCP2515_FRAME),&frame);
+ bitmod_reg(chip,MCP2515_CANINTF, mcpRX0INT, ~mcpRX0INT);
+ }
+ else{
+ read_block(chip,MCP2515_RXB1SIDH,sizeof(MCP2515_FRAME),&frame);
+ bitmod_reg(chip,MCP2515_CANINTF, mcpRX1INT, ~mcpRX1INT);
+ }
+
+ if(frame.sidl & mcpIDE) {
+ DEBUGMSG("extended frame\n");
+ obj->rx_msg.id =
+ ((uint32_t)frame.eid0) |
+ ((uint32_t)frame.eid8) << 8 |
+ ((uint32_t)frame.sidl & 0x03) << 16 |
+ ((uint32_t)frame.sidl & 0xe0) << 13 |
+ ((uint32_t)frame.sidh) << 21;
+ obj->rx_msg.flags = MSG_EXT | ((frame.dlc & mcpRTR) ? MSG_RTR : 0);
+ }
+ else {
+ DEBUGMSG("standard frame\n");
+ obj->rx_msg.id =
+ ((uint32_t)frame.sidl) >> 5 |
+ ((uint32_t)frame.sidh) << 3;
+ obj->rx_msg.flags = ((frame.sidl & mcpSRR) ? MSG_RTR : 0);
+ }
+
+ len = frame.dlc & mcpDLC_MASK;
+ if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
+ obj->rx_msg.length = len;
+
+ memcpy(obj->rx_msg.data,frame.data,len);
+
+ /* fill CAN message timestamp */
+ can_filltimestamp(&obj->rx_msg.timestamp);
+ canque_filter_msg2edges(obj->qends, &obj->rx_msg);
+}
+
+/*****************************************************************************/
+static void tx_handler(struct canchip_t *chip, struct msgobj_t *obj)
+{
+ int cmd;
+
+ if(chip == NULL) panic("tx_handler: chip==NULL");
+ if(obj == NULL) panic("tx_handler: obj==NULL");
+
+ bitmod_reg(chip,MCP2515_CANINTF, mcpTX0INT, ~mcpTX0INT);
+
+
+ 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;
+ }
+
+ can_msgobj_clear_fl(obj,TX_PENDING);
+ cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
+ if(cmd<0)
+ return;
+ can_msgobj_set_fl(obj,TX_PENDING);
+
+ 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;
+ }
+
+}
+
+/*****************************************************************************/
+static void errint_handler(struct canchip_t *chip)
+{
+ uint8_t error;
+ MCP2515_PRIV *priv=(MCP2515_PRIV *)(chip->chip_data);
+
+ error = read_reg(chip, MCP2515_EFLG);
+ bitmod_reg(chip,MCP2515_CANINTF, mcpERRINT, ~mcpERRINT);
+
+
+ if(error & mcpRX0OVR) {
+ (priv->errcnt.rx0ovr)++;
+ CANMSG("can: RX0OVR\n");
+ }
+ if(error & mcpRX1OVR) (priv->errcnt.rx1ovr)++;
+ if(error & mcpTXBO) (priv->errcnt.txbo)++;
+ if(error & mcpTXEP) (priv->errcnt.txep)++;
+ if(error & mcpRXEP) (priv->errcnt.rxep)++;
+ if(error & mcpTXWAR) (priv->errcnt.txwar)++;
+ if(error & mcpRXWAR) (priv->errcnt.rxwar)++;
+ if(error & mcpEWARN) (priv->errcnt.ewarn)++;
+}
+
+/*****************************************************************************/
+static void irq_work(void *data)
+{
+ struct canchip_t *chip=(struct canchip_t *)data;
+ MCP2515_PRIV *priv;
+ uint8_t flags;
+
+ if (chip == NULL)
+ return;
+ priv=(MCP2515_PRIV *)(chip->chip_data);
+ if (priv == NULL)
+ return;
+ while(1) {
+
+ flags = read_reg(chip, MCP2515_CANINTF);
+ if(flags == 0) break;
+ DEBUGMSG("mcp251x_irq_work_handler:%s%s%s%s%s%s%s%s\n",
+ (flags & mcpRX0INT) ? " RX0":"",
+ (flags & mcpRX1INT) ? " RX1":"",
+ (flags & mcpTX0INT) ? " TX0":"",
+ (flags & mcpTX1INT) ? " TX1":"",
+ (flags & mcpTX2INT) ? " TX2":"",
+ (flags & mcpERRINT) ? " ERR":"",
+ (flags & mcpWAKINT) ? " WAK":"",
+ (flags & mcpMERREINT) ? " MERRE":"");
+
+ if(flags & mcpRX0INT) rx_handler(chip,0,chip->msgobj[0]);
+ if(flags & mcpRX1INT) rx_handler(chip,1,chip->msgobj[0]);
+ if(flags & mcpTX0INT) tx_handler(chip,chip->msgobj[0]);
+ if(flags & mcpTX1INT) tx_handler(chip,chip->msgobj[1]);
+ if(flags & mcpTX2INT) tx_handler(chip,chip->msgobj[2]);
+ if(flags & mcpERRINT) errint_handler(chip);
+
+ if(flags & mcpMERREINT){
+ bitmod_reg(chip,MCP2515_CANINTF, mcpMERREINT, ~mcpMERREINT);
+ (priv->errcnt.merre)++;
+ }
+ if(flags & mcpWAKINT) (priv->wakeint_cnt)++;
+
+// bitmod_reg(chip, MCP2515_CANINTF, flags, 0);
+ }
+
+ enable_irq(chip->chip_irq);
+}
+
+/*****************************************************************************/
+
+static void tasklet_handler(unsigned long data)
+{
+ struct canchip_t *chip=(struct canchip_t *)data;
+ if (chip == NULL)
+ return;
+
+ /* Already acquired bus, go do work */
+ irq_work(chip);
+ can_spi_release_bus(chip);
+}
+
+/*****************************************************************************/
+static void workqueue_handler(struct work_struct *work)
+{
+ MCP2515_PRIV *priv = container_of(work,MCP2515_PRIV,workqueue_handler);
+ struct canchip_t *chip = priv->chip;
+ if (chip == NULL)
+ return;
+ can_spi_acquire_bus(chip, 1);
+ irq_work(chip);
+ can_spi_release_bus(chip);
+}
+
+/*****************************************************************************/
+/* EXPORT FUNCTIONS */
+/*****************************************************************************/
+int mcp2515_reset_chip(struct canchip_t *chip)
+{
+ unsigned char *command;
+
+ DEBUGMSG("reset chip\n");
+
+ command = ((MCP2515_PRIV*)(chip->chip_data))->spi_buf;
+ command[0] = RESET_CMD;
+ can_spi_acquire_bus(chip,1);
+ can_spi_transfer(chip,command,command,1);
+ can_spi_release_bus(chip);
+
+#if DEBUG
+ DEBUGMSG("reset mcp2515:%d\n",chip->chip_idx);
+#endif
+ return 0;
+}
+
+/**
+ * mcp2515_enable_configuration - enable chip configuration mode
+ * @chip: pointer to chip state structure
+ */
+int mcp2515_enable_configuration(struct canchip_t *chip)
+{
+ int i;
+ enum mcp2515_MOD stat;
+
+ DEBUGMSG("mcp2515_enable_configuration\n");
+
+ can_disable_irq(chip->chip_irq);
+
+ can_spi_acquire_bus(chip,1);
+ for(i=0;i<10;i++) {
+ bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_CONFIG);
+ stat = read_reg(chip,MCP2515_CANSTAT);
+ if((stat & mcpMOD_MASK) == mcpMOD_CONFIG) {
+ can_spi_release_bus(chip);
+ return 0;
+ }
+ udelay(100);
+ }
+ can_spi_release_bus(chip);
+
+ CANMSG("Failed to set cfg mode\n");
+ can_enable_irq(chip->chip_irq);
+ return -ENODEV;
+}
+
+/**
+ * mcp2515_disable_configuration - disable chip configuration mode
+ * @chip: pointer to chip state structure
+ */
+int mcp2515_disable_configuration(struct canchip_t *chip)
+{
+ int i;
+ enum mcp2515_MOD stat;
+
+ DEBUGMSG("mcp2515_disable_configuration\n");
+
+ can_spi_acquire_bus(chip,1);
+ for(i=0;i<10;i++) {
+ bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_NORM);
+ stat = read_reg(chip,MCP2515_CANSTAT);
+ if((stat & mcpMOD_MASK) == mcpMOD_NORM) {
+ can_enable_irq(chip->chip_irq);
+ can_spi_release_bus(chip);
+ return 0;
+ }
+ udelay(100);
+ }
+ can_spi_release_bus(chip);
+
+ CANMSG("Failed to set normal mode\n");
+ return -ENODEV;
+}
+
+
+/**
+ * mcp2515_chip_config: - can chip configuration
+ * @chip: pointer to chip state structure
+ *
+ * This function configures chip and prepares it for message
+ * transmission and reception. The function resets chip,
+ * resets mask for acceptance of all messages by call to
+ * mcp2515_extended_mask() function and then
+ * computes and sets baudrate with use of function mcp2515_baud_rate().
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_chip_config(struct canchip_t *chip)
+{
+ uint8_t pat0[8]={0x00,0xff,0xaa,0x55,0x0f,0xf0,0x3c,0xc3};
+ uint8_t pat1[8];
+ int i;
+
+ DEBUGMSG("mcp2515_chip_config\n");
+
+ ((MCP2515_PRIV *)(chip->chip_data))->chip = chip;
+
+ if (mcp2515_enable_configuration(chip))
+ return -ENODEV;
+
+ /* Acquire SPI bus */
+ can_spi_acquire_bus(chip,1);
+
+ /* Set TXnRTS pins as digital inputs */
+ write_reg(chip,MCP2515_TXRTSCTRL,0);
+
+ /* Set RXnBF pins as digital outputs, b0=low, b1=high */
+ write_reg(chip, MCP2515_BFPCTRL,
+ mcpB0BFE | mcpB1BFE | /* mcpB0BFS | */ mcpB1BFS);
+
+ /* Ensure, that interrupts are disabled even on the chip level now */
+ write_reg(chip, MCP2515_CANINTE, 0);
+
+ /* Configure second receive buffer for rollover */
+ bitmod_reg(chip, MCP2515_RXB0CTRL, mcpBUKT, mcpBUKT);
+
+
+ /* Simple check for chip presence */
+ memset(pat1,0,8);
+ write_block(chip,MCP2515_TXB0DATA,8,pat0);
+ for (i=0;i<10;i++){
+ read_block(chip,MCP2515_TXB0DATA,8,pat1);
+// *(pat1) = read_reg(chip,MCP2515_TXB0DATA);
+// *(pat1+1) = read_reg(chip,MCP2515_TXB0DATA+1);
+// *(pat1+2) = read_reg(chip,MCP2515_TXB0DATA+2);
+// *(pat1+3) = read_reg(chip,MCP2515_TXB0DATA+3);
+// *(pat1+4) = read_reg(chip,MCP2515_TXB0DATA+4);
+// *(pat1+5) = read_reg(chip,MCP2515_TXB0DATA+5);
+// *(pat1+6) = read_reg(chip,MCP2515_TXB0DATA+6);
+// *(pat1+7) = read_reg(chip,MCP2515_TXB0DATA+7);
+
+ if(memcmp(pat0,pat1,8)) {
+ CANMSG("mcp2515_chip_config: chip #%d not found\n",
+ chip->chip_idx);
+ CANMSG("Requested: Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X\n",pat0[0],pat0[1],pat0[2],pat0[3],pat0[4],pat0[5],pat0[6],pat0[7]);
+ CANMSG("Obtained : Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X Ox%X\n",pat1[0],pat1[1],pat1[2],pat1[3],pat1[4],pat1[5],pat1[6],pat1[7]);
+// return -ENODEV;
+ }
+ else
+ break;
+ }
+ if (i==10)
+ return -ENODEV;
+
+ can_spi_release_bus(chip);
+
+ CANMSG("Found mcp2515:%d\n",chip->chip_idx);
+
+
+ if (mcp2515_extended_mask(chip,0x00000000, 0xffffffff))
+ return -ENODEV;
+
+ if (!chip->baudrate) chip->baudrate=1000000;
+ if (mcp2515_baud_rate(chip,chip->baudrate,chip->clock,0,75,0))
+ return -ENODEV;
+
+ /* Enable hardware interrupts */
+ can_spi_acquire_bus(chip,1);
+ write_reg(chip, MCP2515_CANINTE, 0xff);
+ can_spi_release_bus(chip);
+
+ mcp2515_disable_configuration(chip);
+ return 0;
+}
+
+/**
+ * mcp2515_extended_mask: - setup of extended mask for message filtering
+ * @chip: pointer to chip state structure
+ * @code: can message acceptance code
+ * @mask: can message acceptance mask
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_extended_mask(struct canchip_t *chip, unsigned long code, unsigned long mask)
+{
+ DEBUGMSG("mcp2515_extended_mask\n");
+
+#if 0
+ if (mcp2515_enable_configuration(chip))
+ return -ENODEV;
+
+// LSB to +3, MSB to +0
+ for(i=SJA_PeliCAN_AC_LEN; --i>=0;) {
+ can_write_reg(chip,code&0xff,SJAACR0+i);
+ can_write_reg(chip,mask&0xff,SJAAMR0+i);
+ code >>= 8;
+ mask >>= 8;
+ }
+
+ DEBUGMSG("Setting acceptance code to 0x%lx\n",(unsigned long)code);
+ DEBUGMSG("Setting acceptance mask to 0x%lx\n",(unsigned long)mask);
+
+ mcp2515_disable_configuration(chip);
+#endif
+ return 0;
+}
+
+/**
+ * mcp2515_baud_rate: - set communication parameters.
+ * @chip: pointer to chip state structure
+ * @rate: baud rate in Hz
+ * @clock: frequency of mcp2515 clock in Hz (ISA osc is 14318000)
+ * @sjw: synchronization jump width (0-3) prescaled clock cycles
+ * @sampl_pt: sample point in % (0-100) sets (TSEG1+1)/(TSEG1+TSEG2+2) ratio
+ * @flags: fields %BTR1_SAM, %OCMODE, %OCPOL, %OCTP, %OCTN, %CLK_OFF, %CBP
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw_in,
+ int sampl_pt, int flags)
+{
+ int tqs; /* tbit/TQ */
+ int brp;
+ int ps1, ps2, propseg, sjw;
+ int i;
+ u8 readreg;
+
+ DEBUGMSG("mcp2515_baud_rate\n");
+ DEBUGMSG("Clock = %d, rate = %d\n",clock,rate);
+
+ /* Determine the BRP value that gives the requested bit rate. */
+ for(brp = 0; brp < 8; brp++) {
+ tqs = clock / (2 * (brp + 1)) / rate;
+ if (tqs >= 5 && tqs <= 25
+ && (clock / (2 * (brp + 1)) / tqs) == rate)
+ break;
+ }
+ if (brp >= 8)
+ return -EINVAL;
+
+ /* The CAN bus bit time (tbit) is determined by:
+ * tbit = (SyncSeg + PropSeg + PS1 + PS2) * TQ
+ * with:
+ * SyncSeg = 1
+ * sample point (between PS1 and PS2) must be at 60%-70% of the bit time
+ * PropSeg + PS1 >= PS2
+ * PropSeg + PS1 >= Tdelay
+ * PS2 > SJW
+ * 1 <= PropSeg <= 8, 1 <= PS1 <=8, 2 <= PS2 <= 8
+ * SJW = 1 is sufficient in most cases.
+ * Tdelay is usually 1 or 2 TQ.
+ */
+
+ propseg = ps1 = ps2 = (tqs - 1) / 3;
+ if (tqs - (1 + propseg + ps1 + ps2) == 2)
+ ps1++;
+ if (tqs - (1 + propseg + ps1 + ps2) == 1)
+ ps2++;
+ sjw = 1;
+
+ DEBUGMSG("bit rate: BRP = %d, Tbit = %d TQ, PropSeg = %d, PS1 = %d, PS2 = %d, SJW = %d\n",
+ brp, tqs, propseg, ps1, ps2, sjw);
+
+ if (mcp2515_enable_configuration(chip))
+ return -ENODEV;
+
+#define CNF2_BTLMODE 0x80
+#define CNF3_PHSEG2_MASK 0x07
+
+ can_spi_acquire_bus(chip,1);
+ for (i=0;i<10;i++) {
+ write_reg(chip, MCP2515_CNF1, ((sjw-1) << 6) | brp);
+ write_reg(chip, MCP2515_CNF2, CNF2_BTLMODE | ((ps1-1) << 3) | (propseg-1));
+ bitmod_reg(chip, MCP2515_CNF3, CNF3_PHSEG2_MASK,(ps2-1));
+
+ readreg = read_reg(chip, MCP2515_CNF1);
+ if (readreg != (u8)(((sjw-1) << 6) | brp)){
+ CANMSG("Wrong value in CNF1 - sent: 0x%X, received: 0x%X\n",((sjw-1) << 6) | brp,readreg);
+ continue;
+ }
+ readreg = read_reg(chip, MCP2515_CNF2);
+ if (readreg != (u8)(CNF2_BTLMODE | ((ps1-1) << 3) | (propseg-1))){
+ CANMSG("Wrong value in CNF2 - sent: 0x%X, received: 0x%X\n",CNF2_BTLMODE | ((ps1-1) << 3) | (propseg-1),readreg);
+ continue;
+ }
+ readreg = read_reg(chip, MCP2515_CNF3);
+ if (readreg & CNF3_PHSEG2_MASK != (u8)((ps2-1))){
+ CANMSG("Wrong value in CNF3 - sent: 0x%X, received: 0x%X\n",(ps2-1) | brp,readreg & CNF3_PHSEG2_MASK);
+ continue;
+ }
+ break;
+ }
+ can_spi_release_bus(chip);
+ if (i==10){
+ CANMSG("Failed to set bit rate for %d times\n",i);
+ mcp2515_disable_configuration(chip);
+ return -1;
+ }
+
+ /* Calculate actual bit rate. */
+ DEBUGMSG("actual bit rate=%u\n",clock / (2 * (brp + 1)) / tqs);
+
+ mcp2515_disable_configuration(chip);
+
+ return 0;
+}
+
+/**
+ * mcp2515_pre_read_config: - prepares message object for message reception
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object state structure
+ *
+ * Return Value: negative value reports error.
+ * Positive value indicates immediate reception of message.
+ * File: src/mcp2515.c
+ */
+int mcp2515_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj)
+{
+ DEBUGMSG("mcp2515_pre_read_config\n");
+
+ /* FIXME - error recovery here */
+
+ return 0;
+}
+
+#define MAX_TRANSMIT_WAIT_LOOPS 10
+/**
+ * mcp2515_pre_write_config: - prepares message object for message transmission
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object state structure
+ * @msg: pointer to CAN message
+ *
+ * This function prepares selected message object for future initiation
+ * of message transmission by mcp2515_send_msg() function.
+ * The CAN message data and message ID are transfered from @msg slot
+ * into chip buffer in this function.
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ int i=0;
+ unsigned len;
+ uint8_t busy;
+ MCP2515_FRAME frame;
+
+ DEBUGMSG("mcp2515_pre_write_config: id=%u len=%u\n",
+ (unsigned)msg->id,
+ (unsigned)msg->length);
+
+ can_spi_acquire_bus(chip,1);
+
+ /* Wait until Transmit Buffer Status is released */
+ do {
+ busy = read_reg(chip, MCP2515_TXB0CTRL) & mcpTXREQ;
+ if(!busy) break;
+ udelay(i++);
+ } while (i < MAX_TRANSMIT_WAIT_LOOPS);
+
+
+ /* FIXME - error recovery here */
+
+ if (busy) {
+ CANMSG("Transmit timed out, cancelling\n");
+ bitmod_reg(chip, MCP2515_TXB0CTRL, mcpTXREQ, 0);
+ can_spi_release_bus(chip);
+ return -EIO;
+ }
+
+
+ frame.sidl = (msg->id << 5) ||
+ ((msg->flags & MSG_EXT) ? mcpEXIDE : 0) ||
+ ((msg->id >> 16) & mcpEID_MASK);
+
+ len = msg->length;
+ if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH;
+
+ if(msg->flags & MSG_EXT) {
+ frame.sidh = msg->id >> 21;
+ frame.sidl = (msg->id << 5) | ((msg->id >> 27) & 0x3) | mcpEXIDE;
+ frame.eid8 = msg->id >> 19;
+ frame.eid0 = 13;
+ frame.dlc = len | ((msg->flags & MSG_RTR) ? mcpRTR : 0);
+ memcpy(frame.data, msg->data, len);
+ }
+ else {
+ frame.sidh = msg->id >> 21;
+ frame.sidl = (msg->id << 5) | ((msg->id >> 27) & 0x3);
+ frame.eid8 = msg->id >> 19;
+ frame.eid0 = 13;
+ frame.dlc = len | ((msg->flags & MSG_RTR) ? mcpRTR : 0);
+ memcpy(frame.data, msg->data, len);
+ }
+
+ write_block(chip, MCP2515_TXB0SIDH, len+5, &frame);
+
+ can_spi_release_bus(chip);
+
+ return 0;
+}
+
+/**
+ * mcp2515_send_msg: - initiate message transmission
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object state structure
+ * @msg: pointer to CAN message
+ *
+ * This function is called after mcp2515_pre_write_config() function,
+ * which prepares data in chip buffer.
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_send_msg(struct canchip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ DEBUGMSG("mcp2515_send_msg\n");
+
+ can_spi_acquire_bus(chip,1);
+ bitmod_reg(chip, MCP2515_TXB0CTRL, mcpTXREQ, mcpTXREQ);
+ can_spi_release_bus(chip);
+
+ return 0;
+}
+
+/**
+ * mcp2515_check_tx_stat: - checks state of transmission engine
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * Positive return value indicates transmission under way status.
+ * Zero value indicates finishing of all issued transmission requests.
+ * File: src/mcp2515.c
+ */
+int mcp2515_check_tx_stat(struct canchip_t *chip)
+{
+ int status;
+ DEBUGMSG("mcp2515_check_tx_stat\n");
+
+ can_spi_acquire_bus(chip,1);
+ status = read_reg(chip,MCP2515_TXB0CTRL) & mcpTXREQ;
+ can_spi_release_bus(chip);
+
+ if (status) return 1;
+ else return 0;
+}
+
+/**
+ * mcp2515_set_btregs: - configures bitrate registers
+ * @chip: pointer to chip state structure
+ * @btr0: bitrate register 0
+ * @btr1: bitrate register 1
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_set_btregs(struct canchip_t *chip, unsigned short btr0,
+ unsigned short btr1)
+{
+ DEBUGMSG("mcp2515_set_btregs\n");
+#if 0
+ if (mcp2515_enable_configuration(chip))
+ return -ENODEV;
+
+ can_write_reg(chip, btr0, SJABTR0);
+ can_write_reg(chip, btr1, SJABTR1);
+
+ mcp2515_disable_configuration(chip);
+#endif
+ return 0;
+}
+
+/**
+ * mcp2515_start_chip: - starts chip message processing
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_start_chip(struct canchip_t *chip)
+{
+ MCP2515_PRIV *priv=(MCP2515_PRIV *)(chip->chip_data);
+ DEBUGMSG("mcp2515_start_chip\n");
+
+ can_spi_acquire_bus(chip,1);
+ bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_NORM);
+ can_spi_release_bus(chip);
+
+ /* reset error counters */
+ memset(&(priv->errcnt),0,sizeof(MCP2515_ERRCNT));
+
+ return 0;
+}
+
+/**
+ * mcp2515_stop_chip: - stops chip message processing
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_stop_chip(struct canchip_t *chip)
+{
+ DEBUGMSG("mcp2515_stop_chip\n");
+
+ can_spi_acquire_bus(chip,1);
+ bitmod_reg(chip, MCP2515_CANCTRL, mcpMOD_MASK, mcpMOD_SLEEP);
+ can_spi_release_bus(chip);
+
+ return 0;
+}
+
+/**
+ * mcp2515_attach_to_chip: - attaches to the chip, setups registers and state
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_attach_to_chip(struct canchip_t *chip)
+{
+ DEBUGMSG("mcp2515_attach_to_chip\n");
+ /* Initialize delayed interrupt processing */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler),
+ workqueue_handler,
+ NULL);
+ #else
+ INIT_WORK(&(((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler),
+ workqueue_handler);
+ #endif
+
+ tasklet_init(&(((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler),
+ tasklet_handler,
+ (unsigned long)chip);
+
+ return 0;
+}
+
+/**
+ * mcp2515_release_chip: - called before chip structure removal if %CHIP_ATTACHED is set
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_release_chip(struct canchip_t *chip)
+{
+ DEBUGMSG("mcp2515_release_chip\n");
+ if (chip==NULL)
+ panic("release: chip == NULL");
+ if (chip->chip_data==NULL)
+ panic("release: chip_data == NULL");
+
+ chip->flags &= ~CHIP_ATTACHED;
+ DEBUGMSG("Flush workqueue.\n");
+ cancel_delayed_work(&((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
+ flush_scheduled_work();
+
+ DEBUGMSG("Kill tasklets.\n");
+ tasklet_kill(&((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler);
+#if 0
+ mcp2515_stop_chip(chip);
+ can_write_reg(chip, sjaDISABLE_INTERRUPTS, SJAIER);
+#endif
+ return 0;
+}
+
+/**
+ * mcp2515_remote_request: - configures message object and asks for RTR message
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_remote_request(struct canchip_t *chip, struct msgobj_t *obj)
+{
+ CANMSG("mcp2515_remote_request not implemented\n");
+ return -ENOSYS;
+}
+
+/**
+ * mcp2515_standard_mask: - setup of mask for message filtering
+ * @chip: pointer to chip state structure
+ * @code: can message acceptance code
+ * @mask: can message acceptance mask
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_standard_mask(struct canchip_t *chip, unsigned short code,
+ unsigned short mask)
+{
+ CANMSG("mcp2515_standard_mask not implemented\n");
+ return -ENOSYS;
+}
+
+/**
+ * mcp2515_clear_objects: - clears state of all message object residing in chip
+ * @chip: pointer to chip state structure
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_clear_objects(struct canchip_t *chip)
+{
+ CANMSG("mcp2515_clear_objects not implemented\n");
+ return -ENOSYS;
+}
+
+/**
+ * mcp2515_config_irqs: - tunes chip hardware interrupt delivery
+ * @chip: pointer to chip state structure
+ * @irqs: requested chip IRQ configuration
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_config_irqs(struct canchip_t *chip, short irqs)
+{
+ CANMSG("mcp2515_config_irqs not implemented\n");
+ return -ENOSYS;
+}
+
+#define MAX_RETR 10
+
+
+/**
+ * mcp2515_irq_handler: - interrupt service routine
+ * @irq: interrupt vector number, this value is system specific
+ * @chip: pointer to chip state structure
+ *
+ * Interrupt handler is activated when state of CAN controller chip changes,
+ * there is message to be read or there is more space for new messages or
+ * error occurs. The receive events results in reading of the message from
+ * CAN controller chip and distribution of message through attached
+ * message queues.
+ * File: src/mcp2515.c
+ */
+int mcp2515_irq_handler(int irq, struct canchip_t *chip)
+{
+ //DEBUGMSG("mcp2515_irq_handler\n");
+ if (chip == NULL)
+ return CANCHIP_IRQ_ACCEPTED;
+ if (~chip->flags & CHIP_ATTACHED)
+ return CANCHIP_IRQ_ACCEPTED;
+
+ /* do work in tasklet if bus is immediately available */
+ if(can_spi_acquire_bus(chip, 0))
+ tasklet_schedule(&((MCP2515_PRIV *)(chip->chip_data))->tasklet_handler);
+ else /* do work in workqueue */
+ schedule_work(&((MCP2515_PRIV *)(chip->chip_data))->workqueue_handler);
+
+ disable_irq(chip->chip_irq);
+ return CANCHIP_IRQ_HANDLED;
+}
+
+/**
+ * mcp2515_wakeup_tx: - wakeups TX processing
+ * @chip: pointer to chip state structure
+ * @obj: pointer to message object structure
+ *
+ * Function is responsible for initiating message transmition.
+ * It is responsible for clearing of object TX_REQUEST flag
+ *
+ * Return Value: negative value reports error.
+ * File: src/mcp2515.c
+ */
+int mcp2515_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
+{
+ DEBUGMSG("mcp2515_wakeup_tx\n");
+
+ //can_preempt_disable();
+
+ can_msgobj_set_fl(obj,TX_PENDING);
+ can_msgobj_set_fl(obj,TX_REQUEST);
+
+ while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+ int rq;
+
+ can_msgobj_clear_fl(obj,TX_REQUEST);
+
+ can_spi_acquire_bus(chip,1);
+ rq = read_reg(chip, MCP2515_TXB0CTRL) & mcpTXREQ;
+ if (!rq) {
+ obj->tx_retry_cnt=0;
+ tx_handler(chip, obj);
+ }
+ can_spi_release_bus(chip);
+
+ can_msgobj_clear_fl(obj,TX_LOCK);
+ if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
+ DEBUGMSG("TX looping in mcp2515_wakeup_tx\n");
+ }
+ //can_preempt_enable();
+
+ return 0;
+}
+
+int mcp2515_register(struct chipspecops_t *chipspecops)
+{
+ DEBUGMSG("mcp2515_register\n");
+ chipspecops->chip_config=mcp2515_chip_config;
+ chipspecops->baud_rate=mcp2515_baud_rate;
+ chipspecops->standard_mask=mcp2515_standard_mask;
+ chipspecops->extended_mask=mcp2515_extended_mask;
+ chipspecops->message15_mask=mcp2515_extended_mask;
+ chipspecops->clear_objects=mcp2515_clear_objects;
+ chipspecops->config_irqs=mcp2515_config_irqs;
+ chipspecops->pre_read_config=mcp2515_pre_read_config;
+ chipspecops->pre_write_config=mcp2515_pre_write_config;
+ chipspecops->send_msg=mcp2515_send_msg;
+ chipspecops->check_tx_stat=mcp2515_check_tx_stat;
+ chipspecops->wakeup_tx=mcp2515_wakeup_tx;
+ chipspecops->remote_request=mcp2515_remote_request;
+ chipspecops->enable_configuration=mcp2515_enable_configuration;
+ chipspecops->disable_configuration=mcp2515_disable_configuration;
+ chipspecops->attach_to_chip=mcp2515_attach_to_chip;
+ chipspecops->release_chip=mcp2515_release_chip;
+ chipspecops->set_btregs=mcp2515_set_btregs;
+ chipspecops->start_chip=mcp2515_start_chip;
+ chipspecops->stop_chip=mcp2515_stop_chip;
+ chipspecops->irq_handler=mcp2515_irq_handler;
+ chipspecops->irq_accept=NULL;
+ chipspecops->reset_chip=mcp2515_reset_chip;
+ chipspecops->get_info=mcp2515_get_info;
+ return 0;
+}
+
+/**
+ * mcp2515_fill_chipspecops - fills chip specific operations
+ * @chip: pointer to chip representation structure
+ *
+ * The function fills chip specific operations for mcp2515 (PeliCAN) chip.
+ *
+ * Return Value: returns negative number in the case of fail
+ */
+int mcp2515_fill_chipspecops(struct canchip_t *chip)
+{
+ DEBUGMSG("mcp2515_fill_chipspecops\n");
+ chip->chip_type="mcp2515";
+ chip->max_objects=2;
+ mcp2515_register(chip->chipspecops);
+ return 0;
+}
--- /dev/null
+/* omap2_spican.c
+ * Linux CAN-bus device driver.
+ * Written by Arnaud Westenberg email:arnaud@wanadoo.nl
+ * Rewritten for new CAN queues by Pavel Pisa - OCERA team member
+ * email:pisa@cmp.felk.cvut.cz
+ * This software is released under the GPL-License.
+ * Version lincan-0.3 17 Jun 2004
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+
+#include "../include/can.h"
+#include "../include/can_sysdep.h"
+#include "../include/main.h"
+#include "../include/setup.h"
+#include "../include/omap2_spican.h"
+#include "../include/mcp2515.h"
+
+#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
+ #include <asm/io.h>
+ #include <asm/uaccess.h>
+ #include <plat/dmtimer.h>
+#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
+
+/*******************************************************************/
+/*
+ * We can't use the standard synchronous wrappers for file I/O; we
+ * need to protect against async removal of the underlying spi_device.
+ */
+static void omap2_spican_spi_complete(void *arg)
+{
+ complete(arg);
+}
+
+static ssize_t omap2_spican_spi_sync(struct candevice_t *candev, struct spi_message *message)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+ int status;
+
+ if (pdata == NULL)
+ return -ESHUTDOWN;
+
+ message->complete = omap2_spican_spi_complete;
+ message->context = &done;
+ spin_lock_irq(&pdata->spi_lock);
+ if (pdata->spi == NULL)
+ status = -ESHUTDOWN;
+ else
+ status = spi_async(pdata->spi, message);
+ spin_unlock_irq(&pdata->spi_lock);
+
+ if (status == 0) {
+ wait_for_completion(&done);
+ status = message->status;
+ if (status == 0)
+ status = message->actual_length;
+ }
+ return status;
+}
+
+static ssize_t omap2_spican_spi_transfer(struct candevice_t *candev,void *tx, void *rx, uint16_t len)
+{
+ struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+ struct spi_transfer t = {
+ .tx_buf = tx,
+ .rx_buf = rx,
+ .len = len,
+ .cs_change = 1,
+ .speed_hz = pdata->speed_hz,
+ .bits_per_word = 8,
+ };
+ struct spi_message m;
+ int i,status;
+
+ if (pdata == NULL)
+ return -1;
+ if(len > SPI_BUF_LEN) panic("long CAN spi transfer: %u",len);
+
+ spi_message_init(&m);
+
+ spi_message_add_tail(&t, &m);
+ status = omap2_spican_spi_sync(candev, &m);
+/* struct spi_transfer *t;
+ struct spi_message m;
+ int i,status;
+
+ if (pdata == NULL)
+ return -1;
+ if(len > SPI_BUF_LEN) panic("long CAN spi transfer: %u",len);
+
+ spi_message_init(&m);
+
+ t = can_checked_malloc(len * sizeof(struct spi_transfer));
+ for (i=0;i<len;i++){
+ (*(t+i)).tx_buf = tx+i;
+ (*(t+i)).rx_buf = rx+i;
+ (*(t+i)).len = 1;
+ (*(t+i)).cs_change = 0;
+ (*(t+i)).delay_usecs = 0;
+ (*(t+i)).speed_hz = pdata->speed_hz;
+ (*(t+i)).bits_per_word = 8;
+ spi_message_add_tail(t+i, &m);
+ }
+ status = omap2_spican_spi_sync(candev, &m);
+ can_checked_free(t);*/
+ return status;
+}
+
+
+/// ---------------------- EVERYTHING'S DONE AFTER THIS POINT -----------------------------------------------
+
+/**
+ * omap2_spican_irq_handler: - GPT (general purpose timer) and waiting
+ * thread interrupt handler
+ * @irq: the interrupt line number
+ * @dev_id: pointer to can device data
+ *
+ * Return Value: Interrupt handled status is returned
+ * File: src/omap2_spican.c
+ */
+static irqreturn_t omap2_spican_irq_handler(int irq, void *dev_id)
+{
+ struct candevice_t *candev = dev_id;
+ struct omap2_spican_platform_data *pdata;
+ unsigned long flags;
+ int i;
+
+ if (!dev_id)
+ return IRQ_HANDLED;
+
+ pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+ if (!pdata)
+ return IRQ_HANDLED;
+
+ #ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
+ if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ // reset the timer interrupt status
+ omap_dm_timer_write_status(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
+ omap_dm_timer_read_status(pdata->timer_ptr); // YES, you really need to do this 'wasteful' read
+ }
+ #endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
+
+ for (i=0;i<candev->nr_all_chips;i++){
+ if (candev->chip[i]->chipspecops->irq_handler)
+ candev->chip[i]->chipspecops->irq_handler(irq, candev->chip[i]);
+ }
+
+ return IRQ_HANDLED;
+
+}
+
+/**
+ * omap2_spican_request_io: - reserve io or memory range for can board
+ * @candev: pointer to candevice/board which asks for io. Field @io_addr
+ * of @candev is used in most cases to define start of the range
+ *
+ * The function omap2_spican_request_io() is used to reserve the io-memory. If your
+ * hardware uses a dedicated memory range as hardware control registers you
+ * will have to add the code to reserve this memory as well.
+ * %IO_RANGE is the io-memory range that gets reserved, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/omap2_spican.c
+ */
+int omap2_spican_request_io(struct candevice_t *candev)
+{
+ return 0;
+}
+
+/**
+ * omap2_spican_release_io - free reserved io memory range
+ * @candev: pointer to candevice/board which releases io
+ *
+ * The function omap2_spican_release_io() is used to free reserved io-memory.
+ * In case you have reserved more io memory, don't forget to free it here.
+ * IO_RANGE is the io-memory range that gets released, please adjust according
+ * your hardware. Example: #define IO_RANGE 0x100 for i82527 chips or
+ * #define IO_RANGE 0x20 for sja1000 chips in basic CAN mode.
+ * Return Value: The function always returns zero
+ * File: src/omap2_spican.c
+ */
+int omap2_spican_release_io(struct candevice_t *candev)
+{
+ return 0;
+}
+
+/**
+ * omap2_spican_reset - hardware reset routine
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function omap2_spican_reset() is used to give a hardware reset. This is
+ * rather hardware specific so I haven't included example code. Don't forget to
+ * check the reset status of the chip before returning.
+ * Return Value: The function returns zero on success or %-ENODEV on failure
+ * File: src/omap2_spican.c
+ */
+int omap2_spican_reset(struct candevice_t *candev)
+{
+ int i;
+ DEBUGMSG("Resetting all underlying chips\n");
+ for(i=0;i<OMAP2_SPICAN_NCHIPS;i++)
+ (candev->chip[i]->chipspecops->reset_chip)(candev->chip[i]);
+
+ return 0;
+}
+
+/**
+ * omap2_spican_init_hw_data - Initialize hardware cards
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function omap2_spican_init_hw_data() is used to initialize the hardware
+ * structure containing information about the installed CAN-board.
+ * %RESET_ADDR represents the io-address of the hardware reset register.
+ * %NR_82527 represents the number of Intel 82527 chips on the board.
+ * %NR_SJA1000 represents the number of Philips sja1000 chips on the board.
+ * The flags entry can currently only be %CANDEV_PROGRAMMABLE_IRQ to indicate that
+ * the hardware uses programmable interrupts.
+ * Return Value: The function always returns zero
+ * File: src/omap2_spican.c
+ */
+int omap2_spican_init_hw_data(struct candevice_t *candev)
+{
+ candev->dev_base_addr = 0;
+ candev->nr_82527_chips = 0;
+ candev->nr_sja1000_chips = 0;
+ candev->nr_mcp2515_chips = OMAP2_SPICAN_NCHIPS;
+ candev->nr_all_chips = candev->nr_82527_chips +
+ candev->nr_sja1000_chips +
+ candev->nr_mcp2515_chips;
+ candev->flags &= ~CANDEV_PROGRAMMABLE_IRQ;
+
+ return 0;
+}
+
+/**
+ * omap2_spican_init_chip_data - Initialize chips
+ * @candev: Pointer to candevice/board structure
+ * @chipnr: Number of the CAN chip on the hardware card
+ *
+ * The function omap2_spican_init_chip_data() is used to initialize the hardware
+ * structure containing information about the CAN chips.
+ * %CHIP_TYPE represents the type of CAN chip. %CHIP_TYPE can be "i82527" or
+ * "sja1000".
+ * The @chip_base_addr entry represents the start of the 'official' memory map
+ * of the installed chip. It's likely that this is the same as the @io_addr
+ * argument supplied at module loading time.
+ * The @clock entry holds the chip clock value in Hz.
+ * The entry @sja_cdr_reg holds hardware specific options for the Clock Divider
+ * register. Options defined in the %sja1000.h file:
+ * %sjaCDR_CLKOUT_MASK, %sjaCDR_CLK_OFF, %sjaCDR_RXINPEN, %sjaCDR_CBP, %sjaCDR_PELICAN
+ * The entry @sja_ocr_reg holds hardware specific options for the Output Control
+ * register. Options defined in the %sja1000.h file:
+ * %sjaOCR_MODE_BIPHASE, %sjaOCR_MODE_TEST, %sjaOCR_MODE_NORMAL, %sjaOCR_MODE_CLOCK,
+ * %sjaOCR_TX0_LH, %sjaOCR_TX1_ZZ.
+ * The entry @int_clk_reg holds hardware specific options for the Clock Out
+ * register. Options defined in the %i82527.h file:
+ * %iCLK_CD0, %iCLK_CD1, %iCLK_CD2, %iCLK_CD3, %iCLK_SL0, %iCLK_SL1.
+ * The entry @int_bus_reg holds hardware specific options for the Bus
+ * Configuration register. Options defined in the %i82527.h file:
+ * %iBUS_DR0, %iBUS_DR1, %iBUS_DT1, %iBUS_POL, %iBUS_CBY.
+ * The entry @int_cpu_reg holds hardware specific options for the cpu interface
+ * register. Options defined in the %i82527.h file:
+ * %iCPU_CEN, %iCPU_MUX, %iCPU_SLP, %iCPU_PWD, %iCPU_DMC, %iCPU_DSC, %iCPU_RST.
+ * Return Value: The function always returns zero
+ * File: src/omap2_spican.c
+ */
+int omap2_spican_init_chip_data(struct candevice_t *candev, int chipnr)
+{
+ if(chipnr >= OMAP2_SPICAN_NCHIPS || chipnr < 0) {
+ CANMSG("Error: chipnr=%d\n",chipnr);
+ return -ENODEV;
+ }
+
+ mcp2515_fill_chipspecops(candev->chip[chipnr]);
+/* candev->chip[chipnr]->chip_base_addr = 0;
+ candev->chip[chipnr]->spi_channel = 2;*/
+ candev->chip[chipnr]->chip_data = can_checked_malloc(sizeof(MCP2515_PRIV));
+ if(candev->chip[chipnr]->chip_data == NULL) return -ENOMEM;
+ memset(candev->chip[chipnr]->chip_data,0,sizeof(MCP2515_PRIV));
+
+ return 0;
+}
+
+int omap2_spican_register_chip_data(struct canchip_t *ch,void *data){
+ struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)data;
+ ch->clock = pdata->mcp2515_clk;
+ ch->baudrate = pdata->baudrate;
+ int ret = 0;
+
+ ch->flags |= CHIP_IRQ_CUSTOM;
+// if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ){
+// ch->chip_irq = pdata->irq;
+// }
+
+ ret = ch->chipspecops->chip_config(ch);
+ if (ret){
+ CANMSG("Error configuring chip.\n");
+ }
+ else
+ ch->flags |= CHIP_CONFIGURED;
+ return ret;
+}
+
+/**
+ * omap2_spican_init_obj_data - Initialize message buffers
+ * @chip: Pointer to chip specific structure
+ * @objnr: Number of the message buffer
+ *
+ * The function omap2_spican_init_obj_data() is used to initialize the hardware
+ * structure containing information about the different message objects on the
+ * CAN chip. In case of the sja1000 there's only one message object but on the
+ * i82527 chip there are 15.
+ * The code below is for a i82527 chip and initializes the object base addresses
+ * The entry @obj_base_addr represents the first memory address of the message
+ * object. In case of the sja1000 @obj_base_addr is taken the same as the chips
+ * base address.
+ * Unless the hardware uses a segmented memory map, flags can be set zero.
+ * Return Value: The function always returns zero
+ * File: src/omap2_spican.c
+ */
+int omap2_spican_init_obj_data(struct canchip_t *chip, int objnr)
+{
+ chip->msgobj[objnr]->obj_base_addr=0;
+ chip->msgobj[objnr]->obj_flags=0;
+
+ return 0;
+}
+
+/**
+ * omap2_spican_write_register - Low level write register routine
+ * @data: data to be written
+ * @address: memory address to write to
+ *
+ * The function omap2_spican_write_register() is used to write to hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific write process.
+ * Return Value: The function does not return a value
+ * File: src/omap2_spican.c
+ */
+void omap2_spican_write_register(unsigned data, can_ioptr_t address)
+{
+ panic("omap2_spican_write_register");
+}
+
+/**
+ * omap2_spican_read_register - Low level read register routine
+ * @address: memory address to read from
+ *
+ * The function omap2_spican_read_register() is used to read from hardware registers
+ * on the CAN chip. You should only have to edit this function if your hardware
+ * uses some specific read process.
+ * Return Value: The function returns the value stored in @address
+ * File: src/omap2_spican.c
+ */
+unsigned omap2_spican_read_register(can_ioptr_t address)
+{
+ panic("omap2_spican_read_register");
+ return 0;
+}
+
+/**
+ * omap2_spican_program_irq - program interrupts
+ * @candev: Pointer to candevice/board structure
+ *
+ * The function omap2_spican_program_irq() is used for hardware that uses
+ * programmable interrupts. If your hardware doesn't use programmable interrupts
+ * you should not set the @candevices_t->flags entry to %CANDEV_PROGRAMMABLE_IRQ and
+ * leave this function unedited. Again this function is hardware specific so
+ * there's no example code.
+ * Return value: The function returns zero on success or %-ENODEV on failure
+ * File: src/omap2_spican.c
+ */
+int omap2_spican_program_irq(struct candevice_t *candev)
+{
+ return 0;
+}
+
+/*******************************************************************/
+int omap2_spican_spi_acquire_bus(struct candevice_t *candev, short channel, int block){
+ return 1;
+}
+
+/*******************************************************************/
+void omap2_spican_spi_release_bus(struct candevice_t *candev, short channel){
+ return;
+}
+
+int omap2_spican_register(struct hwspecops_t *hwspecops)
+{
+ hwspecops->request_io = omap2_spican_request_io;
+ hwspecops->release_io = omap2_spican_release_io;
+ hwspecops->reset = omap2_spican_reset;
+ hwspecops->init_hw_data = omap2_spican_init_hw_data;
+ hwspecops->init_chip_data = omap2_spican_init_chip_data;
+ hwspecops->init_obj_data = omap2_spican_init_obj_data;
+ hwspecops->write_register = omap2_spican_write_register;
+ hwspecops->read_register = omap2_spican_read_register;
+ hwspecops->program_irq = omap2_spican_program_irq;
+
+ // SPI specific functions
+ hwspecops->spi_transfer = omap2_spican_spi_transfer;
+ hwspecops->spi_acquire_bus = omap2_spican_spi_acquire_bus;
+ hwspecops->spi_release_bus = omap2_spican_spi_release_bus;
+
+ return 0;
+}
+
+/*******************************************************************/
+
+static int omap2_spican_probe(struct spi_device *spi)
+{
+ struct omap2_spican_platform_data *pdata = spi->dev.platform_data;
+ struct candevice_t *dev;
+ int allocated = 0;
+
+ static struct omap2_spican_platform_data mypdata;
+ static const char* omap2_spican_defaulttype = "mcp2515";
+
+ DEBUGMSG("Starting probe for omap2_spican...\n");
+
+ // Structure checks and initialization
+ if (pdata == NULL){
+/* pdata = (struct omap2_spican_platform_data *)can_checked_malloc(sizeof(struct omap2_spican_platform_data));
+ if (pdata)
+ memset(pdata,0,sizeof(struct omap2_spican_platform_data));
+ else
+ return -ENOMEM;
+ allocated = 1;*/
+ pdata = &mypdata; // No need to free up
+
+ if (!pdata->cs_change) pdata->cs_change = OMAP2_SPICAN_CS_CHANGE;
+ if (!pdata->delay_usecs) pdata->delay_usecs = OMAP2_SPICAN_DELAY_USECS;
+ }
+ if (!pdata->mcp2515_clk) pdata->mcp2515_clk = OMAP2_SPICAN_MCP_CLK;
+ if (!pdata->baudrate) pdata->baudrate = OMAP2_SPICAN_BAUDRATE;
+ if (!pdata->speed_hz) pdata->speed_hz = OMAP2_SPICAN_SPEED_HZ;
+ if (!pdata->chiptype) pdata->chiptype = omap2_spican_defaulttype;
+
+
+ spin_lock_init(&pdata->spi_lock);
+ pdata->spi = spi;
+ pdata->trigger = 0;
+ if (spi->irq) {
+ DEBUGMSG("Interrupt line number %d provided.\n",spi->irq);
+ pdata->trigger = OMAP2_SPICAN_TRIG_IRQ;
+ pdata->irq = spi->irq;
+ }
+ else
+ DEBUGMSG("no IRQ given, trying to find timers. Performance loss will be observed.\n");
+
+ /* Register device data in LinCAN - 1 chip, 2 msgobjs */
+ dev = register_hotplug_dev("omap2_spican",omap2_spican_register_chip_data,pdata);
+ if (!dev)
+ goto release;
+
+ if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ) {
+ int status = 0;
+ DEBUGMSG("Requesting interrupt line %d.\n",spi->irq);
+ status = set_irq_type(pdata->irq,IRQ_TYPE_EDGE_BOTH);
+ if(status){
+ CANMSG("Setting low level trigger on irq %d failed.\n", spi->irq);
+ goto release;
+ }
+ status = request_irq(pdata->irq, omap2_spican_irq_handler, IRQF_DISABLED , "omap2_spican", dev);
+ if(status){
+ CANMSG("request_irq failed on irq %d\n", spi->irq);
+ goto release;
+ }
+ }
+#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
+ if (pdata->trigger == 0){
+ struct clk *gt_fclk;
+ uint32_t gt_rate;
+ int status = 0;
+ int prescaler = 0;
+
+ DEBUGMSG("Setting up OMAP GPT\n");
+ // Get the timer
+ pdata->timer_ptr = omap_dm_timer_request();
+ if(pdata->timer_ptr == NULL){
+ CANMSG("No more gp timers available, bailing out\n");
+ goto release;
+ }
+
+ // Initialize timer
+ omap_dm_timer_set_source(pdata->timer_ptr, OMAP_TIMER_SRC_SYS_CLK); // We use the system clock 38.4 MHz
+
+ omap_dm_timer_set_prescaler(pdata->timer_ptr, prescaler); // Set prescaler to 2^(n+1)
+
+ // get clock rate in Hz
+ gt_fclk = omap_dm_timer_get_fclk(pdata->timer_ptr);
+ gt_rate = clk_get_rate(gt_fclk)/(1<<(prescaler+1));
+
+ // set preload, and autoreload
+ // we set it to the clock rate in order to get 1 overflow every 10 usecs
+ omap_dm_timer_set_load(pdata->timer_ptr, 1, 0xFFFFFFFF - (uint32_t)(gt_rate / 100));
+
+ // Request for the irq
+ pdata->timer_irq = omap_dm_timer_get_irq(pdata->timer_ptr);
+ DEBUGMSG("Requesting irq %d for OMAP GPT\n", pdata->timer_irq);
+ status = request_irq(pdata->timer_irq, omap2_spican_irq_handler, IRQF_DISABLED | IRQF_TIMER , "omap2_spican", dev);
+ if(status){
+ CANMSG("request_irq failed (on irq %d), bailing out\n", pdata->timer_irq);
+ omap_dm_timer_free(pdata->timer_ptr);
+ goto release;
+ }
+
+ // setup timer to trigger our IRQ on the overflow event
+ omap_dm_timer_set_int_enable(pdata->timer_ptr, OMAP_TIMER_INT_OVERFLOW);
+ pdata->trigger = OMAP2_SPICAN_TRIG_GPT;
+ }
+#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
+
+ spi_set_drvdata(spi, dev);
+
+#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
+ if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ // Everything's ready, let's roll!
+ DEBUGMSG("Starting OMAP GPT\n");
+ omap_dm_timer_start(pdata->timer_ptr);
+ }
+#endif /* CONFIG_OC_LINCAN_OMAP_DM_TIMER */
+ DEBUGMSG("Device omap2_spican successfully configured. Have fun!\n");
+ return 0;
+
+release:
+ if (allocated)
+ can_checked_free(pdata);
+ return -ENODEV;
+}
+
+static int omap2_spican_remove(struct spi_device *spi)
+{
+ struct candevice_t *candev = spi_get_drvdata(spi);
+ struct omap2_spican_platform_data *pdata = (struct omap2_spican_platform_data *)(candev->sysdevptr.anydev);
+
+ if (pdata->trigger == OMAP2_SPICAN_TRIG_IRQ){
+ // release the IRQ handler
+ free_irq(pdata->irq, candev);
+ }
+#ifdef CONFIG_OC_LINCAN_OMAP_DM_TIMER
+ if (pdata->trigger == OMAP2_SPICAN_TRIG_GPT){
+ DEBUGMSG("Stopping OMAP GPT\n");
+ omap_dm_timer_stop(pdata->timer_ptr);
+ // release the IRQ handler
+ free_irq(pdata->timer_irq, candev);
+ // release the timer
+ omap_dm_timer_free(pdata->timer_ptr);
+ }
+#endif
+
+ DEBUGMSG("Removing omap2_spican from device structure...");
+ // make sure ops on existing fds can abort cleanly
+ cleanup_hotplug_dev(candev);
+
+ spin_lock_irq(&pdata->spi_lock);
+ pdata->spi = NULL;
+ spi_set_drvdata(spi, NULL);
+// candev->sysdevptr.anydev = NULL;
+ spin_unlock_irq(&pdata->spi_lock);
+
+ DEBUGMSG(" done.\n");
+
+ return 0;
+}
+
+static struct spi_driver omap2_spican_driver = {
+ .driver = {
+ .name = "omap2_spican",
+ .owner = THIS_MODULE,
+ },
+ .probe = omap2_spican_probe,
+ .remove = omap2_spican_remove,
+/* .suspend = omap2_spican_suspend,
+ .resume = omap2_spican_resume,*/
+};
+
+int omap2_spican_init(void){
+ return spi_register_driver(&omap2_spican_driver);
+}
+
+void omap2_spican_exit(void){
+ spi_unregister_driver(&omap2_spican_driver);
+}
+
+#ifdef MODULE_ALIAS
+MODULE_ALIAS("spi:omap2_spican");
+#endif
chip->write_register=candev->hwspecops->write_register;
chip->read_register=candev->hwspecops->read_register;
+ chip->spi_transfer=candev->hwspecops->spi_transfer;
+ chip->spi_acquire_bus=candev->hwspecops->spi_acquire_bus;
+ chip->spi_release_bus=candev->hwspecops->spi_release_bus;
+
chip->chipspecops=can_checked_malloc(sizeof(struct chipspecops_t));
if (chip->chipspecops==NULL)
int msglen;
char specialfile[MAXL+1]="/dev/can0", buf[MAXL+1];
struct canmsg_t message;
+ unsigned val;
printf("\nThis program allows you to send a stream of Can messages.\n");
printf("Please answer the following questions:\n\n");
scanf("%d",&msglen);
message.length=msglen;
for (i=0; i<message.length; i++) {
- int val;
printf("Enter data byte [%d] ",i);
scanf("%x",&val);
message.data[i]=val;