From 32115f5fda63ffe8b582a10d2f55ae765fec4f41 Mon Sep 17 00:00:00 2001 From: "Ing. Jan Kriz" Date: Sun, 17 Apr 2011 20:11:10 +0200 Subject: [PATCH] Spican1 support added --- lincan/include/main.h | 31 + lincan/include/mcp2515.h | 303 +++++++++ lincan/include/omap2_spican.h | 57 ++ lincan/src/Makefile.omk | 6 +- lincan/src/boardlist.c | 4 + lincan/src/finish.c | 2 + lincan/src/main.c | 24 +- lincan/src/mcp2515.c | 1094 +++++++++++++++++++++++++++++++++ lincan/src/omap2_spican.c | 598 ++++++++++++++++++ lincan/src/setup.c | 4 + lincan/utils/send.c | 2 +- 11 files changed, 2119 insertions(+), 6 deletions(-) create mode 100644 lincan/include/mcp2515.h create mode 100644 lincan/include/omap2_spican.h create mode 100644 lincan/src/mcp2515.c create mode 100644 lincan/src/omap2_spican.c diff --git a/lincan/include/main.h b/lincan/include/main.h index 01f8739..853d2ff 100644 --- a/lincan/include/main.h +++ b/lincan/include/main.h @@ -106,6 +106,7 @@ struct candevice_t { 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]; @@ -188,6 +189,13 @@ struct canchip_t { 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! */ @@ -326,6 +334,9 @@ struct hwspecops_t { 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); }; /** @@ -353,6 +364,7 @@ struct hwspecops_t { * @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); @@ -385,6 +397,8 @@ struct chipspecops_t { 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 { @@ -480,6 +494,23 @@ extern inline unsigned can_read_reg(const struct canchip_t *chip, unsigned reg_o 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) { diff --git a/lincan/include/mcp2515.h b/lincan/include/mcp2515.h new file mode 100644 index 0000000..00add4e --- /dev/null +++ b/lincan/include/mcp2515.h @@ -0,0 +1,303 @@ +#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 diff --git a/lincan/include/omap2_spican.h b/lincan/include/omap2_spican.h new file mode 100644 index 0000000..c165a28 --- /dev/null +++ b/lincan/include/omap2_spican.h @@ -0,0 +1,57 @@ +/* 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); diff --git a/lincan/src/Makefile.omk b/lincan/src/Makefile.omk index e9eecd6..e0cf50f 100644 --- a/lincan/src/Makefile.omk +++ b/lincan/src/Makefile.omk @@ -1,16 +1,16 @@ 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=%) @@ -66,7 +66,7 @@ endif #CONFIG_OC_LINCANRTL 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) diff --git a/lincan/src/boardlist.c b/lincan/src/boardlist.c index 7623ea3..8ab7756 100644 --- a/lincan/src/boardlist.c +++ b/lincan/src/boardlist.c @@ -73,6 +73,7 @@ extern int ns_dev_register(struct hwspecops_t *hwspecops); 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); @@ -204,6 +205,9 @@ const struct boardtype_t can_boardtypes[]={ #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}, diff --git a/lincan/src/finish.c b/lincan/src/finish.c index 2766c01..9ba504e 100644 --- a/lincan/src/finish.c +++ b/lincan/src/finish.c @@ -118,6 +118,8 @@ void canchip_done(struct canchip_t *chip) } can_checked_free(chip->chipspecops); + can_checked_free(chip->chip_data); + chip->chip_data = NULL; chip->chipspecops=NULL; } diff --git a/lincan/src/main.c b/lincan/src/main.c index 8018b58..b7a34ac 100644 --- a/lincan/src/main.c +++ b/lincan/src/main.c @@ -100,6 +100,9 @@ #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; @@ -399,6 +402,17 @@ int init_module(void) 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; @@ -441,6 +455,7 @@ int init_module(void) + 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; @@ -656,7 +671,12 @@ void cleanup_module(void) 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()) @@ -698,7 +718,7 @@ void cleanup_module(void) #endif /*CAN_WITH_RTL*/ DEBUGMSG("Can hardware cleanup done, freeing memory.\n"); - + if ( can_del_mem_list() ) CANMSG("Error deallocating memory\n"); diff --git a/lincan/src/mcp2515.c b/lincan/src/mcp2515.c new file mode 100644 index 0000000..10e4c5f --- /dev/null +++ b/lincan/src/mcp2515.c @@ -0,0 +1,1094 @@ +/* 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;ichip_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;ichip_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; +} diff --git a/lincan/src/omap2_spican.c b/lincan/src/omap2_spican.c new file mode 100644 index 0000000..132d3ca --- /dev/null +++ b/lincan/src/omap2_spican.c @@ -0,0 +1,598 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + #include + #include +#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;ispeed_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;inr_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;ichip[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 diff --git a/lincan/src/setup.c b/lincan/src/setup.c index b051d04..525539f 100644 --- a/lincan/src/setup.c +++ b/lincan/src/setup.c @@ -345,6 +345,10 @@ int init_chip_struct(struct candevice_t *candev, int chipnr, int irq, long baudr 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) diff --git a/lincan/utils/send.c b/lincan/utils/send.c index b8171fd..3b7d3fe 100644 --- a/lincan/utils/send.c +++ b/lincan/utils/send.c @@ -14,6 +14,7 @@ int main(void) 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"); @@ -35,7 +36,6 @@ int main(void) scanf("%d",&msglen); message.length=msglen; for (i=0; i