]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - lincan/src/esdpci200.c
LinCAN sources go through big white-space cleanup.
[lincan.git] / lincan / src / esdpci200.c
index ead998b127eb1c54e5a7292cbf8cdc8581a4e6a4..09c44fafdc7bcbd7d50fd8e53acf6b903a2eb364 100644 (file)
@@ -1,10 +1,37 @@
-/* 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);
 }
@@ -78,7 +104,7 @@ 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);
+       outl(0x0, candev->res_addr + PLX9052_INTCSR);
        DEBUGMSG("disabled interrupts on the PLX\n");
 }
 
@@ -86,8 +112,8 @@ 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"); 
+               candev->res_addr+PLX9052_INTCSR);
+       DEBUGMSG("enabled interrupts on the PLX\n");
 }
 
 int esdpci200_irq_handler(int irq, struct canchip_t *chip)
@@ -98,10 +124,10 @@ 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++)
@@ -118,13 +144,13 @@ int esdpci200_irq_handler(int irq, struct canchip_t *chip)
                        }
                }
                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");
                }
        }
@@ -133,7 +159,61 @@ int esdpci200_irq_handler(int irq, struct canchip_t *chip)
 
 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)
@@ -141,7 +221,7 @@ 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);
@@ -153,17 +233,15 @@ int esdpci200_release_io(struct candevice_t *candev)
        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)
@@ -200,28 +278,27 @@ 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;
@@ -231,52 +308,31 @@ int esdpci200_init_hw_data(struct candevice_t *candev)
        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;
@@ -284,31 +340,25 @@ int esdpci200_init_hw_data(struct candevice_t *candev)
        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;
 
@@ -322,13 +372,13 @@ int esdpci200_init_chip_data(struct candevice_t *candev, int chipnr)
        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)
 {
@@ -348,6 +398,7 @@ int esdpci200_register(struct hwspecops_t *hwspecops)
        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;