Subversion Repositories f9daq

Rev

Rev 44 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. //****************************************************************************
  2. // Copyright (C) 2000-2006  ARW Elektronik Germany
  3. //
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation; either version 2 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program; if not, write to the Free Software
  17. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. //
  19. // This product is not authorized for use as critical component in
  20. // life support systems without the express written approval of
  21. // ARW Elektronik Germany.
  22. //  
  23. // Please announce changes and hints to ARW Elektronik
  24. //
  25. // Maintainer(s): Klaus Hitschler (klaus.hitschler@gmx.de)
  26. //
  27. //****************************************************************************
  28.  
  29. //****************************************************************************
  30. //
  31. // main.c -- the main driver module for the PCIVME PCI to VME Interface
  32. //           Thanks to A.Rubini's Book and Dirk Muelhlenberg and H.J.Mathes
  33. //           for their arwvme driver
  34. //
  35. // $Log: main.c,v $
  36. // Revision 1.10  2006/06/04 12:20:46  klaus
  37. // release_20060604; Version 3.2; pci_{en|dis}able_device() added
  38. //
  39. // Revision 1.9  2004/08/13 19:23:26  klaus
  40. // conversion to kernel-version 2.6, released version 3.0
  41. //
  42. // Revision 1.8  2003/06/27 17:25:52  klaus
  43. // incomplete try to get mmap() with nopage() running for automatic page switch
  44. //
  45. // Revision 1.7  2002/10/18 21:56:28  klaus
  46. // completed functional features, untested
  47. //
  48. // Revision 1.6  2002/10/18 21:56:28  klaus
  49. // completed functional features, untested
  50. //
  51. // Revision 1.5  2002/10/17 19:05:03  klaus
  52. // VME access is working through test to lib to driver
  53. //
  54. //****************************************************************************
  55. #define NOPAGE_SIGBUS   (NULL)
  56.  
  57. #define VERSION_HI 3
  58. #define VERSION_LO 2
  59.  
  60. /*--- INCLUDES ---------------------------------------------------------------------------*/
  61. #include "common.h"  /* must be the first include */
  62. #include <linux/module.h>
  63.  
  64. #include <linux/sched.h>
  65. #include <linux/proc_fs.h>
  66. #include <linux/pci.h>
  67. #include <asm/types.h>
  68. #include <linux/mm.h>
  69. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
  70. #include <linux/interrupt.h>
  71. #endif
  72.  
  73. #include "askpci.h"
  74. #include "plxbug.h"
  75. #include "plx9050.h"
  76. #include "fops.h"
  77. #include "pcivme.h"
  78. #include "pciif.h"
  79. #include "vic.h"
  80. #include "main.h"
  81.  
  82. /*--- DEFINES -----------------------------------------------------------------------------*/
  83. MODULE_AUTHOR("klaus.hitschler@gmx.de");
  84. MODULE_DESCRIPTION("Driver for ARW Elektronik PCI VME interface.");
  85. MODULE_SUPPORTED_DEVICE("PCIVME");
  86. MODULE_LICENSE("GPL");
  87.  
  88. #define MAJOR_NO               0                         /* use dynamic assignment */
  89.  
  90. #define PCIVME_VENDOR_ID  0x10B5
  91. #define PCIVME_DEVICE_ID  0x9050
  92. #define PCIVME_SUBSYS_ID  0x1167
  93. #define PCIVME_SUBVEN_ID  0x9050
  94.  
  95. /*--- TYPEDEFS ----------------------------------------------------------------------------*/
  96.  
  97. /*--- GLOBALS -----------------------------------------------------------------------------*/
  98. DRIVER_OBJ drv;
  99.  
  100. /*--- LOCALS ------------------------------------------------------------------------------*/
  101.  
  102. /*--- FUNCTIONS ---------------------------------------------------------------------------*/
  103.  
  104. struct proc_dir_entry *proc;
  105.  
  106. static int my_interrupt(u16 intCSR)
  107. {
  108.     int result = NOT_MY_INTERRUPT;
  109.  
  110.     if (intCSR & 0x0040)  // it is global enabled
  111.     {
  112.         if ((intCSR & 0x0028) == 0x0028) // it is a enabled PCIADA interrupt
  113.             result = PCIADA_INTERRUPT;
  114.         else
  115.             if ((intCSR & 0x0005) == 0x0005) // it is a enabled VMEMM interrupt
  116.             result = VMEMM_INTERRUPT;
  117.     }
  118.  
  119.     return result;
  120. }
  121.  
  122. static irqreturn_t pcivme_irqhandler(int irq, void *dev_id )
  123. {
  124.     DEVICE_OBJ *pd = (DEVICE_OBJ *)dev_id;
  125.  
  126.     if (pd)
  127.     {
  128.         // evaluate the reason of the interrupt - if it is mine
  129.         u16 intCSR          = readw((const volatile void *) (pd->pPCIADAIntCSR));
  130.         int which_interrupt = my_interrupt(intCSR);
  131.  
  132.         if (which_interrupt)
  133.         {
  134.             writew(intCSR & ~0x40, (volatile void *) pd->pPCIADAIntCSR); /* disable global interrupts */
  135.             pd->wIrqStatus = (u16)which_interrupt;
  136.             pd->dwInterruptCount++;
  137.             wake_up_interruptible(&pd->event_queue);   /* stop blocking if any */
  138.                        
  139.                         return IRQ_RETVAL(1);
  140.         }
  141.     }
  142.        
  143.         return IRQ_RETVAL(0);
  144. }
  145.  
  146. static int request_io_memory(PCIConfig *pPch)
  147. {
  148.     if (!request_mem_region(pci_resource_start(pPch->pciDev, 0),  LCR_SPACE, DEVICE_NAME) ) {
  149.                         PRINTK(KERN_WARNING "%s: LCR memory region %llx-%llx already in use\n", DEVICE_NAME,
  150.                                  pci_resource_start(pPch->pciDev, 0), pci_resource_start(pPch->pciDev, 0) + LCR_SPACE );
  151.                         kfree(pPch);
  152.                         return EBUSY;
  153.  
  154.     };
  155.     if (!request_mem_region(pci_resource_start(pPch->pciDev, 2),  CTL_SPACE, DEVICE_NAME) ){
  156.                         PRINTK(KERN_WARNING "%s: CTL memory region %llx-%llx already in use\n", DEVICE_NAME,
  157.                                  pci_resource_start(pPch->pciDev, 0),  pci_resource_start(pPch->pciDev, 2) + CTL_SPACE );
  158.                         kfree(pPch);
  159.                         return EBUSY;
  160.  
  161.     }
  162.     if (!request_mem_region(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE, VME_SPACE, DEVICE_NAME) ){
  163.                         PRINTK(KERN_WARNING "%s: VME memory region %llx-%llx already in use\n", DEVICE_NAME,
  164.                                  pci_resource_start(pPch->pciDev, 0)+CTL_SPACE,  pci_resource_start(pPch->pciDev, 0) + LCR_SPACE+VME_SPACE );
  165.                         kfree(pPch);
  166.                         return EBUSY;
  167.  
  168.     };
  169.     return 0;
  170. }
  171.  
  172. static void release_io_memory(PCIConfig *pPch)
  173. {
  174.     release_mem_region(pci_resource_start(pPch->pciDev, 0),  LCR_SPACE);
  175.     release_mem_region(pci_resource_start(pPch->pciDev, 2),  CTL_SPACE);
  176.     release_mem_region(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE, VME_SPACE);
  177. }
  178.  
  179. static int translate_addresses(DEVICE_OBJ *pd, PCIConfig *pPch)    /* differs from PCICC32 */
  180. {
  181.     if (pci_resource_start(pPch->pciDev, 0) < LOW_MEMORY)  /* LCR ISA base addresses */
  182.         pd->pLCR = (u32)bus_to_virt(pci_resource_start(pPch->pciDev, 0));
  183.     else
  184.         pd->pLCR = (u32)ioremap(pci_resource_start(pPch->pciDev, 0), LCR_SPACE);
  185.  
  186.     if (pci_resource_start(pPch->pciDev, 2) < LOW_MEMORY)  /* User ISA base addresses */
  187.     {
  188.         pd->pCtl = (u32)bus_to_virt(pci_resource_start(pPch->pciDev, 2)            );
  189.         pd->pVME = (u32)bus_to_virt(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE);
  190.     }
  191.     else
  192.     {
  193.         pd->pPhysVME = pci_resource_start(pPch->pciDev, 2) + CTL_SPACE;
  194.  
  195.         pd->pCtl = (u32)ioremap(pci_resource_start(pPch->pciDev, 2)            , CTL_SPACE);
  196.         pd->pVME = (u32)ioremap(pci_resource_start(pPch->pciDev, 2) + CTL_SPACE, VME_SPACE);
  197.     }
  198.  
  199.     return 0;
  200. }
  201.  
  202. static void un_translate_addresses(DEVICE_OBJ *pd, PCIConfig *pPch)
  203. {
  204.     if (pci_resource_start(pPch->pciDev, 0) >= LOW_MEMORY)  /* no LCR ISA base addresses */
  205.         iounmap((void *)pd->pLCR);
  206.  
  207.     if (pci_resource_start(pPch->pciDev, 2) >= LOW_MEMORY)
  208.     {
  209.         pd->pPhysVME = 0;
  210.  
  211.         iounmap((void *)pd->pCtl);
  212.         iounmap((void *)pd->pVME);
  213.     }
  214. }
  215.  
  216. static void soft_init(DEVICE_OBJ *pd)
  217. {
  218.     if (pd)
  219.     {
  220.         init_waitqueue_head(&pd->event_queue);
  221.  
  222.         pd->pLCR = pd->pCtl = pd->pVME = 0;
  223.         pd->pPch = (PCIConfig *)NULL;
  224.         pd->bConnected = 0;
  225.         pd->wInitStep = 0;    
  226.         pd->wIrq = 0xFFFF;          
  227.         pd->dwInterruptCount = 0;
  228.         pd->wIrqStatus = 0;
  229.         pd->nOpenCounter = 0;
  230.  
  231.         pd->cModuleNumber = 255;
  232.         pd->cFPGAVersion = 255;
  233.         pd->cSystemController = 0;
  234.         pd->cWordMode         = 0;
  235.  
  236.         pd->pAdrMod = 0;
  237.         pd->pAdrReg = 0;
  238.         pd->pCSR    = 0;
  239.  
  240.         pd->pPCIADACntrl  = 0;
  241.         pd->pPCIADAIntCSR = 0;      
  242.  
  243.         pd->bCurrentModifier     = 0;
  244.         pd->dwCurrentPageAddress = -1;
  245.                                 pd->currentMap.pageptr = NOPAGE_SIGBUS;
  246.                                 pd->currentMap.addr = 0;
  247.     }
  248. }
  249.  
  250. int test_connection(DEVICE_OBJ *pd)
  251. {
  252.     u16 intCSR_store;
  253.     u16 cntrl_store;
  254.     int i;      
  255.     int error   = 0;
  256.     u32 dwADRH  = pd->pCtl + ADRH;
  257.     u32 dwADRL  = pd->pCtl + ADRL;
  258.     u32 dwADRHL = pd->pCtl + ADRHL;
  259.     u32 dwStore;
  260.     u16 wRet;
  261.  
  262.     cntrl_store  = readw((const volatile void *) pd->pPCIADACntrl);         /* read CONTROL register */
  263.     intCSR_store = readw((const volatile void *) pd->pPCIADAIntCSR);        /* read interrupt + CSR register */
  264.  
  265.     writew(0, (volatile void *) pd->pPCIADAIntCSR);                   /* disable interrupts */
  266.     writew(cntrl_store | 0x0180, (volatile void *) pd->pPCIADACntrl); /* enable access */
  267.  
  268.     // save adr register
  269.     dwStore = readl((const volatile void *) dwADRHL);
  270.     for (i = 1000; i; i--)
  271.     {
  272.         writew(0x5555, (volatile void *) dwADRH);
  273.         writew(0xAAAA, (volatile void *) dwADRL);
  274.         wRet   = readw((const volatile void *) dwADRH);
  275.         if (wRet != 0x5555)
  276.         {
  277.             error = 1;
  278.             break;
  279.         }
  280.  
  281.         writew(0xAAAA, (volatile void *) dwADRH);
  282.         writew(0x5555, (volatile void *) dwADRL);
  283.         wRet   = readw((const volatile void *) dwADRH);
  284.         if (wRet != 0xAAAA)
  285.         {
  286.             error = 1;
  287.             break;
  288.         }
  289.  
  290.         writew(0x0000, (volatile void *) dwADRH);
  291.         writew(0xFFFF, (volatile void *) dwADRL);
  292.         wRet   = readw((const volatile void *) dwADRH);
  293.         if (wRet != 0x0000)
  294.         {
  295.             error = 1;
  296.             break;
  297.         }
  298.  
  299.         writew(0xFFFF, (volatile void *) dwADRH);
  300.         writew(0x0000, (volatile void *) dwADRL);
  301.         wRet   = readw((const volatile void *) dwADRH);
  302.         if (wRet != 0xFFFF)
  303.         {
  304.             error = 1;
  305.             break;
  306.         }
  307.     }
  308.  
  309.     // restore register
  310.     writel(dwStore, (volatile void *) dwADRHL);
  311.  
  312.     //clear possible interrupts
  313.     writew(cntrl_store & ~0x0100, (volatile void *) pd->pPCIADACntrl); /* clear potential interrupt */
  314.  
  315.     // restore LCR registers
  316.     writew(cntrl_store,  (volatile void *) pd->pPCIADACntrl);
  317.     writew(intCSR_store, (volatile void *) pd->pPCIADAIntCSR);
  318.  
  319.     return error;  
  320. }
  321.  
  322. int get_module_info(DEVICE_OBJ *pd)
  323. {
  324.     u16 intCSR_store;
  325.     u16 cntrl_store;
  326.     int found = 0;
  327.     u16 data;
  328.  
  329.     cntrl_store  = readw((const volatile void *) pd->pPCIADACntrl);  /* read CONTROL register */
  330.     intCSR_store = readw((const volatile void *) pd->pPCIADAIntCSR); /* read interrupt + CSR register */
  331.  
  332.     PRINTK(KERN_DEBUG "%s : cntrl=0x%04x, intCSR=0x%04x\n", DEVICE_NAME, cntrl_store, intCSR_store);
  333.  
  334.     if (cntrl_store & 0x0800) /* a VMEMM is connected */
  335.     {
  336.         u16 bla = cntrl_store | 0x0180;
  337.  
  338.         writew(0,   (volatile void *) pd->pPCIADAIntCSR); /* disable interrupts */
  339.         writew(bla, (volatile void *) pd->pPCIADACntrl);  /* enable access */
  340.  
  341.         // read main status register
  342.         data = readw((const volatile void *) pd->pCSR);
  343.  
  344.         if ((data & 0xF000) != VMEMM_MODULE_TYPE)
  345.         {
  346.             pd->cModuleNumber = pd->cFPGAVersion = 255;
  347.             printk(KERN_ERR "%s : Wrong module type connected @ index %d!\n", DEVICE_NAME, pd->wIndex);
  348.         }
  349.         else
  350.         {
  351.             found = 1;
  352.             pd->cModuleNumber     = (data >> 4) & 0xF;
  353.             pd->cFPGAVersion      = (data >> 8) & 0xF;
  354.             pd->cSystemController = (data & 0x0008);
  355.             pd->cWordMode         = (data & 0x0004);
  356.         }
  357.  
  358.         // clear possible interrupts
  359.         writew(cntrl_store & ~0x0100, (volatile void *) pd->pPCIADACntrl); /* clear potential interrupt */
  360.  
  361.         /* restore all contents */
  362.         writew(cntrl_store,  (volatile void *) pd->pPCIADACntrl);
  363.         writew(intCSR_store, (volatile void *) pd->pPCIADAIntCSR);
  364.     }
  365.  
  366.     return found;  
  367. }
  368.  
  369. //static int pcivme_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *data)
  370. static ssize_t pcivme_read_proc(struct file *filp,char *buf,size_t count,loff_t *offp )
  371.     {
  372.     int              pos = 0;
  373.     DEVICE_OBJ       *pd;
  374.     PCIConfig        *ch;
  375.     u16                cntrl;
  376.     char             *cause = "none";
  377.     struct list_head *ptr;
  378.     PRINTK(KERN_DEBUG "%s : pcivme_read_proc()\n",DEVICE_NAME);
  379.    
  380.     if (*offp != 0) return 0;
  381.     pos += sprintf(buf + pos, "\nPCIVME information. Version %d.%d from Klaus Hitschler.\n", VERSION_HI, VERSION_LO);
  382.  
  383.     pos += sprintf(buf + pos, " ---------------------\n");
  384.     pos += sprintf(buf + pos, " Interfaces found : %d\n", drv.count);
  385.     pos += sprintf(buf + pos, " Major Number     : %d\n", drv.nMajor);
  386.  
  387.     for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  388.     {
  389.         pd = list_entry(ptr, DEVICE_OBJ, list);
  390.         ch = pd->pPch;
  391.         cntrl = readw((const volatile void *)(pd->pLCR + PLX9050_CNTRL));
  392.         pos += sprintf(buf + pos, " --- %d ---------------\n", pd->wIndex + 1);
  393.         pos += sprintf(buf + pos, " LCR     phys/virt/size : 0x%08lx/0x%08x/%d\n",(long unsigned int) pci_resource_start(ch->pciDev, 0),             pd->pLCR, LCR_SPACE);
  394.         pos += sprintf(buf + pos, " Control phys/virt/size : 0x%08lx/0x%08x/%d\n",(long unsigned int)  pci_resource_start(ch->pciDev, 2),             pd->pCtl, CTL_SPACE);
  395.         pos += sprintf(buf + pos, " VME     phys/virt/size : 0x%08lx/0x%08x/%d\n",(long unsigned int)  pci_resource_start(ch->pciDev, 2) + CTL_SPACE, pd->pVME, VME_SPACE);
  396.         pos += sprintf(buf + pos, " Irq                    : %d\n", pd->wIrq);
  397.  
  398.         if (pd->bConnected)
  399.         {
  400.             pos += sprintf(buf + pos, " VMEMM is or was        : (software) connected.\n");
  401.             pos += sprintf(buf + pos, " Module-Number          : %d\n", pd->cModuleNumber);
  402.             pos += sprintf(buf + pos, " FPGA-Version           : %d\n", pd->cFPGAVersion);
  403.             pos += sprintf(buf + pos, " Systemcontroller       : %s\n", (pd->cSystemController) ? "yes" : "no");
  404.             pos += sprintf(buf + pos, " Word Mode              : %s\n", (pd->cWordMode) ? "yes" : "no");
  405.         }
  406.         else
  407.             pos += sprintf(buf + pos, " VMEMM is or was        : not (software) connected.\n");      
  408.  
  409.         if (!((cntrl & 0x0800) && (!(cntrl & 0x0600))))
  410.             pos += sprintf(buf + pos, " VMEMM is               : powered off or cable disconnected.\n");
  411.  
  412.         pos += sprintf(buf + pos, " IrqCount               : %d\n", pd->dwInterruptCount);
  413.         if (pd->wIrqStatus & PCIADA_INTERRUPT)
  414.             cause = "Timeout";
  415.         else
  416.             if (pd->wIrqStatus & VMEMM_INTERRUPT)
  417.                 cause = "VME";
  418.         pos += sprintf(buf + pos, " Pending IrqStatus      : %s\n", cause);
  419.     }
  420.  
  421.     pos += sprintf(buf + pos, "\n");
  422.     if (pos>count) {
  423.       buf[count-1]=0;
  424.       pos=count;
  425.     }    
  426.     PRINTK(KERN_DEBUG "%s : pcivme_read_proc() end count=%d\n",DEVICE_NAME, pos);
  427.     //*eof = 1;
  428.     *offp = pos;
  429.     return pos;
  430. }
  431.  
  432. static const struct proc_ops  pcivme_read_proc_fops = {    
  433. proc_read: pcivme_read_proc
  434. };
  435.  
  436. static void deleteMyLists(void)
  437. {
  438.     DEVICE_OBJ      *pd;
  439.  
  440.     /* delete my lists */
  441.     while (!list_empty(&drv.devList))                 // cycle through the list of pci devices and remove them
  442.     {
  443.         pd = (DEVICE_OBJ *)drv.devList.prev;            // empty in reverse order
  444.         list_del(&pd->list);
  445.         kfree(pd);
  446.     }
  447.  
  448.     DeletePCIConfig(&drv);
  449. }
  450.  
  451. int pcivme_module_init(void)
  452. {
  453.     PCIConfig        *ch;
  454.     DEVICE_OBJ       *pd;
  455.     int              result = 0;
  456.     struct list_head *ptr;
  457.  
  458.     PRINTK(KERN_DEBUG "%s : init_module\n", DEVICE_NAME);
  459.  
  460.     /* create list of PCIADAs and work devices */
  461.     INIT_LIST_HEAD(&drv.devList);
  462.     INIT_LIST_HEAD(&drv.pciList);
  463.  
  464.     drv.count = 0;
  465.  
  466.     /* search for all PCIADA modules */
  467.     if ((result = GetPCIConfig(&drv, PCIVME_DEVICE_ID, PCIVME_VENDOR_ID, PCIVME_SUBSYS_ID, PCIVME_SUBVEN_ID)))
  468.     {
  469.         deleteMyLists();
  470.         return result;
  471.     }
  472.  
  473.     /* fix the PLX bug in all PCIADAs */
  474.     for (ptr = drv.pciList.next; ptr != &drv.pciList; ptr = ptr->next)
  475.     {
  476.         ch = list_entry(ptr, PCIConfig, list);
  477.         PLX9050BugFix(ch);
  478.     }
  479.  
  480.     /* create work_devices and translate the access addresses */
  481.     for (ptr = drv.pciList.next; ptr != &drv.pciList; ptr = ptr->next)
  482.     {
  483.         ch = list_entry(ptr, PCIConfig, list);
  484.  
  485.         pd = (DEVICE_OBJ *)kmalloc(sizeof(DEVICE_OBJ), GFP_ATOMIC);
  486.         soft_init(pd);
  487.         pd->pPch = ch;
  488.         pd->wIndex = drv.count;
  489.  
  490.         if (!request_io_memory(ch))
  491.         {
  492.             pd->wInitStep = 1;
  493.  
  494.             if (translate_addresses(pd, ch))
  495.             {
  496.                 printk(KERN_ERR "%s : translation of addresses failed!\n", DEVICE_NAME);
  497.                 kfree_s(pd, sizeof(*pd));       // FREE(pd);
  498.             }
  499.             else
  500.             {
  501.                 // successful translate_addresses
  502.                 pd->wInitStep = 2;
  503.  
  504.                 // create some 'fast access' addresses
  505.                 pd->pAdrMod = pd->pCtl + VICBASE + AMSR;
  506.                 pd->pAdrReg = pd->pCtl + ADRHL;
  507.                 pd->pCSR    = pd->pCtl + CSR;
  508.  
  509.                 pd->pPCIADACntrl  = pd->pLCR + PLX9050_CNTRL;
  510.                 pd->pPCIADAIntCSR = pd->pLCR + PLX9050_INTCSR;
  511.  
  512.                 //if (request_irq(pd->pPch->pciDev->irq, pcivme_irqhandler, IRQF_DISABLED| IRQF_SHARED, DEVICE_NAME, pd))
  513.                 if (request_irq(pd->pPch->pciDev->irq, pcivme_irqhandler, IRQF_SHARED, DEVICE_NAME, pd))
  514.                 {
  515.                     printk(KERN_ERR "%s : can't get irq @ %d\n", DEVICE_NAME, pd->pPch->pciDev->irq);
  516.                     kfree_s(pd, sizeof(*pd));       // FREE(pd);
  517.                 }
  518.                 else
  519.                 {
  520.                     // successful request_irq
  521.                     pd->wInitStep = 3;
  522.                     pd->wIrq = pd->pPch->pciDev->irq;
  523.  
  524.                     list_add_tail(&pd->list, &drv.devList);  /* add this device to list of working devices*/
  525.                     drv.count++;
  526.  
  527.                     pd->bConnected =  get_module_info(pd);
  528.                     if (pd->bConnected && test_connection(pd))
  529.                     {
  530.                         printk(KERN_ERR "%s : connection test @ driver install failed!\n", DEVICE_NAME);
  531.                         pd->bConnected = 0;
  532.                     }
  533.                 }
  534.             }
  535.         }
  536.         else
  537.             printk(KERN_ERR "%s : requested io-memory still claimed!\n", DEVICE_NAME);
  538.     }
  539.  
  540.     drv.nMajor = MAJOR_NO;
  541.     result = register_chrdev(drv.nMajor, DEVICE_NAME, &pcivme_fops);
  542.     if (result < 0)
  543.     {
  544.         printk(KERN_ERR "%s: Can't install driver (%d)\n", DEVICE_NAME, result);
  545.  
  546.         /* untranslate translated addresses */
  547.         for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  548.         {
  549.             pd = list_entry(ptr, DEVICE_OBJ, list);
  550.             ch = pd->pPch;
  551.             un_translate_addresses(pd, ch);
  552.         }
  553.  
  554.         /* delete my lists */
  555.         deleteMyLists();
  556.  
  557.         return result;
  558.     }
  559.     else
  560.     {
  561.         if (drv.nMajor == 0)
  562.             drv.nMajor = result;
  563.  
  564.         printk(KERN_DEBUG "%s : major #%d assigned.\n", DEVICE_NAME, drv.nMajor);
  565.     }
  566.  
  567.     /* register the proc device */
  568.  
  569.     // create_proc_read_entry is depricated since kernel 3.10
  570.     //return create_proc_read_entry(DEVICE_NAME, 0, NULL, pcivme_read_proc, NULL) ? 0 : -ENODEV;
  571.     proc = proc_create_data(DEVICE_NAME, 0, NULL, &pcivme_read_proc_fops, NULL);
  572.     return (proc) ? 0 : -ENODEV;
  573.  
  574. }
  575.  
  576. void pcivme_module_exit(void)
  577. {
  578.     PCIConfig        *ch;
  579.     DEVICE_OBJ       *pd;
  580.     struct list_head *ptr;
  581.  
  582.     PRINTK(KERN_DEBUG "%s : cleanup_module.\n", DEVICE_NAME);
  583.  
  584.     unregister_chrdev(drv.nMajor, DEVICE_NAME);
  585.  
  586.     /* unregister the proc device */
  587.     remove_proc_entry(DEVICE_NAME, NULL);
  588.  
  589.     /* redo all */
  590.     for (ptr = drv.devList.next; ptr != &drv.devList; ptr = ptr->next)
  591.     {
  592.         pd = list_entry(ptr, DEVICE_OBJ, list);
  593.         ch = pd->pPch;
  594.         switch (pd->wInitStep)
  595.         {
  596.             case 3:  writew(readw((const volatile void *)(pd->pLCR + PLX9050_INTCSR)) & ~0x40, (volatile void *) (pd->pLCR + PLX9050_INTCSR));  // disable global interrupts
  597.                      free_irq(pd->wIrq, pd);  
  598.             case 2:  un_translate_addresses(pd, ch);
  599.             case 1:  release_io_memory(ch);
  600.             default: pd->wInitStep = 0;
  601.         }
  602.  
  603.         drv.count--;
  604.     }
  605.  
  606.     deleteMyLists();
  607.  
  608.     return;
  609. }
  610.  
  611. module_init(pcivme_module_init);
  612. module_exit(pcivme_module_exit);
  613.  
  614.