From 2c5dbee366e95b46de0cb95cab23d2a94a13f184 Mon Sep 17 00:00:00 2001 From: ppisa Date: Fri, 16 Jan 2004 01:53:19 +0000 Subject: [PATCH] Added support for KVASER PCICAN-S/D/Q cards. --- lincan/include/can_sysdep.h | 9 +- lincan/include/main.h | 7 + lincan/src/Makefile.omk | 2 +- lincan/src/Makefile.std | 4 +- lincan/src/boardlist.c | 6 + lincan/src/kv_pcican.c | 275 ++++++++++++++++++++++++++++++++++++ lincan/src/main.c | 1 + 7 files changed, 299 insertions(+), 5 deletions(-) create mode 100644 lincan/src/kv_pcican.c diff --git a/lincan/include/can_sysdep.h b/lincan/include/can_sysdep.h index 56cfc4e..b719236 100644 --- a/lincan/include/can_sysdep.h +++ b/lincan/include/can_sysdep.h @@ -27,12 +27,19 @@ #include #include +/*optional features*/ +#define CAN_ENABLE_KERN_FASYNC +#define CAN_ENABLE_PCI_SUPPORT + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) #include #else #include #endif +#ifdef CAN_ENABLE_PCI_SUPPORT +#include "linux/pci.h" +#endif /*CAN_ENABLE_PCI_SUPPORT*/ /* Next is not sctrictly correct, because of 2.3.0, 2.3.1, 2.3.2 kernels need next definitions too */ @@ -92,8 +99,6 @@ #define del_timer_sync del_timer #endif /* <2.4.0 */ -#define CAN_ENABLE_KERN_FASYNC - #ifndef CAN_WITH_RTL /* Standard LINUX kernel */ diff --git a/lincan/include/main.h b/lincan/include/main.h index ba01dd2..f73e967 100644 --- a/lincan/include/main.h +++ b/lincan/include/main.h @@ -83,6 +83,13 @@ struct candevice_t { struct hwspecops_t *hwspecops; struct canhardware_t *hosthardware_p; + + union { + #ifdef CAN_ENABLE_PCI_SUPPORT + struct pci_dev *pcidev; + #endif /*CAN_ENABLE_PCI_SUPPORT*/ + } sysdevptr; + }; /** diff --git a/lincan/src/Makefile.omk b/lincan/src/Makefile.omk index 674077c..69927f9 100644 --- a/lincan/src/Makefile.omk +++ b/lincan/src/Makefile.omk @@ -9,7 +9,7 @@ kernel_MODULES = lincan lincan_cards_SOURCES = pip.c pccan.c smartcan.c nsi.c cc_can104.c \ pc_i03.c pcm3680.c aim104.c m437.c pcccan.c ssv.c \ - bfadcan.c pikronisa.c virtual.c template.c + bfadcan.c pikronisa.c kv_pcican.c virtual.c template.c lincan_rtl_SOURCES = open_rtl.c close_rtl.c read_rtl.c write_rtl.c ioctl_rtl.c diff --git a/lincan/src/Makefile.std b/lincan/src/Makefile.std index 1a0f0e9..dc60238 100644 --- a/lincan/src/Makefile.std +++ b/lincan/src/Makefile.std @@ -47,7 +47,7 @@ endif #KERNEL_LOCATION=/usr/src/linux #KERNEL_LOCATION=/usr/src/linux-2.2.19 #KERNEL_LOCATION=/usr/src/linux-2.2.22 -#KERNEL_LOCATION=/usr/src/linux-2.6.0-test4 +#KERNEL_LOCATION=/usr/src/linux-2.6.0 #KERNEL_LOCATION=/home/cvs/ocera/ocera-build/kernel/linux @@ -62,7 +62,7 @@ endif SUPPORTED_CARDS = pip pccan smartcan nsi cc_can104 \ pc_i03 pcm3680 aim104 m437 pcccan ssv \ - bfadcan pikronisa virtual template + bfadcan pikronisa kv_pcican virtual template #SUPPORTED_CARDS = pcm3680 bfadcan pikronisa template diff --git a/lincan/src/boardlist.c b/lincan/src/boardlist.c index f85dcb6..342c4e6 100644 --- a/lincan/src/boardlist.c +++ b/lincan/src/boardlist.c @@ -30,6 +30,7 @@ extern int smartcan_register(struct hwspecops_t *hwspecops); extern int pccanf_register(struct hwspecops_t *hwspecops); extern int pccand_register(struct hwspecops_t *hwspecops); extern int pccanq_register(struct hwspecops_t *hwspecops); +extern int kv_pcican_register(struct hwspecops_t *hwspecops); extern int nsi_register(struct hwspecops_t *hwspecops); extern int cc104_register(struct hwspecops_t *hwspecops); extern int pci03_register(struct hwspecops_t *hwspecops); @@ -76,6 +77,11 @@ const struct boardtype_t can_boardtypes[]={ {"pccan-d", pccand_register, 2}, {"pccan-q", pccanq_register, 4}, #endif + #if defined(ENABLE_CARD_kv_pcican)&&defined(CAN_ENABLE_PCI_SUPPORT) + {"pcican-s", kv_pcican_register, 0}, + {"pcican-d", kv_pcican_register, 0}, + {"pcican-q", kv_pcican_register, 0}, + #endif #ifdef ENABLE_CARD_m437 {"m437", m437_register, 1}, #endif diff --git a/lincan/src/kv_pcican.c b/lincan/src/kv_pcican.c new file mode 100644 index 0000000..eace091 --- /dev/null +++ b/lincan/src/kv_pcican.c @@ -0,0 +1,275 @@ +/* kv_pcican.c - support for KVASER PCIcan-S/D/Q cards + * 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.2 9 Jul 2003 + */ + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" +#include "../include/sja1000p.h" + +#ifdef CAN_ENABLE_PCI_SUPPORT + +#define KV_PCICAN_PCICAN_VENDOR 0x10e8 +#define KV_PCICAN_PCICAN_ID 0x8406 + +/*AMCC 5920*/ + +#define S5920_OMB 0x0C +#define S5920_IMB 0x1C +#define S5920_MBEF 0x34 +#define S5920_INTCSR 0x38 +#define S5920_RCR 0x3C +#define S5920_PTCR 0x60 + +#define INTCSR_ADDON_INTENABLE_M 0x2000 +#define INTCSR_INTERRUPT_ASSERTED_M 0x800000 + +#define KV_PCICAN_BYTES_PER_CIRCUIT 0x20 + +// Standard value: Pushpull (OCTP1|OCTN1|OCTP0|OCTN0|OCM1) +#define KV_PCICAN_OCR_DEFAULT_STD 0xDA +// For Galathea piggyback. +#define KV_PCICAN_OCR_DEFAULT_GAL 0xDB + +/* + +You need to know the following: +" RX1 is connected to ground. +" TX1 is not connected. +" CLKO is not connected. +" Setting the OCR register to 0xDA is a good idea. + This means normal output mode , push-pull and the correct polarity. +" In the CDR register, you should set CBP to 1. + You will probably also want to set the clock divider value to 0 (meaning divide-by-2), + the Pelican bit, and the clock-off bit (you have no need for CLKOUT anyway.) + +*/ + + + +void kv_pcican_disconnect_irq(struct candevice_t *candev) +{ + unsigned long tmp; + /* Disable interrupts from card */ + tmp = inl(candev->dev_base_addr + S5920_INTCSR); + tmp &= ~INTCSR_ADDON_INTENABLE_M; + outl(tmp, candev->dev_base_addr + S5920_INTCSR); +} + +void kv_pcican_connect_irq(struct candevice_t *candev) +{ + unsigned long tmp; + /* Enable interrupts from card */ + tmp = inl(candev->dev_base_addr + S5920_INTCSR); + tmp |= INTCSR_ADDON_INTENABLE_M; + outl(tmp, candev->dev_base_addr + S5920_INTCSR); +} + + +int kv_pcican_request_io(struct candevice_t *candev) +{ + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)) + if(pci_request_region(candev->sysdevptr.pcidev, 0, "kv_pcican_s5920") != 0){ + CANMSG("Request of kv_pcican_s5920 range failed\n"); + return -ENODEV; + }else if(pci_request_region(candev->sysdevptr.pcidev, 1, "kv_pcican_io") != 0){ + CANMSG("Request of kv_pcican_io range failed\n"); + goto error_io; + }else if(pci_request_region(candev->sysdevptr.pcidev, 2, "kv_pcican_xilinx") != 0){ + CANMSG("Request of kv_pcican_xilinx range failed\n"); + goto error_xilinx; + } + #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + if(pci_request_regions(candev->sysdevptr.pcidev, "kv_pcican") != 0){ + CANMSG("Request of kv_pcican_s5920 regions failed\n"); + return -ENODEV; + } + #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + + kv_pcican_disconnect_irq(candev); + + return 0; + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)) + error_xilinx: + pci_release_region(candev->sysdevptr.pcidev, 1); + error_io: + pci_release_region(candev->sysdevptr.pcidev, 0); + #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + + return -ENODEV; +} + +int kv_pcican_release_io(struct candevice_t *candev) +{ + kv_pcican_disconnect_irq(candev); + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)) + pci_release_region(candev->sysdevptr.pcidev, 2); + pci_release_region(candev->sysdevptr.pcidev, 1); + pci_release_region(candev->sysdevptr.pcidev, 0); + #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + pci_release_regions(candev->sysdevptr.pcidev); + #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + + return 0; +} + + +void kv_pcican_write_register(unsigned char data, unsigned long address) +{ + outb(data,address); +} + +unsigned kv_pcican_read_register(unsigned long address) +{ + return inb(address); +} + +int kv_pcican_reset(struct candevice_t *candev) +{ + int i=0,chip_nr; + struct chip_t *chip; + unsigned cdr; + + DEBUGMSG("Resetting kv_pcican hardware ...\n"); + + /* Assert PTADR# - we're in passive mode so the other bits are not important */ + outl(0x80808080L, candev->dev_base_addr + S5920_PTCR); + + kv_pcican_disconnect_irq(candev); + + for(chip_nr=0;chip_nrnr_all_chips;chip_nr++){ + if(!candev->chip[chip_nr]) continue; + chip=candev->chip[chip_nr]; + + kv_pcican_write_register(MOD_RM, chip->chip_base_addr+SJAMOD); + udelay(1000); + + cdr=kv_pcican_read_register(chip->chip_base_addr+SJACDR); + kv_pcican_write_register(cdr|CDR_PELICAN, chip->chip_base_addr+SJACDR); + + kv_pcican_write_register(0, chip->chip_base_addr+SJAIER); + + i=20; + kv_pcican_write_register(0, chip->chip_base_addr+SJAMOD); + while (kv_pcican_read_register(chip->chip_base_addr+SJAMOD)&MOD_RM){ + if(!i--) return -ENODEV; + udelay(1000); + kv_pcican_write_register(0, chip->chip_base_addr+SJAMOD); + } + + cdr=kv_pcican_read_register(chip->chip_base_addr+SJACDR); + kv_pcican_write_register(cdr|CDR_PELICAN, chip->chip_base_addr+SJACDR); + + kv_pcican_write_register(0, chip->chip_base_addr+SJAIER); + + kv_pcican_read_register(chip->chip_base_addr+SJAIR); + } + + + kv_pcican_connect_irq(candev); + + return 0; +} + +int kv_pcican_init_hw_data(struct candevice_t *candev) +{ + struct pci_dev *pcidev = NULL; + int i; + + pcidev = pci_find_device(KV_PCICAN_PCICAN_VENDOR, KV_PCICAN_PCICAN_ID, pcidev); + if(pcidev == NULL) return -ENODEV; + + if (pci_enable_device (pcidev)){ + printk(KERN_CRIT "Setup of PCICAN failed\n"); + return -EIO; + } + candev->sysdevptr.pcidev=pcidev; + + for(i=0;i<3;i++){ + if(!(pci_resource_flags(pcidev,0)&IORESOURCE_IO)){ + printk(KERN_CRIT "PCICAN region %d is not IO\n",i); + return -EIO; + } + } + candev->dev_base_addr=pci_resource_start(pcidev,0); /*S5920*/ + candev->io_addr=pci_resource_start(pcidev,1); /*IO window for SJA1000 chips*/ + candev->res_addr=pci_resource_start(pcidev,2); /*XILINX board wide address*/ + + /*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/ + + if (!strcmp(candev->hwname,"pcican-s")) { + candev->nr_82527_chips=0; + candev->nr_sja1000_chips=1; + candev->nr_all_chips=1; + } + if (!strcmp(candev->hwname,"pcican-d")) { + candev->nr_82527_chips=0; + candev->nr_sja1000_chips=2; + candev->nr_all_chips=2; + } + if (!strcmp(candev->hwname,"pcican-q")) { + candev->nr_82527_chips=0; + candev->nr_sja1000_chips=4; + candev->nr_all_chips=4; + } + + return 0; +} + +int kv_pcican_init_chip_data(struct candevice_t *candev, int chipnr) +{ + + if(candev->sysdevptr.pcidev==NULL) + return -ENODEV; + + candev->chip[chipnr]->chip_irq=candev->sysdevptr.pcidev->irq; + + candev->chip[chipnr]->chip_type="sja1000p"; + candev->chip[chipnr]->chip_base_addr= + candev->io_addr+chipnr*KV_PCICAN_BYTES_PER_CIRCUIT; + candev->chip[chipnr]->flags = 0; + candev->chip[chipnr]->int_cpu_reg = 0; + candev->chip[chipnr]->int_clk_reg = 0; + candev->chip[chipnr]->int_bus_reg = 0; + candev->chip[chipnr]->sja_cdr_reg = CDR_CBP | CDR_CLK_OFF; + candev->chip[chipnr]->sja_ocr_reg = KV_PCICAN_OCR_DEFAULT_STD; + candev->chip[chipnr]->clock = 16000000; + + return 0; +} + +int kv_pcican_init_obj_data(struct chip_t *chip, int objnr) +{ + chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr; + return 0; +} + +int kv_pcican_program_irq(struct candevice_t *candev) +{ + + return 0; +} + +int kv_pcican_register(struct hwspecops_t *hwspecops) +{ + hwspecops->request_io = kv_pcican_request_io; + hwspecops->release_io = kv_pcican_release_io; + hwspecops->reset = kv_pcican_reset; + hwspecops->init_hw_data = kv_pcican_init_hw_data; + hwspecops->init_chip_data = kv_pcican_init_chip_data; + hwspecops->init_obj_data = kv_pcican_init_obj_data; + hwspecops->write_register = kv_pcican_write_register; + hwspecops->read_register = kv_pcican_read_register; + hwspecops->program_irq = kv_pcican_program_irq; + return 0; +} + + +#endif /*CAN_ENABLE_PCI_SUPPORT*/ diff --git a/lincan/src/main.c b/lincan/src/main.c index b57c79c..817d678 100644 --- a/lincan/src/main.c +++ b/lincan/src/main.c @@ -163,6 +163,7 @@ int init_module(void) if (parse_mod_parms()) return -EINVAL; + can_spin_lock_init(&canuser_manipulation_lock); canqueue_kern_initialize(); if (init_hw_struct()) -- 2.39.2