diff -Nur lincan-cvs-20070814/README lincan-cvs-20070814+esd006-untouched/README --- lincan-cvs-20070814/README 2006-07-13 12:48:07.000000000 -0800 +++ lincan-cvs-20070814+esd006-untouched/README 2007-08-14 18:10:19.000000000 -0800 @@ -267,6 +267,7 @@ - tscan1 for Technologic Systems' TS-CAN1 single SJA1000 based board - ts7kv for Technologic Systems' TS-7KV Multi-function board with SJA1000 both these cards require CONFIG_OC_LINCAN_CARD_tscan1=y +- esdpci200 for the CAN/PCI-200 card by ESD Electronics - template, for yet unsupported hardware (you need to edit src/template.c) - virtual, CAN channel for testing of software and driver without CAN hardware diff -Nur lincan-cvs-20070814/src/Makefile.omk lincan-cvs-20070814+esd006-untouched/src/Makefile.omk --- lincan-cvs-20070814/src/Makefile.omk 2007-08-05 15:22:03.000000000 -0800 +++ lincan-cvs-20070814+esd006-untouched/src/Makefile.omk 2007-08-14 18:13:37.000000000 -0800 @@ -1,6 +1,6 @@ lincan_cards_NAMES = pip pccan smartcan nsi cc_can104 ems_cpcpci \ pc_i03 pcm3680 aim104 m437 pcccan ssv bfadcan gensja1000io pikronisa eb8245 \ - kv_pcican msmcan oscar adlink7841 pcan_pci unican virtual template + kv_pcican msmcan oscar adlink7841 pcan_pci unican esdpci200 virtual template lincan_morecards_NAMES = hms30c7202_can ns_dev_can ipci165 pimx1 tscan1 nsi_canpci diff -Nur lincan-cvs-20070814/src/Makefile.std lincan-cvs-20070814+esd006-untouched/src/Makefile.std --- lincan-cvs-20070814/src/Makefile.std 2006-10-06 08:41:05.000000000 -0800 +++ lincan-cvs-20070814+esd006-untouched/src/Makefile.std 2007-08-14 18:10:56.000000000 -0800 @@ -64,7 +64,7 @@ SUPPORTED_CARDS = pip pccan smartcan nsi cc_can104 \ pc_i03 pcm3680 aim104 m437 pcccan ssv \ bfadcan pikronisa kv_pcican msmcan virtual template \ - unican unican_cl2 ems_cpcpci adlink7841 oscar + unican unican_cl2 ems_cpcpci adlink7841 oscar esdpci200 # hms30c7202_can c_can c_can_irq tscan1 # pcan_dongle diff -Nur lincan-cvs-20070814/src/boardlist.c lincan-cvs-20070814+esd006-untouched/src/boardlist.c --- lincan-cvs-20070814/src/boardlist.c 2007-08-05 15:22:03.000000000 -0800 +++ lincan-cvs-20070814+esd006-untouched/src/boardlist.c 2007-08-14 18:14:15.000000000 -0800 @@ -46,6 +46,7 @@ extern int hms30c7202_register(struct hwspecops_t *hwspecops); extern int nsi_canpci_register(struct hwspecops_t *hwspecops); extern int pcan_pci_register(struct hwspecops_t *hwspecops); +extern int esdpci200_register(struct hwspecops_t *hwspecops); const struct boardtype_t can_boardtypes[]={ #ifdef CONFIG_OC_LINCAN_CARD_template @@ -140,6 +141,9 @@ #if defined(CONFIG_OC_LINCAN_CARD_adlink7841) {"adlink7841", adlink7841_register, 0}, #endif + #if defined(CONFIG_OC_LINCAN_CARD_esdpci200) + {"esdpci200", esdpci200_register, 0}, + #endif #if defined(CONFIG_OC_LINCAN_CARD_tscan1) {"tscan1", tscan1_register, 1}, {"ts7kv", ts7kv_register, 1}, diff -Nur lincan-cvs-20070814/src/esdpci200.c lincan-cvs-20070814+esd006-untouched/src/esdpci200.c --- lincan-cvs-20070814/src/esdpci200.c 1969-12-31 14:00:00.000000000 -1000 +++ lincan-cvs-20070814+esd006-untouched/src/esdpci200.c 2007-08-14 18:10:56.000000000 -0800 @@ -0,0 +1,360 @@ +/* esdpci200.c - support for ESD Electronics' CAN/PCI-200 cards + * Linux CAN-bus device driver. + * The card support was added by Manuel Bessler + * Based on adlink7841.c and nsi_canpci.c + * This software is released under the GPL-License. + * Version lincan-0.3.3 + */ + +#include "../include/can.h" +#include "../include/can_sysdep.h" +#include "../include/main.h" +#include "../include/sja1000p.h" + +#ifdef CAN_ENABLE_PCI_SUPPORT + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)) + #define ioread32 readl + #define iowrite32 writel + #define ioread8 readb + #define iowrite8 writeb + #define wmb() + #define rmb() +#else +#endif + +#define ESDPCI200_PCI_VENDOR_ID 0x10B5 +#define ESDPCI200_PCI_PRODUCT_ID 0x9050 + +/* PCI to local bus bridge PLX9052 */ + +#define PLX9052_INTCSR 0x4c /* interrupt control register */ +#define PLX9052_CNTRL 0x50 /* control register, for software reset */ + +/* The ESD PCI/200 uses (per default) just LINTi1 (Local Interrupt 1) + * on the PLX. This means that both CAN channels' (SJA1000's) /INT pins + * are OR'ed to the LINTi1 pin (actually ANDed in the 74HC08 since both + * the SJA1000's /INT pins and the LINTi1 pin are active low). + * + * The board does have an option to route the 2nd channel to LINTi2, + * apparently just one or two resistors need to be added. + * + * LINTi2 is floating per default, so don't set its interrupt enable flag + * 'PLX9052_INTCSR_LI2EN', it'll just interrupt all the time. + */ +#define PLX9052_INTCSR_LI1EN 0x00000001 /* Local Interrupt 1 enable */ +#define PLX9052_INTCSR_LI1S 0x00000004 /* Local Interrupt 1 status */ +#define PLX9052_INTCSR_LI2EN 0x00000008 /* Local Interrupt 2 enable */ +#define PLX9052_INTCSR_LI2S 0x00000020 /* Local Interrupt 2 status */ +#define PLX9052_INTCSR_PIEN 0x00000040 /* PCI Interrupt enable */ + +#define PLX9052_CNTRL_SWRESET 0x40000000 /* PCI Adapter Software Reset to Local Bus */ + +#define IO_RANGE 0x100 +#define RESET_ADDR 0x00 + +// Standard value: Pushpull (OCTP1|OCTN1|OCPOL1|OCTP0|OCTN0|OCM1) +#define ESDPCI200_OCR_DEFAULT_STD 0xFA +/* Setting the OCR register to 0xFA is a good idea. + This means normal output mode , push-pull and the correct polarity. */ + + +void esdpci200_pci_soft_reset(struct candevice_t *candev) +{ + unsigned long reg_reset; + reg_reset = inl( candev->dev_base_addr+PLX9052_CNTRL); + reg_reset &= ~(PLX9052_CNTRL_SWRESET); + rmb(); + /* PCI Adapter Software Reset plus reset local bus */ + outl( (reg_reset | PLX9052_CNTRL_SWRESET ), candev->dev_base_addr+PLX9052_CNTRL); + wmb(); + udelay(2500); + outl(reg_reset, candev->dev_base_addr+PLX9052_CNTRL); + wmb(); + udelay(2500); +} + +void esdpci200_disconnect_irq(struct candevice_t *candev) +{ + /* writing 0x0 into the PLX's INTCSR register disables interrupts */ + /* 0x0 is also the value in the register after a power-on reset */ + outl(0x0, candev->dev_base_addr + PLX9052_INTCSR); + DEBUGMSG("disabled interrupts on the PLX\n"); +} + +void esdpci200_connect_irq(struct candevice_t *candev) +{ + /* enable interrupts for the SJA1000's, enable PCI interrupts */ + outl( PLX9052_INTCSR_LI1EN | PLX9052_INTCSR_PIEN, + candev->dev_base_addr+PLX9052_INTCSR); + DEBUGMSG("enabled interrupts on the PLX\n"); +} + +int esdpci200_irq_handler(int irq, struct canchip_t *chip) +{ + int retcode; + unsigned long it_reg; + struct candevice_t *candev; + candev = chip->hostdevice; + retcode = CANCHIP_IRQ_NONE; + //DEBUGMSG("Starting to handle an IRQ\n"); + it_reg = inl(candev->dev_base_addr+PLX9052_INTCSR); + rmb(); + if((it_reg & (PLX9052_INTCSR_LI1S | PLX9052_INTCSR_LI1EN) ) + == (PLX9052_INTCSR_LI1S | PLX9052_INTCSR_LI1EN) ) + { /*interrupt enabled and active */ + int chipnum; + for(chipnum=0; chipnum < candev->nr_sja1000_chips; chipnum++) + { + if(sja1000p_irq_handler(irq, candev->chip[chipnum]) == CANCHIP_IRQ_NONE) + { /* since both chips use the same IRQ and the same LINTi on the PLX, + we need manually do 'interrupt sharing' on the boardlevel + by checking all chips one-by-one */ + continue; + } + else + { + retcode=CANCHIP_IRQ_HANDLED; + } + } + if( retcode != CANCHIP_IRQ_HANDLED ) + {/* None of the chips felt they were responsible for this IRQ... + so it appears we have problems with the IRQ */ + it_reg &= ~(PLX9052_INTCSR_LI1EN); + //Either we have a problem with IRQ malfunctions, or our IRQ is shared with some other device. + // + //not actually disabled, unless outl() below is uncommented + //outl(it_reg,(void*)(candev->dev_base_addr+PLX9052_INTCSR)); + //CANMSG("CAN Interrupt disabled due to malfunction\n"); + } + } + return retcode; +} + +int esdpci200_request_io(struct candevice_t *candev) +{ + return 0; +} + +int esdpci200_release_io(struct candevice_t *candev) +{ + esdpci200_disconnect_irq(candev); + esdpci200_pci_soft_reset(candev); + + iounmap((void*)candev->io_addr); + #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 esdpci200_write_register(unsigned data, unsigned long address) +{ + //writeb(data,address); + iowrite8((u8)data,(void*)address); + wmb(); +} + +unsigned esdpci200_read_register(unsigned long address) +{ + //return readb(address); + return ioread8((void*)address); +} + +int esdpci200_reset(struct candevice_t *candev) +{ + int i=0,chip_nr; + struct canchip_t *chip; + unsigned cdr; + DEBUGMSG("Resetting esdpci200 hardware ...\n"); + + esdpci200_disconnect_irq(candev); + esdpci200_pci_soft_reset(candev); + + for(chip_nr=0;chip_nrnr_all_chips;chip_nr++){ + if(!candev->chip[chip_nr]) continue; + chip=candev->chip[chip_nr]; + + esdpci200_write_register(sjaMOD_RM, chip->chip_base_addr+SJAMOD); + udelay(1000); + + cdr=esdpci200_read_register(chip->chip_base_addr+SJACDR); + esdpci200_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR); + + esdpci200_write_register(0, chip->chip_base_addr+SJAIER); + + i=20; + esdpci200_write_register(0, chip->chip_base_addr+SJAMOD); + while (esdpci200_read_register(chip->chip_base_addr+SJAMOD)&sjaMOD_RM){ + if(!i--) return -ENODEV; + udelay(1000); + esdpci200_write_register(0, chip->chip_base_addr+SJAMOD); + } + + cdr=esdpci200_read_register(chip->chip_base_addr+SJACDR); + esdpci200_write_register(cdr|sjaCDR_PELICAN, chip->chip_base_addr+SJACDR); + + esdpci200_write_register(0, chip->chip_base_addr+SJAIER); + + esdpci200_read_register(chip->chip_base_addr+SJAIR); + } + + + esdpci200_connect_irq(candev); + + return 0; +} + +int esdpci200_init_hw_data(struct candevice_t *candev) +{ + struct pci_dev *pcidev = NULL; + unsigned long bar2_addr; + + do { + pcidev = pci_find_device(ESDPCI200_PCI_VENDOR_ID, ESDPCI200_PCI_PRODUCT_ID, pcidev); + if(pcidev == NULL) return -ENODEV; + } while(can_check_dev_taken(pcidev)); + + if (pci_enable_device (pcidev)){ + printk(KERN_CRIT "Setup of ESDPCI200 failed\n"); + return -EIO; + } + candev->sysdevptr.pcidev=pcidev; + + can_spin_lock_init(&candev->device_lock); + + if(!(pci_resource_flags(pcidev, 0)&IORESOURCE_MEM)) + { + printk(KERN_CRIT "PCI200 region %d is not MEM\n",0); + return -EIO; + } + if(!(pci_resource_flags(pcidev, 1)&IORESOURCE_IO)) + { + printk(KERN_CRIT "PCI200 region %d is not IO\n",1); + return -EIO; + } + + if(!(pci_resource_flags(pcidev,2)&IORESOURCE_MEM)) + { + printk(KERN_CRIT "PCI200 region %d is not MEM\n",2); + return -EIO; + } + + + #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)) + if(pci_request_region(candev->sysdevptr.pcidev, 0, "esdpci200_plx9050") != 0){ + CANMSG("Request of esdpci200_plx9050 range failed\n"); + return -ENODEV; + }else if(pci_request_region(candev->sysdevptr.pcidev, 1, "esdpci200_io") != 0){ + CANMSG("Request of esdpci200_io range failed\n"); + pci_release_region(candev->sysdevptr.pcidev, 0); + return -ENODEV; + }else if(pci_request_region(candev->sysdevptr.pcidev, 2, "esdpci200_sja") != 0){ + CANMSG("Request of esdpci200_sja range failed\n"); + pci_release_region(candev->sysdevptr.pcidev, 1); + pci_release_region(candev->sysdevptr.pcidev, 0); + return -ENODEV; + } + #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + if(pci_request_regions(candev->sysdevptr.pcidev, "esdpci200") != 0){ + CANMSG("Request of esdpci200_plx9050 regions failed\n"); + return -ENODEV; + } + #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/ + + + candev->res_addr=RESET_ADDR; /* reset address not used currently. Not used by other Lincan parts either */ + candev->dev_base_addr=pci_resource_start(pcidev,1); /* ioports, PLX local configuration registers */ + bar2_addr = pci_resource_start(pcidev,2); + if( ! (candev->io_addr=(long)ioremap(bar2_addr, + pci_resource_len(pcidev,2)))) /*MEM window for SJA1000 chips*/ + { + CANMSG("Unable to access I/O memory at: 0x%lx\n", bar2_addr); + goto ioremap_error; + } + /*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/ + + candev->nr_82527_chips=0; + candev->nr_sja1000_chips=2; + candev->nr_all_chips=2; + + return 0; + + ioremap_error: + #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 -ENODEV; +} + +int esdpci200_init_chip_data(struct candevice_t *candev, int chipnr) +{ + + if(candev->sysdevptr.pcidev==NULL) + return -ENODEV; + + CANMSG("initializing esdpci200 chip operations\n"); + + + sja1000p_fill_chipspecops(candev->chip[chipnr]); +// candev->chip[chipnr]->chip_base_addr= candev->io_addr + chipnr*IO_RANGE; + //candev->chip[chipnr]->chip_base_addr= (long)ioremap( candev->io_addr, IO_RANGE ) + chipnr * IO_RANGE; + candev->chip[chipnr]->chip_base_addr=candev->io_addr + chipnr * IO_RANGE; + + candev->chip[chipnr]->chipspecops->irq_handler=esdpci200_irq_handler; + + candev->chip[chipnr]->flags = 0; + candev->chip[chipnr]->int_cpu_reg = 0; /* i82527 specific */ + candev->chip[chipnr]->int_clk_reg = 0; /* i82527 specific */ + candev->chip[chipnr]->int_bus_reg = 0; /* i82527 specific */ + candev->chip[chipnr]->sja_cdr_reg = sjaCDR_CBP | sjaCDR_CLK_OFF; /* hardware specific options for the Clock Divider register */ + candev->chip[chipnr]->sja_ocr_reg = ESDPCI200_OCR_DEFAULT_STD; /* hardware specific options for the Output Control register */ + candev->chip[chipnr]->clock = 16000000; + candev->chip[chipnr]->chip_irq=candev->sysdevptr.pcidev->irq; + candev->chip[chipnr]->flags |= CHIP_IRQ_PCI; + if( chipnr > 0 ) /* only one IRQ used for both channels. + CHIP_IRQ_CUSTOM req'd for RTAI, since + registering two handlers for the same IRQ + returns an error */ + candev->chip[chipnr]->flags |= CHIP_IRQ_CUSTOM; + + return 0; +} + +int esdpci200_init_obj_data(struct canchip_t *chip, int objnr) +{ + chip->msgobj[objnr]->obj_base_addr=chip->chip_base_addr; + return 0; +} + +int esdpci200_program_irq(struct candevice_t *candev) +{ + + return 0; +} + +int esdpci200_register(struct hwspecops_t *hwspecops) +{ + hwspecops->request_io = esdpci200_request_io; + hwspecops->release_io = esdpci200_release_io; + hwspecops->reset = esdpci200_reset; + hwspecops->init_hw_data = esdpci200_init_hw_data; + hwspecops->init_chip_data = esdpci200_init_chip_data; + hwspecops->init_obj_data = esdpci200_init_obj_data; + hwspecops->write_register = esdpci200_write_register; + hwspecops->read_register = esdpci200_read_register; + hwspecops->program_irq = esdpci200_program_irq; + return 0; +} + + +#endif /*CAN_ENABLE_PCI_SUPPORT*/