-/* esdpci200.c - support for ESD Electronics' CAN/PCI-200 cards
- * Linux CAN-bus device driver.
- * The card support was added by Manuel Bessler <m.bessler@gmx.net>
- * Based on adlink7841.c and nsi_canpci.c
- * This software is released under the GPL-License.
- * Version lincan-0.3.3
- */
+/**************************************************************************/
+/* File: esdpci200.c - support for ESD Electronics' CAN/PCI-200 cards */
+/* */
+/* LinCAN - (Not only) Linux CAN bus driver */
+/* Copyright (C) 2002-2009 DCE FEE CTU Prague <http://dce.felk.cvut.cz> */
+/* Copyright (C) 2002-2009 Pavel Pisa <pisa@cmp.felk.cvut.cz> */
+/* Copyright (C) 2007 Manuel Bessler <m.bessler@gmx.net> */
+/* Funded by OCERA and FRESCOR IST projects */
+/* Based on CAN driver code by Arnaud Westenberg <arnaud@wanadoo.nl> */
+/* */
+/* LinCAN is free software; you can redistribute it and/or modify it */
+/* under terms of the GNU General Public License as published by the */
+/* Free Software Foundation; either version 2, or (at your option) any */
+/* later version. LinCAN is distributed in the hope that it will be */
+/* useful, but WITHOUT ANY WARRANTY; without even the implied warranty */
+/* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
+/* General Public License for more details. You should have received a */
+/* copy of the GNU General Public License along with LinCAN; see file */
+/* COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, */
+/* Cambridge, MA 02139, USA. */
+/* */
+/* To allow use of LinCAN in the compact embedded systems firmware */
+/* and RT-executives (RTEMS for example), main authors agree with next */
+/* special exception: */
+/* */
+/* Including LinCAN header files in a file, instantiating LinCAN generics */
+/* or templates, or linking other files with LinCAN objects to produce */
+/* an application image/executable, does not by itself cause the */
+/* resulting application image/executable to be covered by */
+/* the GNU General Public License. */
+/* This exception does not however invalidate any other reasons */
+/* why the executable file might be covered by the GNU Public License. */
+/* Publication of enhanced or derived LinCAN files is required although. */
+/**************************************************************************/
#include "../include/can.h"
#include "../include/can_sysdep.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 ioread32 can_readl
+ #define iowrite32 can_writel
+ #define ioread8 can_readb
+ #define iowrite8 can_writeb
#define wmb()
#define rmb()
#else
#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
+ * 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
+ * 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_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.
+/* 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 = inl( candev->res_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);
+ outl( (reg_reset | PLX9052_CNTRL_SWRESET ), candev->res_addr+PLX9052_CNTRL);
wmb();
udelay(2500);
- outl(reg_reset, candev->dev_base_addr+PLX9052_CNTRL);
+ outl(reg_reset, candev->res_addr+PLX9052_CNTRL);
wmb();
udelay(2500);
}
{
/* 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);
+ outl(0x0, candev->res_addr + PLX9052_INTCSR);
DEBUGMSG("disabled interrupts on the PLX\n");
}
{
/* 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");
+ candev->res_addr+PLX9052_INTCSR);
+ DEBUGMSG("enabled interrupts on the PLX\n");
}
int esdpci200_irq_handler(int irq, struct canchip_t *chip)
candev = chip->hostdevice;
retcode = CANCHIP_IRQ_NONE;
//DEBUGMSG("Starting to handle an IRQ\n");
- it_reg = inl(candev->dev_base_addr+PLX9052_INTCSR);
+ it_reg = inl(candev->res_addr+PLX9052_INTCSR);
rmb();
- if((it_reg & (PLX9052_INTCSR_LI1S | PLX9052_INTCSR_LI1EN) )
- == (PLX9052_INTCSR_LI1S | PLX9052_INTCSR_LI1EN) )
+ 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( retcode != CANCHIP_IRQ_HANDLED )
- {/* None of the chips felt they were responsible for this IRQ...
+ {/* 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));
+ //outl(it_reg,(void*)(candev->res_addr+PLX9052_INTCSR));
//CANMSG("CAN Interrupt disabled due to malfunction\n");
}
}
int esdpci200_request_io(struct candevice_t *candev)
{
+ struct pci_dev *pcidev = candev->sysdevptr.pcidev;
+ can_ioptr_t remap_addr;
+ unsigned long bar2_addr;
+
+ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
+ if(pci_request_region(pcidev, 0, "esdpci200_plx9050") != 0){
+ CANMSG("Request of esdpci200_plx9050 range failed\n");
+ return -ENODEV;
+ }else if(pci_request_region(pcidev, 1, "esdpci200_io") != 0){
+ CANMSG("Request of esdpci200_io range failed\n");
+ pci_release_region(pcidev, 0);
+ return -ENODEV;
+ }else if(pci_request_region(pcidev, 2, "esdpci200_sja") != 0){
+ CANMSG("Request of esdpci200_sja range failed\n");
+ pci_release_region(pcidev, 1);
+ pci_release_region(pcidev, 0);
+ return -ENODEV;
+ }
+ #else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+ if(pci_request_regions(pcidev, "esdpci200") != 0){
+ CANMSG("Request of esdpci200_plx9050 regions failed\n");
+ return -ENODEV;
+ }
+ #endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+
+
+ /* ioports, PLX local configuration registers */
+ candev->res_addr=pci_resource_start(pcidev,1);
+ /*MEM window for SJA1000 chips*/
+ bar2_addr = pci_resource_start(pcidev,2);
+ candev->io_addr = bar2_addr;
+ if( ! (remap_addr=ioremap(bar2_addr,
+ pci_resource_len(pcidev,2)))) /*MEM window for SJA1000 chips*/
+ {
+ CANMSG("Unable to access I/O memory at: 0x%lx\n", (unsigned long)bar2_addr);
+ goto ioremap_error;
+ }
+
+ can_base_addr_fixup(candev, remap_addr);
+ CANMSG("esdpci200_sja IO-memory: 0x%lx - 0x%lx (VMA 0x%lx)\n",
+ (unsigned long) bar2_addr,
+ (unsigned long) (bar2_addr + pci_resource_len(pcidev,2) - 1),
+ (long) remap_addr);
+
return 0;
+
+ ioremap_error:
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
+ pci_release_region(pcidev, 2);
+ pci_release_region(pcidev, 1);
+ pci_release_region(pcidev, 0);
+#else /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+ pci_release_regions(pcidev);
+#endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))*/
+ return -ENODEV;
}
int esdpci200_release_io(struct candevice_t *candev)
esdpci200_disconnect_irq(candev);
esdpci200_pci_soft_reset(candev);
- iounmap((void*)candev->io_addr);
+ iounmap(candev->dev_base_addr);
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21))
pci_release_region(candev->sysdevptr.pcidev, 2);
pci_release_region(candev->sysdevptr.pcidev, 1);
return 0;
}
-void esdpci200_write_register(unsigned data, unsigned long address)
+void esdpci200_write_register(unsigned data, can_ioptr_t address)
{
- //writeb(data,address);
- iowrite8((u8)data,(void*)address);
+ iowrite8((u8)data,address);
wmb();
}
-unsigned esdpci200_read_register(unsigned long address)
+unsigned esdpci200_read_register(can_ioptr_t address)
{
- //return readb(address);
- return ioread8((void*)address);
+ return ioread8(address);
}
int esdpci200_reset(struct candevice_t *candev)
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;
+ struct pci_dev *pcidev;
+
+ pcidev = can_pci_get_next_untaken_device(ESDPCI200_PCI_VENDOR_ID, ESDPCI200_PCI_PRODUCT_ID);
+ if(pcidev == NULL)
+ return -ENODEV;
- 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");
+ can_pci_dev_put(pcidev);
return -EIO;
}
candev->sysdevptr.pcidev=pcidev;
if(!(pci_resource_flags(pcidev, 0)&IORESOURCE_MEM))
{
printk(KERN_CRIT "PCI200 region %d is not MEM\n",0);
+ can_pci_dev_put(pcidev);
return -EIO;
}
if(!(pci_resource_flags(pcidev, 1)&IORESOURCE_IO))
{
printk(KERN_CRIT "PCI200 region %d is not IO\n",1);
+ can_pci_dev_put(pcidev);
return -EIO;
}
if(!(pci_resource_flags(pcidev,2)&IORESOURCE_MEM))
{
printk(KERN_CRIT "PCI200 region %d is not MEM\n",2);
+ can_pci_dev_put(pcidev);
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))*/
+ /* Reset/control field - used to store port of PLX9052 control region */
+ candev->res_addr = pci_resource_start(pcidev,1);;
+ /* Physical address of SJA1000 window, stored for debugging only */
+ candev->io_addr = pci_resource_start(pcidev,2);
- 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->aux_base_addr=NULL; /* mapped dynamically in esdpci200_request_io() */
+ candev->dev_base_addr=NULL; /* mapped dynamically in esdpci200_request_io() */
/*candev->flags |= CANDEV_PROGRAMMABLE_IRQ;*/
candev->nr_82527_chips=0;
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;
+void esdpci200_done_hw_data(struct candevice_t *candev)
+{
+ struct pci_dev *pcidev = candev->sysdevptr.pcidev;
+ can_pci_dev_put(pcidev);
}
int esdpci200_init_chip_data(struct candevice_t *candev, int chipnr)
{
if(candev->sysdevptr.pcidev==NULL)
- return -ENODEV;
+ 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]->chip_base_addr=candev->dev_base_addr + chipnr * IO_RANGE;
candev->chip[chipnr]->chipspecops->irq_handler=esdpci200_irq_handler;
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
+ 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)
{
hwspecops->release_io = esdpci200_release_io;
hwspecops->reset = esdpci200_reset;
hwspecops->init_hw_data = esdpci200_init_hw_data;
+ hwspecops->done_hw_data = esdpci200_done_hw_data;
hwspecops->init_chip_data = esdpci200_init_chip_data;
hwspecops->init_obj_data = esdpci200_init_obj_data;
hwspecops->write_register = esdpci200_write_register;