]> rtime.felk.cvut.cz Git - lincan.git/commitdiff
Spican1 support added
authorIng. Jan Kriz <devel@jojen.net>
Sun, 17 Apr 2011 18:11:10 +0000 (20:11 +0200)
committerIng. Jan Kriz <devel@jojen.net>
Sun, 17 Apr 2011 18:11:10 +0000 (20:11 +0200)
lincan/include/main.h
lincan/include/mcp2515.h [new file with mode: 0644]
lincan/include/omap2_spican.h [new file with mode: 0644]
lincan/src/Makefile.omk
lincan/src/boardlist.c
lincan/src/finish.c
lincan/src/main.c
lincan/src/mcp2515.c [new file with mode: 0644]
lincan/src/omap2_spican.c [new file with mode: 0644]
lincan/src/setup.c
lincan/utils/send.c

index 01f8739f2ff122bdbc7cc217eb79bea40dfcd91e..853d2ffd453ff03415be66ed8df5a62d63b2d6d8 100644 (file)
@@ -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 (file)
index 0000000..00add4e
--- /dev/null
@@ -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 (file)
index 0000000..c165a28
--- /dev/null
@@ -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);
index e9eecd6ed8c057ef5c247dbdcd9477c50126a213..e0cf50f6811c328ebaa0d123a2004cc1db38f6b0 100644 (file)
@@ -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)
index 7623ea353bc08e04c10742dd797a824543d3cd71..8ab77561e3b1f82b28a247b54de7368a8d8e4ae6 100644 (file)
@@ -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},
index 2766c01a7b0f5e49cb9f2d98b2e2b7b12154abe9..9ba504ec0b83a5b7043c9cea75508c83e9ee56e4 100644 (file)
@@ -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;
 
 }
index 8018b58ba4323db67bad83a9af54626b116ea6e1..b7a34acba19ebbe53a430d641ee4e77ddb0c3488 100644 (file)
 #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 (file)
index 0000000..10e4c5f
--- /dev/null
@@ -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;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;
+}
diff --git a/lincan/src/omap2_spican.c b/lincan/src/omap2_spican.c
new file mode 100644 (file)
index 0000000..132d3ca
--- /dev/null
@@ -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 <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
index b051d04425f776c841616462e4c4c5cf7dda664b..525539ff641ec6026a8cb2dcee32b463778fb0bf 100644 (file)
@@ -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)
index b8171fd046f5da7f5400f3770e878fadf8224de7..3b7d3fe41081d9ae950feb40d0ee4b9e9e9c7f24 100644 (file)
@@ -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<message.length; i++) {
-               int val;
                printf("Enter data byte [%d] ",i);
                scanf("%x",&val);
                message.data[i]=val;