From 0fec21d9ba47b6b54a588dc57b24466741b59ccd Mon Sep 17 00:00:00 2001 From: termitt Date: Tue, 20 Nov 2007 21:47:41 +0000 Subject: [PATCH] Added support for HCAN2 controllers integrated in SH7760. The code has been developed during diploma thesis work at the Department of Control Engineering, FEL, CTU and has been tested on EMX32 SH7760 based board produced by MSC. --- lincan/include/hcan2.h | 193 ++++++++ lincan/include/sh7760.h | 29 ++ lincan/src/hcan2.c | 991 ++++++++++++++++++++++++++++++++++++++++ lincan/src/sh7760.c | 104 +++++ 4 files changed, 1317 insertions(+) create mode 100644 lincan/include/hcan2.h create mode 100644 lincan/include/sh7760.h create mode 100644 lincan/src/hcan2.c create mode 100644 lincan/src/sh7760.c diff --git a/lincan/include/hcan2.h b/lincan/include/hcan2.h new file mode 100644 index 0000000..2e5b6ef --- /dev/null +++ b/lincan/include/hcan2.h @@ -0,0 +1,193 @@ +/* hcan2.h +* Header file for the Linux CAN-bus driver. +* This software is released under the GPL-License. +*/ + +int hcan2_chip_config(struct canchip_t *chip); +int hcan2_enable_configuration(struct canchip_t *chip); +int hcan2_disable_configuration(struct canchip_t *chip); + +int hcan2_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw, int sampl_pt, int flags); +int hcan2_set_btregs(struct canchip_t *chip, unsigned short btr0, unsigned short btr1); + +int hcan2_start_chip(struct canchip_t *chip); +int hcan2_stop_chip(struct canchip_t *chip); +int hcan2_attach_to_chip(struct canchip_t *chip); +int hcan2_release_chip(struct canchip_t *chip); + +int hcan2_standard_mask(struct canchip_t *chip, unsigned short code, unsigned short mask); +int hcan2_extended_mask(struct canchip_t *chip, unsigned long code, unsigned long mask); +int hcan2_message15_mask(int irq, struct canchip_t *chip); + +int hcan2_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj); +int hcan2_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg); +int hcan2_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg); +int hcan2_remote_request(struct canchip_t *chip, struct msgobj_t *obj); + +int hcan2_irq_handler(int irq, struct canchip_t *chip); +int hcan2_irq_accept(int irq, struct canchip_t *chip); +int hcan2_config_irqs(struct canchip_t *chip, short irqs); + +int hcan2_clear_objects(struct canchip_t *chip); +int hcan2_check_tx_stat(struct canchip_t *chip); +int hcan2_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj); +int hcan2_filtch_rq(struct canchip_t *chip, struct msgobj_t * obj); + +int hcan2_register(struct chipspecops_t *chipspecops); +int hcan2_fill_chipspecops(struct canchip_t *chip); + +int hcan2_reset_chip(struct canchip_t *chip); + + +extern inline void can_write_reg_w(const struct canchip_t *pchip, uint16_t data, unsigned reg) +{ + can_ioptr_t address = pchip->chip_base_addr + reg; + #ifndef CONFIG_OC_LINCAN_DYNAMICIO + writew(data,address); + #else /*CONFIG_OC_LINCAN_DYNAMICIO*/ + pchip->write_register(data, address); + #endif /*CONFIG_OC_LINCAN_DYNAMICIO*/ +} + +extern inline uint16_t can_read_reg_w(const struct canchip_t *pchip, unsigned reg) +{ + can_ioptr_t address = pchip->chip_base_addr + reg; + #ifndef CONFIG_OC_LINCAN_DYNAMICIO + return readw(address); + #else /*CONFIG_OC_LINCAN_DYNAMICIO*/ + return pchip->read_register(address); + #endif /*CONFIG_OC_LINCAN_DYNAMICIO*/ +} + + +/* BasicCAN mode address map */ +#define HCAN2_MCR 0x00000000 /* Master control register */ +#define HCAN2_GSR 0x00000002 /* General status register */ +#define HCAN2_BCR1 0x00000004 /* Bit configuration register 1 */ +#define HCAN2_BCR0 0x00000006 /* Bit configuration register 0 */ +#define HCAN2_IRR 0x00000008 /* Interrupt request register */ +#define HCAN2_IMR 0x0000000a /* Interrupt mask register */ +#define HCAN2_TECREC 0x0000000c /* 15:8 Transmit error counter 7:0 Receive error counter */ +#define HCAN2_TXPR1 0x00000020 /* Transmit pending request register 1 */ +#define HCAN2_TXPR0 0x00000022 /* Transmit pending request register 0 */ +#define HCAN2_TXCR1 0x00000028 /* Transmit cancel register 1 */ +#define HCAN2_TXCR0 0x0000002a /* Transmit cancel register 0 */ +#define HCAN2_TXACK1 0x00000030 /* Transmit acknowledge register 1 */ +#define HCAN2_TXACK0 0x00000032 /* Transmit acknowledge register 0 */ +#define HCAN2_ABACK1 0x00000038 /* Abort acknowledge register 1 */ +#define HCAN2_ABACK0 0x0000003a /* Abort acknowledge register 0 */ +#define HCAN2_RXPR1 0x00000040 /* Receive data frame pending register 1 */ +#define HCAN2_RXPR0 0x00000042 /* Receive data frame pending register 0 */ +#define HCAN2_RFPR1 0x00000048 /* Remote frame request pending register 1 */ +#define HCAN2_RFPR0 0x0000004a /* Remote frame request pending register 0 */ +#define HCAN2_MBIMR1 0x00000050 /* Mailbox interrupt mask register 1 */ +#define HCAN2_MBIMR0 0x00000052 /* Mailbox interrupt mask register 0 */ +#define HCAN2_UMSR1 0x00000058 /* Unread message status register 1 */ +#define HCAN2_UMSR0 0x0000005a /* Unread message status register 0 */ +#define HCAN2_TCNTR 0x00000080 /* Timer counter register */ +#define HCAN2_TCR 0x00000082 /* Timer control register */ +#define HCAN2_TCMR 0x00000084 /* Timer Compare Match register */ +#define HCAN2_TDCR 0x00000086 +#define HCAN2_LOSR 0x00000088 +#define HCAN2_ICR1 0x0000008e +#define HCAN2_TCMR0 0x00000090 /* Timer compare match register */ +#define HCAN2_TCMR1 0x00000092 +#define HCAN2_TCMR2 0x00000094 +#define HCAN2_CCR 0x00000096 +#define HCAN2_CMAX 0x00000098 +#define HCAN2_TMR 0x0000009a + +/* BaudRate minimal and maximal TSEG values */ +#define TSEG_MIN 8 +#define TSEG_MAX 25 +#define TSEG1_MIN 4 +#define TSEG1_MAX 16 +#define TSEG2_MIN 2 +#define TSEG2_MAX 8 + +enum hcan2_mcr { + /* bits 15 to 8 are used for test mode */ + HCAN2_MCR_AWAKE = 1 << 7, /* Auto Wake Mode */ + HCAN2_MCR_SLEEP = 1 << 5, /* Sleep Mode */ + HCAN2_MCR_TXP = 1 << 2, /* Transmition Priority 0-message ID number priority, 1-mailbox number piority*/ + HCAN2_MCR_HALT = 1 << 1, /* Halt Request */ + HCAN2_MCR_RESET = 1 << 0, /* Reset Request */ +}; + +enum hcan2_gsr { + HCAN2_GSR_EPS = 1 << 5, /* Error Passive Status */ + HCAN2_GSR_HSS = 1 << 4, /* Halt/Sleep Status */ + HCAN2_GSR_RESET = 1 << 3, /* Reset Status */ + HCAN2_GSR_TXC = 1 << 2, /* Message Transmission Complete Flag */ + HCAN2_GSR_TXRXW = 1 << 1, /* Transmit/Receive Warning Flag */ + HCAN2_GSR_BOFF = 1 << 0, /* Buss Off Flag */ +}; + +/* IRR and IMR register */ +enum hcan2_irr { + HCAN2_IRR_TCMI = 1 << 14, /* Time Compare Match Register */ + HCAN2_IRR_TOI = 1 << 13, /* Time Overrun Interrupt */ + HCAN2_IRR_WUBA = 1 << 12, /* Wake-up on Bus Activity */ + HCAN2_IRR_MOOI = 1 << 9, /* Message Overrun/Overwrite Interrupt Flag */ + HCAN2_IRR_MBEI = 1 << 8, /* Messagebox Empty Interrupt Flag */ + HCAN2_IRR_OF = 1 << 7, /* Overload Frame */ + HCAN2_IRR_BOI = 1 << 6, /* Bus Off Interrupt Flag */ + HCAN2_IRR_EPI = 1 << 5, /* Error Passive Interrupt Flag */ + HCAN2_IRR_ROWI = 1 << 4, /* Receive Overload Warning Interrupt Flag */ + HCAN2_IRR_TOWI = 1 << 3, /* Transmit Overload Warining Interrupt Flag */ + HCAN2_IRR_RFRI = 1 << 2, /* Remote Frame Request Interrupt Flag */ + HCAN2_IRR_DFRI = 1 << 1, /* Data Frame Received Interrupt Flag */ + HCAN2_IRR_RHSI = 1 << 0, /* Reset/Halt/Sleep Interrupt Flag */ +}; + +/* Message box 0-31 */ +#define HCAN2_MB0 0x00000100 /* RECEIVE ONLY */ +#define HCAN2_MB_OFFSET 0x00000020 + +/* Message box structure offsets */ +#define HCAN2_MB_CTRL0 0x00000000 /* Control 0 */ +#define HCAN2_MB_CTRL1 0x00000002 /* Control 1 */ +#define HCAN2_MB_CTRL2 0x00000004 /* Control 2 */ +#define HCAN2_MB_TSTP 0x00000006 /* Time stamp */ +#define HCAN2_MB_DATA0 0x00000009 /* Data 0 */ +#define HCAN2_MB_DATA1 0x00000008 /* Data 1 */ +#define HCAN2_MB_DATA2 0x0000000b /* Data 2 */ +#define HCAN2_MB_DATA3 0x0000000a /* Data 3 */ +#define HCAN2_MB_DATA4 0x0000000d /* Data 4 */ +#define HCAN2_MB_DATA5 0x0000000c /* Data 5 */ +#define HCAN2_MB_DATA6 0x0000000f /* Data 6 */ +#define HCAN2_MB_DATA7 0x0000000e /* Data 7 */ +#define HCAN2_MB_MASK 0x00000010 /* Acceptance filter mask 4 bytes */ + +/* Control register 0 */ +enum hcan2_mb_ctrl0{ + HCAN2_MBCT0_STDID = 0x7ff0, /* STD ID */ + HCAN2_MBCT0_RTR = 0x0008, /* Remote transmition request 0-DataFrame 1-RemoteFrame */ + HCAN2_MBCT0_IDE = 0x0004, /* Identifier extension 0-Standard 1-Extended */ + HCAN2_MBCT0_EXTID = 0x0003 /* EXTID 17:16 */ +}; + +/* Control register 1 */ +/* whole register is EXTD ID 15:0 */ + +/* Control register 2 */ +enum hcan2_mb_ctrl2{ + HCAN2_MBCT2_NMC = 1<<13, /* New message control */ + HCAN2_MBCT2_ATX = 1<<12, /* Automatic transmition of data frame */ + HCAN2_MBCT2_DART = 1<<11, /* Disable automatic re-transmition */ + HCAN2_MBCT2_MBC = 7<<8, /* Mailbox configuration */ + HCAN2_MBCT2_CBE = 1<<5, /* CAN bus error */ + HCAN2_MBCT2_DLC = 0xf /* Data length code */ +}; + +/* MessageBox modes */ +enum hcan2_mb_mode{ + HCAN2_MBMOD_TXDR = 0, /* Transmit Data and Remote Frame */ + HCAN2_MBMOD_TXDR_RXR = 1, /* Transmit Data and Remote Frame, Receive Remote Frame */ + HCAN2_MBMOD_RXDR = 2, /* Receive Data and Remote Frame */ + HCAN2_MBMOD_RXD = 3, /* Receive Data Frame */ + HCAN2_MBMOD_TXR_RXDR = 4, /* Transmit Remote Frame, Receive Data and Remove Frame */ + HCAN2_MBMOD_TXR_RXD = 5, /* Transmit Remote Frame, Receive Data Frame */ + /* 6 is not used and prohibited */ + HCAN2_MBMOD_INNACTIVE = 7, /* Mailboc Innactive */ +}; diff --git a/lincan/include/sh7760.h b/lincan/include/sh7760.h new file mode 100644 index 0000000..914d848 --- /dev/null +++ b/lincan/include/sh7760.h @@ -0,0 +1,29 @@ +/* sh7760.h + * Header file for the Linux CAN-bus driver. + * This software is released under the GPL-License. + */ + +#define NR_82527 0 +#define NR_SJA1000 0 +#define NR_ALL 2 + +#define SH7760_CAN_IRQ 56 +#define SH7760_CAN_CHIP_OFFSET 0x10000 +#define SH7760_CAN_CLOCK 33333330 /* 33.3 MHz */ + +#define IO_RANGE 0x10000 + +/* static CAN_DEFINE_SPINLOCK(sh7760_port_lock); */ + + + +int sh7760_request_io(struct candevice_t *candev); +int sh7760_release_io(struct candevice_t *candev); +int sh7760_reset(struct candevice_t *candev); +int sh7760_init_hw_data(struct candevice_t *candev); +int sh7760_init_chip_data(struct candevice_t *candev, int chipnr); +int sh7760_init_obj_data(struct canchip_t *chip, int objnr); +int sh7760_program_irq(struct candevice_t *candev); +void sh7760_write_register(unsigned data, can_ioptr_t address); +unsigned sh7760_read_register(can_ioptr_t address); + diff --git a/lincan/src/hcan2.c b/lincan/src/hcan2.c new file mode 100644 index 0000000..96b1043 --- /dev/null +++ b/lincan/src/hcan2.c @@ -0,0 +1,991 @@ +/* hcan2.c + * Linux CAN-bus device driver. + * This software is released under the GPL-License. + */ + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" +#include "../include/hcan2.h" + +#define myDEBUG 0 + +#if myDEBUG + #define DEBUGMSG(fmt,args...) can_printk(KERN_ERR "lincan (debug): " fmt,##args) +#endif + + +#define MAX_TRANSMIT_WAIT_LOOPS 20 +#define MAX_SETTING_WAIT_LOOPS 25 /* maximal loop count while checking Chip reply to action */ +#define MAX_IRQ_WAIT_LOOPS 25 + +void hcan2_irq_read_handler(struct canchip_t *chip, struct msgobj_t *obj); +void hcan2_irq_write_handler(struct canchip_t *chip, struct msgobj_t *obj); + +void hcan2_clear_irq_flags(struct canchip_t *chip); +void hcan2_clear_mbox(struct canchip_t *chip, int msgobj_idx); + +void hcan2_setup_mbox4write(struct msgobj_t * obj, struct canmsg_t * msg); +void hcan2_setup_mbox4write_data(struct msgobj_t * obj, struct canmsg_t * msg); +void hcan2_setup_mbox4read(struct msgobj_t * obj); + +void hcan2_setup_ctrl_regs(struct canmsg_t * msg, uint16_t * ctrl0, uint16_t * ctrl1, uint16_t * ctrl2); + +int hcan2_compare_msg(struct msgobj_t * obj, struct canmsg_t * msg); + +/* Enable folowing IRQs + * HCAN2_IRR_DFRI = Data Frame Received Interrupt Flag + * HCAN2_IRR_MBEI = Mailbox Empty Interrupt Flag + */ +uint16_t IRQs = ~(HCAN2_IRR_DFRI + HCAN2_IRR_MBEI); +/* 1 - mask interrupt, 0 - interrupt not masked */ + +int hcan2_chip_config(struct canchip_t *chip) +{ + DEBUGMSG("Configuring chip...\n"); + + if (hcan2_enable_configuration(chip)) + return -ENODEV; + + if (!chip->baudrate) + chip->baudrate=1000000; + if (hcan2_baud_rate(chip, chip->baudrate,chip->clock,0,75,0)) + return -ENODEV; + + hcan2_config_irqs(chip, IRQs); + + if (hcan2_disable_configuration(chip)) + return -ENODEV; + +/* DEBUGMSG("Chip configured\n"); */ + return 0; +} + +int hcan2_enable_configuration(struct canchip_t *chip) +{ + int i = 0; + uint16_t gsr; + + DEBUGMSG("Enabling configuration...\n"); + + /* Disable interrupt */ + can_disable_irq(chip->chip_irq); + + /* Halt mode - disable CAN activity after completing current operation */ + gsr = can_read_reg_w(chip, HCAN2_GSR); + if (gsr & (HCAN2_GSR_BOFF | HCAN2_GSR_RESET)) /* chip is already in config mode */ + return 0; + + can_write_reg_w(chip, HCAN2_MCR_HALT, HCAN2_MCR); + + /* Waits until chip enters Halt mode - finishes current TX/RX operation */ + gsr = can_read_reg_w(chip, HCAN2_GSR); + while ( !(gsr & HCAN2_GSR_HSS) && ((i++) <= MAX_SETTING_WAIT_LOOPS) ) { + udelay(200); + gsr = can_read_reg_w(chip, HCAN2_GSR); + } + + if (i >= MAX_SETTING_WAIT_LOOPS) { + CANMSG("Error entering HALT mode (enable configuration) \n"); + can_enable_irq(chip->chip_irq); + return -ENODEV; + } + +/* DEBUGMSG("Configuration mode is ENABLED\n"); */ + return 0; +} + +int hcan2_disable_configuration(struct canchip_t *chip) +{ + int i = 0; + uint16_t gsr, mcr; + + DEBUGMSG("Disabling configuration mode...\n"); + + /* Halt mode - disable CAN activity after completing current operation */ + mcr = can_read_reg_w(chip, HCAN2_MCR); + gsr = can_read_reg_w(chip, HCAN2_GSR); + + /* if configuration already disabled */ + if (!(gsr & HCAN2_GSR_HSS) && !(mcr & HCAN2_MCR_HALT)) + return 0; + + can_write_reg_w(chip, mcr & ~HCAN2_MCR_HALT, HCAN2_MCR); + + /* Waits until chip leaves Halt mode */ + gsr = can_read_reg_w(chip, HCAN2_GSR); + while ( (gsr & HCAN2_GSR_BOFF) && ((i++) <= MAX_SETTING_WAIT_LOOPS) ) { + udelay(200); + gsr = can_read_reg_w(chip, HCAN2_GSR); + } + + if (i >= MAX_SETTING_WAIT_LOOPS) { + CANMSG("Error leaving HALT mode (enable configuration) \n"); + return -ENODEV; + } + + /* Enable interrupt */ + can_enable_irq(chip->chip_irq); + +/* DEBUGMSG("Configuration mode is DISABLED\n"); */ + return 0; +} + +/* ********************************************* */ +int hcan2_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw, int sampl_pt, int flags) +{ + /* Set communication parameters. + * param rate baud rate in Hz + * param clock frequency of hcan2 clock in Hz (on sh7760 most probably 27.5MHz) + * param sjw synchronization jump width (0-3) prescaled clock cycles + * param sampl_pt sample point in % (0-100) sets (TSEG1 + 1)/(TSEG1 + TSEG2 + 1) ration + * param flags fields BTR1_SAM, OCMODE, OCPOL, OCTP, OCTN, CLK_OFF, CBP + */ + + + /* rate = clock / ((tseg1 + tseg2 + 1) * brp ) */ + + int best_error = 1000000000, error; + int best_tseg = 0, best_brp = 0, best_rate = 0, brp = 0; + int tseg, tseg1 = 0, tseg2 = 0; /* tseg = TSEG1 + TSEG2 + 1*/ + uint16_t bcr0 = 0, bcr1 = 0; + + DEBUGMSG("Seting Baud rate...\n"); + + for (tseg = TSEG_MIN; tseg <= TSEG_MAX; tseg++) + { + brp = 10 * clock/(tseg * rate); + brp = brp % 10 > 4 ? brp / 10 + 1: brp / 10; /* round */ + + if (brp == 0 || brp > 256) + continue; + + error = rate - clock/(brp * tseg); + if (error < 0) + error = -error; + if (error <= best_error) { + best_error = error; + best_tseg = tseg; + best_brp = brp; + best_rate = clock/(brp * tseg); + } + } + + tseg2 = best_tseg - (sampl_pt * best_tseg)/100; + if (tseg2 < TSEG2_MIN) /* tseg2 <= sjw +1 , TSEG2_MIN = 4 */ + tseg2 = TSEG2_MIN; + if (tseg2 > TSEG2_MAX) + tseg2 = TSEG2_MAX; + tseg1 = best_tseg - tseg2 - 1; + if (tseg1 > TSEG1_MAX) { + tseg1 = TSEG1_MAX; + tseg2 = best_tseg - tseg1 - 1; + } + + if (best_error && (rate/best_error < 10)) { + CANMSG("baud rate %d is not possible with %d Hz clock\n", + rate, clock); + CANMSG("%d bps. brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d\n", + best_rate, best_brp, best_tseg, tseg1, tseg2); + return -EINVAL; + } + + DEBUGMSG("Setting %d bps.\n", best_rate); +/* DEBUGMSG("brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d, sampl_pt=%d\n", + best_brp, best_tseg, tseg1, tseg2, + (100 * tseg1 / best_tseg)); +*/ + /* + * EG = 0b0 - Resynchronization at falling edge + * BSP = 0b00 - bit sampling at one point (end of TSEG1) + */ + + bcr1 = (((tseg1 - 1) & 0x000f) << 12) + (((tseg2 - 1) & 0x0007) << 8) + ((sjw & 0x0003) << 4); + bcr0 = (best_brp - 1) & 0x00ff; + + hcan2_set_btregs(chip, bcr0, bcr1); + + hcan2_disable_configuration(chip); + +/* DEBUGMSG("Baud rate set successfully\n"); */ + return 0; +} + +int hcan2_set_btregs(struct canchip_t *chip, unsigned short bcr0, unsigned short bcr1) +{ +/* DEBUGMSG("Seting BCR0 and BCR1.\n"); */ + + /* masks words to correct format */ + bcr0 &= 0x00ff; + bcr1 &= 0xf733; + + can_write_reg_w(chip, bcr1, HCAN2_BCR1); + can_write_reg_w(chip, bcr0, HCAN2_BCR0); + +/* DEBUGMSG("BCR0 and BCR1 successfully set.\n"); */ + return 0; +} + +/* ********************************************* */ +int hcan2_start_chip(struct canchip_t *chip) +{ +/* DEBUGMSG("Starting chip %d...\n", chip->chip_idx); */ + + /* Stop chip turns chip to HALT mode - traffic on CAN bus is ignored after completing curent operation. + * Start chip only turn chip back from HALT state - using disable_config + */ + hcan2_disable_configuration(chip); + + DEBUGMSG("Chip [%d] started\n", chip->chip_idx); + return 0; +} + +int hcan2_stop_chip(struct canchip_t *chip) +{ +/* DEBUGMSG("Stopping chip %d...\n", chip->chip_idx); */ + + /* Stop chip turns chip to HALT mode - traffic on CAN bus is ignored after completing curent operation. + * - using enable_config + */ + hcan2_enable_configuration(chip); + + DEBUGMSG("Chip [%d] stopped\n", chip->chip_idx); + return 0; +} + +int hcan2_attach_to_chip(struct canchip_t *chip) +{ +/* DEBUGMSG("Attaching to chip %d.\n", chip->chip_idx); */ + + /* initialize chip */ + + if (hcan2_enable_configuration(chip)) + return -ENODEV; + + /* Clear all Mailboxes */ + if (hcan2_clear_objects(chip)) + return -ENODEV; + + /* set Baudrate and Interrupts */ + if (hcan2_chip_config(chip)) + return -ENODEV; + + if (hcan2_disable_configuration(chip)) + return -ENODEV; + + /* Enable interrupt */ + can_enable_irq(chip->chip_irq); + can_enable_irq(chip->chip_irq); + + CANMSG("Successfully attached to chip [%02d].\n", chip->chip_idx); + return 0; +} + +int hcan2_release_chip(struct canchip_t *chip) +{ + hcan2_stop_chip(chip); + can_disable_irq(chip->chip_irq); + + hcan2_clear_objects(chip); + + DEBUGMSG("Chip released [%02d]\n", chip->chip_idx); + return 0; +} + +/* ********************************************* */ +int hcan2_standard_mask(struct canchip_t *chip, unsigned short code, unsigned short mask) +{ + uint16_t ctrl0, lafm0; + struct msgobj_t * obj; + int obj_idx = (int) (chip->chip_data); + + if (code & 0x1ffff800) + return hcan2_extended_mask(chip, code, mask); + + + if (obj_idx > 0 && obj_idx <= 32) + obj = chip->msgobj[obj_idx - 1]; + else + return -ENODEV; + + chip->chip_data = (void*)0; /* reset mbox number */ + + + ctrl0 = ((code & 0x07ff) << 4); + lafm0 = ((mask & 0x07ff) << 4); + + can_write_reg_w(chip, ctrl0, (int) obj->obj_base_addr + HCAN2_MB_CTRL0); + can_write_reg_w(chip, 0x0000, (int) obj->obj_base_addr + HCAN2_MB_CTRL1); + can_write_reg_w(chip, lafm0, (int) obj->obj_base_addr + HCAN2_MB_MASK); + can_write_reg_w(chip, 0xffff, (int) obj->obj_base_addr + HCAN2_MB_MASK + 2); + + DEBUGMSG("Set standard_mask [id:0x%04x, m:0x%04x]\n", code, mask); + return 0; +} + +int hcan2_extended_mask(struct canchip_t *chip, unsigned long code, unsigned long mask) +{ + uint16_t ctrl0, ctrl1, lafm0, lafm1; + + struct msgobj_t * obj; + + int obj_idx = (int) (chip->chip_data); + + if (obj_idx > 0 && obj_idx <= 32) + obj = chip->msgobj[obj_idx - 1]; + else + return -ENODEV; + + chip->chip_data = (void*)0; /* reset mbox number */ + + ctrl0 = ((code & 0x1ffc0000) >> 14); + ctrl0 |=((code & 0x00030000) >> 16); + ctrl0 |= HCAN2_MBCT0_IDE; /* set IDE flag */ + ctrl1 = (code & 0x0000ffff); + + lafm0 = ((mask & 0x1ffc0000) >> 14); + lafm0 |=((mask & 0x00030000) >> 16); + lafm1 = (mask & 0x0000ffff); + + can_write_reg_w(chip, ctrl0, (int) obj->obj_base_addr + HCAN2_MB_CTRL0); + can_write_reg_w(chip, ctrl1, (int) obj->obj_base_addr + HCAN2_MB_CTRL1); + can_write_reg_w(chip, lafm0, (int) obj->obj_base_addr + HCAN2_MB_MASK); + can_write_reg_w(chip, lafm1, (int) obj->obj_base_addr + HCAN2_MB_MASK + 2); + + DEBUGMSG("Set extended_mask [id:0x%08x, m:0x%08x]\n", (uint32_t)code, (uint32_t)mask); + + return 0; +} + +/* ********************************************* */ +int hcan2_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj) +{ + DEBUGMSG("Pre read config\n"); + + hcan2_enable_configuration(chip); + + /* clears mailbox and setup LFA to accept all Exted Messages */ + hcan2_setup_mbox4read(obj); + + hcan2_disable_configuration(chip); + + return 0; +} + +int hcan2_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg) +{ + DEBUGMSG("Pre write config\n"); + + /* change Mailbox header only if neccessary */ + /* otherwise change only data */ + if (hcan2_compare_msg(obj, msg)) + { + if (hcan2_enable_configuration(chip)) + return -ENODEV; + + hcan2_setup_mbox4write(obj, msg); + + if (hcan2_disable_configuration(chip)) + return -ENODEV; + } + else + hcan2_setup_mbox4write_data(obj, msg); + + return 0; +} + +int hcan2_send_msg(struct canchip_t *chip, struct msgobj_t *obj, struct canmsg_t *msg) +{ + unsigned obj_bit; + int i; + int b_addr = ((obj->object - 1) / 16) * (-2); + + obj_bit = (1 << ((obj->object - 1) % 16)); + +/* CANMSG("Sending message [obj: %d]\n", obj->object - 1); */ + + can_write_reg_w(chip, obj_bit, b_addr + HCAN2_TXPR0); + + /* Waits until chip sends message */ + i = 0; + while (hcan2_check_tx_stat(chip) && ((i++) <= MAX_TRANSMIT_WAIT_LOOPS)) + udelay(200); + + if (i >= MAX_TRANSMIT_WAIT_LOOPS) { + CANMSG("Failed while waiting for Transmitt finished\n"); + return -ENODEV; + } + +/* CANMSG("Message sent [obj: %d]\n", obj->object - 1); */ + return 0; +} + +int hcan2_remote_request(struct canchip_t *chip, struct msgobj_t *obj) +{ + CANMSG("hcan2_remote_request not implemented\n"); + return -ENOSYS; +} + +/* ********************************************* */ +int hcan2_irq_handler(int irq, struct canchip_t *chip) +{ + uint16_t irq_reg, idx; + short loop_cnt = MAX_IRQ_WAIT_LOOPS; + uint32_t rxdf, txdf; + struct msgobj_t * obj; + + irq_reg = can_read_reg_w(chip, HCAN2_IRR); + DEBUGMSG("irq: %d, chip base addr: 0x%08x\n", irq, (uint32_t)chip->chip_base_addr); +/* DEBUGMSG("IRQ Handler: HCAN2_IRR: 0x%04x\n", irq_reg); */ + + do { + + if(!loop_cnt--) { + CANMSG("hcan2_irq_handler IRQ %d stuck\n", irq); + return CANCHIP_IRQ_STUCK; + } + + if (irq_reg & HCAN2_IRR_DFRI) + { /* Received message */ + rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) + + can_read_reg_w(chip, HCAN2_RXPR0); + + DEBUGMSG("Received message [0x%08x]\n", rxdf); + + while (rxdf) { + /* find the message object */ + for (idx = 0; (idx < chip->max_objects) && !(rxdf & (1<max_objects) + hcan2_irq_read_handler(chip, chip->msgobj[idx]); + else + break; + + /* clear RXPR flag for this msgobj */ + can_write_reg_w(chip, (1 << (idx % 16)), HCAN2_RXPR0 - 2*(idx / 16)); + + rxdf = (can_read_reg_w(chip, HCAN2_RXPR1) << 16) + + can_read_reg_w(chip, HCAN2_RXPR0); + } + + /* reset HCAN2_IRR_DFRI flag */ + can_write_reg_w(chip, irq_reg & ~HCAN2_IRR_DFRI, HCAN2_IRR); + } + + if (irq_reg & (HCAN2_IRR_MBEI)) + { + /* Mailbox empty - after message was sent */ + txdf = (can_read_reg_w(chip, HCAN2_TXACK1) << 16) + + can_read_reg_w(chip, HCAN2_TXACK0); + + /* find the message object */ + for (idx = 0; (idx < chip->max_objects) && !(txdf & (1<= chip->max_objects) { + /* IRQ is caused by Aborted transmition */ + can_write_reg_w(chip, 0xffff, HCAN2_ABACK0); + can_write_reg_w(chip, 0xffff, HCAN2_ABACK1); + return CANCHIP_IRQ_HANDLED; + } + + obj = chip->msgobj[idx]; + + /* Clear TXACK flag */ + can_write_reg_w(chip, 1 << (idx % 16), HCAN2_TXACK0 - 2 * (idx / 16)); + + /* sends message */ + hcan2_wakeup_tx(chip, obj); + } + + irq_reg=can_read_reg_w(chip, HCAN2_IRR); + } while(irq_reg & (HCAN2_IRR_MBEI | HCAN2_IRR_DFRI)); + + /* reset ALL tinterrupt flags */ + can_write_reg_w(chip, irq_reg, HCAN2_IRR); + + return CANCHIP_IRQ_HANDLED; +} + +int hcan2_irq_accept(int irq, struct canchip_t *chip) +{ + CANMSG("hcan2_irq_accept NOT IMPLEMENTED\n"); + return -ENOSYS; +} + +int hcan2_config_irqs(struct canchip_t *chip, short irqs) +{ + hcan2_clear_irq_flags(chip); + + can_write_reg_w(chip, irqs, HCAN2_IMR); + + /* allow all mailboxes to generate IRQ */ + can_write_reg_w(chip, 0, HCAN2_MBIMR0); + can_write_reg_w(chip, 0, HCAN2_MBIMR1); + +/* CANMSG("IRQ Mask set [0x%02x]\n", irqs); */ + return 0; +} + + +/* ********************************************* */ +int hcan2_clear_objects(struct canchip_t *chip) +{ + int i; + for (i = 0; i < chip->max_objects; i++) + hcan2_clear_mbox(chip, i); + + return 0; +} + +int hcan2_check_tx_stat(struct canchip_t *chip) +{ +/* DEBUGMSG("Check TX stat\n"); */ + /* If Transmition is complete return 0 - no error */ + if (can_read_reg_w(chip, HCAN2_GSR) & HCAN2_GSR_TXC) + return 0; + else + return 1; +} + +int hcan2_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj) +{ + DEBUGMSG("WakeUP TX\n"); + + if (obj->object == 1) /* msgbox 0 cant transmit only receive ! */ + return -ENODEV; + + can_preempt_disable(); + + can_msgobj_set_fl(obj,TX_REQUEST); + while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){ + can_msgobj_clear_fl(obj,TX_REQUEST); + + hcan2_irq_write_handler(chip, obj); + + can_msgobj_clear_fl(obj,TX_LOCK); + if(!can_msgobj_test_fl(obj,TX_REQUEST)) break; + } + + can_preempt_enable(); + +/* DEBUGMSG("WakeUP TX - END\n"); */ + return 0; +} + +int hcan2_filtch_rq(struct canchip_t *chip, struct msgobj_t * obj) +{ + struct canfilt_t filter; + + +#if myDEBUG + int num = canqueue_ends_filt_conjuction(obj->qends, &filter); +#else + canqueue_ends_filt_conjuction(obj->qends, &filter); +#endif + + /* in structure chip->chip_data is Mailbox number */ + chip->chip_data = (void*)(obj->object); + + /* HCAN2 uses oposite logic for LAFM: 1-ignore bit, 0-use bit as mask */ + + DEBUGMSG("CNT: %d ID: 0x%08x MASK: 0x%08x\n", num, (uint32_t) (filter.id) & 0x1fffffff, (uint32_t) (~filter.mask) & 0x1fffffff); + + if (filter.flags & MSG_EXT) /* Extended ID */ + return hcan2_extended_mask(chip, filter.id, ~filter.mask); + else /* Standard ID */ + return hcan2_standard_mask(chip, filter.id, ~filter.mask); +} + +/* ********************************************* */ +void hcan2_irq_read_handler(struct canchip_t *chip, struct msgobj_t *obj) +{ + int i, len; + unsigned ctrl0, ctrl2, data; + unsigned long flag_addr; + uint16_t mb_offset; + + + mb_offset = (int ) obj->obj_base_addr; + +/* DEBUGMSG("------IRQ Read Handler\n"); */ + + ctrl0 = can_read_reg_w(chip, mb_offset + HCAN2_MB_CTRL0); + ctrl2 = can_read_reg_w(chip, mb_offset + HCAN2_MB_CTRL2); + + obj->rx_msg.length = len = ctrl2 & HCAN2_MBCT2_DLC; + obj->rx_msg.flags = (ctrl0 & HCAN2_MBCT0_RTR) ? MSG_RTR : 0; + obj->rx_msg.cob = obj->object - 1; + + /* get ID of received message */ + if (ctrl0 & HCAN2_MBCT0_IDE) + { + DEBUGMSG("EXTENDED ID\n"); + obj->rx_msg.id = (ctrl0 & HCAN2_MBCT0_STDID) << (18 - 4); + obj->rx_msg.id |= can_read_reg_w(chip, mb_offset + HCAN2_MB_CTRL1); + obj->rx_msg.id |= ((ctrl0 & HCAN2_MBCT0_EXTID) << 16); + } + else + obj->rx_msg.id = (ctrl0 & HCAN2_MBCT0_STDID)>>4; + + + if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH; + for (i = 0; i < len; i++) + { + /* rcanqueue_ends_filt_conjuctionead 16bit data - two data bytes*/ + data = can_read_reg_w(chip, (int) obj->obj_base_addr + HCAN2_MB_DATA1 + i); + obj->rx_msg.data[i] = (data & 0xff00) >> 8; + if (++i < len) obj->rx_msg.data[i] = data & 0x00ff; + } + + /* Computes correct offset address of register from MSGBOX_IDX and RTR flag + * result is one of these: + * HCAN2_RXPR1, HCAN2_RXPR0, HCAN2_RFPR1, HCAN2_RFPR0 + */ + flag_addr = ((ctrl0 & HCAN2_MBCT0_RTR) << 3) + HCAN2_RXPR0 - ((obj->object - 1) / 16) * 2; + + /* Reset flag by writing 1 to its position */ + can_write_reg_w(chip, (1 << ((obj->object - 1) % 16)), flag_addr); + + /* fill CAN message timestamp */ + can_filltimestamp(&obj->rx_msg.timestamp); + + canque_filter_msg2edges(obj->qends, &obj->rx_msg); +} + +void hcan2_irq_write_handler(struct canchip_t *chip, struct msgobj_t *obj) +{ + int cmd; + + 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; + } + + cmd = canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot); + if(cmd < 0) + return; + + 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; + } +} + +/* ********************************************* */ +int hcan2_register(struct chipspecops_t *chipspecops) +{ + chipspecops->chip_config = hcan2_chip_config; + chipspecops->enable_configuration = hcan2_enable_configuration; + chipspecops->disable_configuration = hcan2_disable_configuration; + + chipspecops->baud_rate = hcan2_baud_rate; + chipspecops->set_btregs = hcan2_set_btregs; + + chipspecops->start_chip = hcan2_start_chip; + chipspecops->stop_chip = hcan2_stop_chip; + chipspecops->attach_to_chip = hcan2_attach_to_chip; + chipspecops->release_chip = hcan2_release_chip; + + chipspecops->standard_mask = hcan2_standard_mask; + chipspecops->extended_mask = hcan2_extended_mask; + chipspecops->message15_mask = NULL; /* hcan2_message15_mask; */ + + chipspecops->pre_read_config = hcan2_pre_read_config; + chipspecops->pre_write_config = hcan2_pre_write_config; + chipspecops->send_msg = hcan2_send_msg; + chipspecops->remote_request = hcan2_remote_request; + + chipspecops->irq_handler = hcan2_irq_handler; + chipspecops->irq_accept = NULL; /* hcan2_irq_accept; */ + chipspecops->config_irqs = hcan2_config_irqs; + + chipspecops->clear_objects = hcan2_clear_objects; + chipspecops->check_tx_stat = hcan2_check_tx_stat; + chipspecops->wakeup_tx = hcan2_wakeup_tx; + chipspecops->filtch_rq = hcan2_filtch_rq; + return 0; +} + +int hcan2_fill_chipspecops(struct canchip_t *chip) +{ + chip->chip_type = "hcan2"; + chip->max_objects = 32; + chip->write_register = chip->hostdevice->hwspecops->write_register; + chip->read_register = chip->hostdevice->hwspecops->read_register; + + /* + chip->flags; + chip->baudrate; + chip->msgobj; + chip->chip_data; + chip->chip_lock; + + chip->sja_cdr_reg; + chip->sja_ocr_reg; + chip->int_cpu_reg; + chip->int_clk_reg; + chip->int_bus_reg; + + #ifdef CAN_WITH_RTL + chip->worker_thread; + chip->pend_flags; + #endif + */ + + hcan2_register(chip->chipspecops); + return 0; +} + + +/* ********************************************* */ +int hcan2_reset_chip(struct canchip_t *chip) +{ + /* After reset and reconfig (start_chip) Chip waits for + * 11 recessive bits to join CAN bus activity + */ + + int i; + unsigned gsr_reset; + + DEBUGMSG("Resetting HCAN2 chip %d...\n", chip->chip_idx); + + /* send Reset Request */ + can_write_reg_w(chip, HCAN2_MCR_RESET, HCAN2_MCR ); + + /* Check hardware reset status */ + i = 0; + gsr_reset = can_read_reg_w(chip, HCAN2_GSR) & HCAN2_GSR_RESET; + while (!(gsr_reset) && ((i++) <= MAX_SETTING_WAIT_LOOPS)) + { + udelay(10000); + gsr_reset = can_read_reg_w(chip, HCAN2_GSR) & HCAN2_GSR_RESET; + } + + if (i >= MAX_SETTING_WAIT_LOOPS) { + CANMSG("Reset status timeout! (enter Reset Mode)\n"); + return -ENODEV; + } + + /* Clear Reset request flag */ + can_write_reg_w(chip, can_read_reg_w(chip, HCAN2_MCR) & (~HCAN2_MCR_RESET), HCAN2_MCR); + + + /* Clear Reset Interrupt Flag IRR 0 */ + can_write_reg_w(chip, HCAN2_IRR_RHSI, HCAN2_IRR); + +/* DEBUGMSG("Chips reset status ok.\n"); */ + + return 0; +} + +/* !!! Functions below doesn't call enable/disable chip config !!! */ +/* !!! Usable only in block, where enable/diable config is called explicitly !!! */ +void hcan2_clear_irq_flags(struct canchip_t *chip) +{ + uint16_t irr; + DEBUGMSG("Clearing IRQ flags...\n"); + DEBUGMSG("IRR: %04x\n",can_read_reg_w(chip, HCAN2_IRR)); + + irr = HCAN2_IRR_TCMI | HCAN2_IRR_TOI | HCAN2_IRR_WUBA | + HCAN2_IRR_OF | HCAN2_IRR_BOI | HCAN2_IRR_EPI | + HCAN2_IRR_ROWI | HCAN2_IRR_TOWI | HCAN2_IRR_RHSI; + can_write_reg_w(chip, irr, HCAN2_IRR); + + /* Other IRQ flags are cleared through other registers - see below */ + + /* Meseage Overrun/Overwrite interrupt */ + can_write_reg_w(chip, 0, HCAN2_UMSR0); + can_write_reg_w(chip, 0, HCAN2_UMSR1); + + /* Mailbox Empty Interrupt */ + can_write_reg_w(chip, 0, HCAN2_TXACK0); + can_write_reg_w(chip, 0, HCAN2_TXACK1); + can_write_reg_w(chip, 0, HCAN2_ABACK0); + can_write_reg_w(chip, 0, HCAN2_ABACK1); + + /* Remote Frame Request Interrupt */ + can_write_reg_w(chip, 0, HCAN2_RFPR0); + can_write_reg_w(chip, 0, HCAN2_RFPR1); + + /* Data Frame Received Interupt Flag */ + can_write_reg_w(chip, 0, HCAN2_RXPR0); + can_write_reg_w(chip, 0, HCAN2_RXPR1); + + DEBUGMSG("clear_irq_flags - OK\n"); +} + +void hcan2_clear_mbox(struct canchip_t *chip, int msgobj_idx) +{ + unsigned long mb_start_addr = HCAN2_MB0 + msgobj_idx * HCAN2_MB_OFFSET; + +/* DEBUGMSG("Clearing message object %d\n", msgobj_idx); */ + + /* STDID = 0 + * Standard Identifier format (0) + * Data Frame (0) + * EXTID{17,16} = 0 + */ + can_write_reg_w(chip, 0, mb_start_addr + HCAN2_MB_CTRL0); + + /* EXTID {15:0} = 0 */ + can_write_reg_w(chip, 0, mb_start_addr + HCAN2_MB_CTRL1); + + /* NMC: overwrite stored message (1) + * ATDF: No message is transmited after receiving Remote Frame (0) + * DARTX: disable Automatic Retransmition: yes (1) + * MBC = 111 - not used: HCAN2_MBCT2_MBC default value correspond to 'Not Used' + * CAN Bus error - 0 + * Data Length = 0 + */ + can_write_reg_w(chip, (uint16_t) (HCAN2_MBCT2_NMC | HCAN2_MBCT2_DART | HCAN2_MBCT2_MBC), mb_start_addr + HCAN2_MB_CTRL2); + + /* TimeStamp */ + can_write_reg_w(chip, 0, mb_start_addr + HCAN2_MB_TSTP); + + /* Data: all bytes 0xff */ + can_write_reg_w(chip, (uint16_t) 0xffff, mb_start_addr + HCAN2_MB_DATA0); + can_write_reg_w(chip, (uint16_t) 0xffff, mb_start_addr + HCAN2_MB_DATA2); + can_write_reg_w(chip, (uint16_t) 0xffff, mb_start_addr + HCAN2_MB_DATA4); + can_write_reg_w(chip, (uint16_t) 0xffff, mb_start_addr + HCAN2_MB_DATA6); + + /* Local Acceptance Filter Mask - all bits of STDID and EXTID must match values set in mailbox */ + can_write_reg_w(chip, 0, mb_start_addr + HCAN2_MB_MASK); + can_write_reg_w(chip, 0, mb_start_addr + HCAN2_MB_MASK + 2); /* Mask is 4 bytes */ + + + DEBUGMSG("Mailbox [%d] cleared.\n", msgobj_idx); +} + +void hcan2_setup_mbox4write(struct msgobj_t * obj, struct canmsg_t * msg) +{ + int mb_offset; + uint16_t ctrl0, ctrl1, ctrl2; + + struct canchip_t * chip = obj->hostchip; + + DEBUGMSG("Change Header\n"); + + mb_offset = (int) obj->obj_base_addr; + + hcan2_setup_ctrl_regs(msg, &ctrl0, &ctrl1, &ctrl2); + + can_write_reg_w(chip, ctrl0, mb_offset + HCAN2_MB_CTRL0); + can_write_reg_w(chip, ctrl1, mb_offset + HCAN2_MB_CTRL1); /* set 0 if not using EXT format */ + can_write_reg_w(chip, ctrl2, mb_offset + HCAN2_MB_CTRL2); + + /* data */ + hcan2_setup_mbox4write_data(obj, msg); +} + +void hcan2_setup_mbox4write_data(struct msgobj_t * obj, struct canmsg_t * msg) +{ + int len,i, mb_offset; + uint16_t data; + + struct canchip_t * chip = obj->hostchip; + + DEBUGMSG("Change Data\n"); + + mb_offset = (int) obj->obj_base_addr; + + len = msg->length; + if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH; + + for (i = 0; i < len; i+=2) + { + data = (msg->data[i] << 8) + (i+1 < len ? msg->data[i+1] : 0); + can_write_reg_w(chip, data, mb_offset + HCAN2_MB_DATA1 + i); + } +} + +void hcan2_setup_mbox4read(struct msgobj_t * obj) +{ + struct canchip_t * chip = obj->hostchip; + + hcan2_clear_mbox(chip, obj->object - 1); + + // in structure chip->chip_data is Mailbox number + chip->chip_data = (void*)(obj->object); + hcan2_extended_mask(chip, 2048, 0x1fffffff); /* accept all */ + + can_write_reg_w(chip, HCAN2_MBCT2_DART + (HCAN2_MBMOD_RXDR << 8), + (int) obj->obj_base_addr + HCAN2_MB_CTRL2); +} + +void hcan2_setup_ctrl_regs(struct canmsg_t * msg, uint16_t * ctrl0, uint16_t * ctrl1, uint16_t * ctrl2) +{ + uint8_t len; + uint32_t id = msg->id; + + len = msg->length; + if(len > CAN_MSG_LENGTH) len = CAN_MSG_LENGTH; + + *ctrl0 = (msg->flags & MSG_RTR ? HCAN2_MBCT0_RTR : 0); + + if (msg->flags & MSG_EXT) + { + /* Extended ID */ + *ctrl0 |= ((id & 0x1ffc0000) << 14); + *ctrl0 |= ((id & 0x00030000) >> 16) | HCAN2_MBCT0_IDE; /* get bits {17:16} from EXTID */ + *ctrl1 = (id & 0x0000ffff); + } + else + { + /* Standard ID */ + *ctrl0 |= ((id & 0x01ff) << 4); + *ctrl1 = 0; + } + + *ctrl2 = HCAN2_MBCT2_DART + HCAN2_MBMOD_TXDR + (len & HCAN2_MBCT2_DLC); +} + +int hcan2_compare_msg(struct msgobj_t * obj, struct canmsg_t * msg) +{ + /* Check if mailbox header content change is neccessary */ + /* Comparing only Control regs */ + uint16_t ctrl0, ctrl1, ctrl2; + uint16_t mb_offset; + uint16_t c0,c1,c2; + + struct canchip_t * chip = obj->hostchip; + + mb_offset = (int) obj->obj_base_addr; + + c0 = can_read_reg_w(chip, mb_offset + HCAN2_MB_CTRL0); + c1 = can_read_reg_w(chip, mb_offset + HCAN2_MB_CTRL1); + c2 = can_read_reg_w(chip, mb_offset + HCAN2_MB_CTRL2); + + hcan2_setup_ctrl_regs(msg, &ctrl0, &ctrl1, &ctrl2); + + /* if using EXT ID conpare also ctrl1 */ + if (msg->flags & MSG_EXT && ctrl1 ^ c1) + return 1; + + + DEBUGMSG("C0 0x%04x HW: 0x%04x\n", ctrl0, c0); + DEBUGMSG("C1 0x%04x HW: 0x%04x\n", ctrl1, c1); + DEBUGMSG("C2 0x%04x HW: 0x%04x\n", ctrl2, c2); + + return ((ctrl0 ^ c0) || (ctrl2 ^ c2)); +} diff --git a/lincan/src/sh7760.c b/lincan/src/sh7760.c new file mode 100644 index 0000000..cc71444 --- /dev/null +++ b/lincan/src/sh7760.c @@ -0,0 +1,104 @@ +/* sh7760.c +* Linux CAN-bus device driver. +* This software is released under the GPL-License. +*/ + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" +#include "../include/sh7760.h" +#include "../include/hcan2.h" + +int sh7760_request_io(struct candevice_t *candev) +{ + if (!can_request_io_region(candev->io_addr, candev->nr_all_chips * IO_RANGE, DEVICE_NAME)) { + CANMSG("Unable to open port: 0x%lx\n",candev->io_addr); + return -ENODEV; + } + + DEBUGMSG("Registered IO-memory: 0x%lx - 0x%lx\n", candev->io_addr, candev->io_addr + candev->nr_all_chips * IO_RANGE - 1); + return 0; +} + +int sh7760_release_io(struct candevice_t *candev) +{ + can_release_io_region(candev->io_addr, candev->nr_all_chips * IO_RANGE); + + return 0; +} + +int sh7760_reset(struct candevice_t *candev) +{ + int i; + DEBUGMSG("Resetting HCAN2 chips ...\n"); + + for (i = 0; i < candev->nr_all_chips; i++) + { + /* !!! Assuming this card has ONLY HCAN2 chips !!! */ + if (hcan2_reset_chip(candev->chip[i])) return -ENODEV; + } + + return 0; +} + +int sh7760_init_hw_data(struct candevice_t *candev) +{ + /* candev->res_addr = RESET_ADDR; */ + candev->nr_82527_chips = NR_82527; + candev->nr_sja1000_chips = NR_SJA1000; + candev->nr_all_chips = NR_ALL; + /* candev->flags |= CANDEV_PROGRAMMABLE_IRQ; */ + + return 0; +} +int sh7760_init_chip_data(struct candevice_t *candev, int chipnr) +{ + hcan2_fill_chipspecops(candev->chip[chipnr]); + + candev->chip[chipnr]->chip_base_addr = can_ioport2ioptr(candev->io_addr) + chipnr * SH7760_CAN_CHIP_OFFSET; /* one chip with 2 interfaces */ + candev->chip[chipnr]->clock = SH7760_CAN_CLOCK; + candev->chip[chipnr]->chip_irq = SH7760_CAN_IRQ + chipnr; + candev->chip[chipnr]->hostdevice = candev; + + return 0; +} + +int sh7760_init_obj_data(struct canchip_t *chip, int objnr) +{ + chip->msgobj[objnr]->obj_base_addr = (can_ioptr_t) HCAN2_MB0 + HCAN2_MB_OFFSET * objnr; + + return 0; +} + +int sh7760_program_irq(struct candevice_t *candev) +{ + /* sh7760 doesn't use programmable interrupt */ + return 0; +} + + +void sh7760_write_register(unsigned data, can_ioptr_t address) +{ + /* address is an absolute address */ + writew(data, address); +} + +unsigned sh7760_read_register(can_ioptr_t address) +{ + /* address is an absolute address */ + return readw(address); +} + +int sh7760_register(struct hwspecops_t *hwspecops) +{ + hwspecops->request_io = sh7760_request_io; + hwspecops->release_io = sh7760_release_io; + hwspecops->reset = sh7760_reset; + hwspecops->init_hw_data = sh7760_init_hw_data; + hwspecops->init_chip_data = sh7760_init_chip_data; + hwspecops->init_obj_data = sh7760_init_obj_data; + hwspecops->program_irq = sh7760_program_irq; + hwspecops->write_register = sh7760_write_register; + hwspecops->read_register = sh7760_read_register; + return 0; +} -- 2.39.2