Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 86 | f9daq | 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 | // pcicc32.c -- the driver module for the PCICC32 PCI to CAMAC 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.29 2006/06/04 12:26:07 klaus |
||
| 37 | // release_20060604; Version 6.9; pci_{en|dis}able_device() added; remap_page_range reorganized |
||
| 38 | // |
||
| 39 | // Revision 1.28 2006/03/28 21:49:49 klaus |
||
| 40 | // release_20060328; Version 6.8; Support for AMD64 and Kernel 2.6.15 |
||
| 41 | // |
||
| 42 | // Revision 1.27 2005/10/08 13:21:52 klaus |
||
| 43 | // release 6.7, removed "requested io-memory still claimed" bug at incomplete termination |
||
| 44 | // |
||
| 45 | // Revision 1.26 2005/10/07 16:57:10 klaus |
||
| 46 | // fixed a bug with request_irq with IRQs greater than 127 |
||
| 47 | // |
||
| 48 | // Revision 1.25 2005/03/11 13:23:26 klaus |
||
| 49 | // simple corrections for to use with kernels 2.4.21 |
||
| 50 | // |
||
| 51 | // Revision 1.24 2004/08/12 19:59:19 klaus |
||
| 52 | // conversion to kernel-version 2.6, released version 6.0 |
||
| 53 | // |
||
| 54 | // Revision 1.23 2004/01/16 18:42:26 klaus |
||
| 55 | // converted remap_page_range call for kernels >= 2.4.20, contributed by Roberto Bertoni, |
||
| 56 | // release of version 5.3 |
||
| 57 | // |
||
| 58 | // Revision 1.22 2003/12/04 20:34:49 klaus |
||
| 59 | // minor change: restore the hardware set intCSR content at release of path, release of version 5.2 |
||
| 60 | // |
||
| 61 | // Revision 1.21 2003/06/19 08:23:38 klaus |
||
| 62 | // re-compiled with RH-7.2 (kernel 2.4.10) |
||
| 63 | // |
||
| 64 | // Revision 1.20 2003/05/12 20:59:50 klaus |
||
| 65 | // another improvement at previous place |
||
| 66 | // |
||
| 67 | // Revision 1.19 2003/05/12 20:38:28 klaus |
||
| 68 | // improved debug messages from test of connection |
||
| 69 | // |
||
| 70 | //**************************************************************************** |
||
| 71 | |||
| 72 | #define VERSION_HI 6 |
||
| 73 | #define VERSION_LO 9 |
||
| 74 | |||
| 75 | /*--- INCLUDES ---------------------------------------------------------------------------*/ |
||
| 76 | #include "common.h" /* must be the first include */ |
||
| 77 | |||
| 78 | //#include <linux/config.h> |
||
| 79 | |||
| 80 | #include <linux/sched.h> |
||
| 81 | #include <linux/proc_fs.h> |
||
| 82 | #include <linux/pci.h> |
||
| 83 | #include <asm/types.h> |
||
| 84 | #include <linux/delay.h> |
||
| 85 | #include <linux/wait.h> |
||
| 86 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) |
||
| 87 | #include <linux/interrupt.h> |
||
| 88 | #endif |
||
| 89 | |||
| 90 | #include "list.h" |
||
| 91 | #include "askpci.h" |
||
| 92 | #include "plxbug.h" |
||
| 93 | #include "plx9050.h" |
||
| 94 | #include "fops.h" |
||
| 95 | #include "pcicc32.h" |
||
| 96 | |||
| 97 | /*--- DEFINES -----------------------------------------------------------------------------*/ |
||
| 98 | #define MAJOR_NO 0 /* use dynamic assignment */ |
||
| 99 | #define DEVICE_NAME "pcicc32" |
||
| 100 | |||
| 101 | #if (VERSION_HI < 3) |
||
| 102 | #define ARWVME_DEVICE_ID 0x9050 |
||
| 103 | #define ARWVME_VENDOR_ID 0x10B5 |
||
| 104 | #define ARWVME_SUBSYSTEM_ID 0x1167 |
||
| 105 | #define ARWVME_SUBSYSTEM_VENDOR_ID 0x9050 |
||
| 106 | #else |
||
| 107 | #define ARWVME_DEVICE_ID 0x2258 |
||
| 108 | #define ARWVME_VENDOR_ID 0x10B5 |
||
| 109 | #define ARWVME_SUBSYSTEM_ID 0x2258 |
||
| 110 | #define ARWVME_SUBSYSTEM_VENDOR_ID 0x9050 |
||
| 111 | #endif |
||
| 112 | |||
| 113 | #define MAKE_CC32_ADR(N, A, F) (u16)((N << 10) + (A << 6) + ((F & 0xf) << 2)) |
||
| 114 | |||
| 115 | /*--- TYPEDEFS ----------------------------------------------------------------------------*/ |
||
| 116 | extern struct file_operations pcicc32_fops; |
||
| 117 | |||
| 118 | /*--- DRIVER GLOBALS ----------------------------------------------------------------------*/ |
||
| 119 | static List *pci_device_header = NULL; /* list of all PCIADAs found */ |
||
| 120 | List *pcicc32_work_device_header = NULL; /* list of working device interfaces */ |
||
| 121 | static unsigned int nMajor; |
||
| 122 | |||
| 123 | /*--- FUNCTIONS ---------------------------------------------------------------------------*/ |
||
| 124 | static int my_interrupt(u16 intCSR) |
||
| 125 | { |
||
| 126 | int result = NOT_MY_INTERRUPT; |
||
| 127 | |||
| 128 | if (intCSR & 0x0040) // it is global enabled |
||
| 129 | { |
||
| 130 | if ((intCSR & 0x0028) == 0x0028) // it is a enabled PCIADA interrupt |
||
| 131 | result = PCIADA_INTERRUPT; |
||
| 132 | else |
||
| 133 | if ((intCSR & 0x0005) == 0x0005) // it is a enabled CC32 interrupt |
||
| 134 | result = CC32_INTERRUPT; |
||
| 135 | } |
||
| 136 | |||
| 137 | return result; |
||
| 138 | } |
||
| 139 | |||
| 140 | static irqreturn_t cc32_irqhandler(int irq, void *dev_id, struct pt_regs *regs) |
||
| 141 | { |
||
| 142 | CC32_DESCRIPTOR *wd = (CC32_DESCRIPTOR *)dev_id; |
||
| 143 | |||
| 144 | if (wd) |
||
| 145 | { |
||
| 146 | // evaluate the reason of the interrupt - if it is mine |
||
| 147 | u16 intCSR = readw(wd->pLCR + PLX9050_INTCSR); |
||
| 148 | int which_interrupt = my_interrupt(intCSR); |
||
| 149 | |||
| 150 | if (which_interrupt) |
||
| 151 | { |
||
| 152 | writew(intCSR & ~0x40, wd->pLCR + PLX9050_INTCSR); /* disable global interrupts */ |
||
| 153 | wd->wIrqStatus = (u16)which_interrupt; |
||
| 154 | wd->dwInterruptCount++; |
||
| 155 | wake_up_interruptible(&wd->event_queue); /* stop blocking if any */ |
||
| 156 | } |
||
| 157 | |||
| 158 | return IRQ_RETVAL(1); |
||
| 159 | } |
||
| 160 | |||
| 161 | return IRQ_RETVAL(0); |
||
| 162 | } |
||
| 163 | |||
| 164 | static int request_io_memory(PCIConfigHeader *pPch) |
||
| 165 | { |
||
| 166 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) |
||
| 167 | if (check_mem_region(pPch->desc[0].base_address, pPch->desc[0].size)) |
||
| 168 | return -EBUSY; |
||
| 169 | |||
| 170 | if (check_mem_region(pPch->desc[3].base_address, pPch->desc[3].size)) |
||
| 171 | return -EBUSY; |
||
| 172 | |||
| 173 | request_mem_region(pPch->desc[0].base_address, pPch->desc[0].size, DEVICE_NAME); |
||
| 174 | request_mem_region(pPch->desc[3].base_address, pPch->desc[3].size, DEVICE_NAME); |
||
| 175 | #endif |
||
| 176 | |||
| 177 | return 0; |
||
| 178 | } |
||
| 179 | |||
| 180 | static void release_io_memory(PCIConfigHeader *pPch) |
||
| 181 | { |
||
| 182 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) |
||
| 183 | release_mem_region(pPch->desc[0].base_address, pPch->desc[0].size); |
||
| 184 | release_mem_region(pPch->desc[3].base_address, pPch->desc[3].size); |
||
| 185 | #endif |
||
| 186 | } |
||
| 187 | |||
| 188 | static int translate_addresses(CC32_DESCRIPTOR *pWd, PCIConfigHeader *pPch) /* differs from PCIVME */ |
||
| 189 | { |
||
| 190 | if (pPch->desc[0].type == PCI_BASE_ADDRESS_MEM_TYPE_1M) /* LCR ISA base addresses */ |
||
| 191 | pWd->pLCR = bus_to_virt(pPch->desc[0].base_address); |
||
| 192 | else |
||
| 193 | pWd->pLCR = ioremap(pPch->desc[0].base_address, pPch->desc[0].size); |
||
| 194 | |||
| 195 | if (pPch->desc[3].type == PCI_BASE_ADDRESS_MEM_TYPE_1M) /* User ISA base addresses */ |
||
| 196 | pWd->pUsr = bus_to_virt(pPch->desc[3].base_address); |
||
| 197 | else |
||
| 198 | pWd->pUsr = ioremap(pPch->desc[3].base_address, pPch->desc[3].size); |
||
| 199 | |||
| 200 | return 0; |
||
| 201 | } |
||
| 202 | |||
| 203 | static void un_translate_addresses(CC32_DESCRIPTOR *pWd, PCIConfigHeader *pPch) |
||
| 204 | { |
||
| 205 | if (pPch->desc[0].type != PCI_BASE_ADDRESS_MEM_TYPE_1M) /* no LCR ISA base addresses */ |
||
| 206 | iounmap((void *)pWd->pLCR); |
||
| 207 | if (pPch->desc[3].type != PCI_BASE_ADDRESS_MEM_TYPE_1M) |
||
| 208 | iounmap((void *)pWd->pUsr); |
||
| 209 | } |
||
| 210 | |||
| 211 | static void soft_init(CC32_DESCRIPTOR *pd) |
||
| 212 | { |
||
| 213 | if (pd) |
||
| 214 | { |
||
| 215 | init_waitqueue_head(&pd->event_queue); |
||
| 216 | |||
| 217 | pd->pLCR = pd->pUsr = 0; |
||
| 218 | pd->pPch = (PCIConfigHeader *)NULL; |
||
| 219 | pd->cModuleNumber = 255; |
||
| 220 | pd->cFPGAVersion = 255; |
||
| 221 | pd->bConnected = 0; |
||
| 222 | pd->wInitStep = 0; |
||
| 223 | pd->wIrq = 0xFFFF; |
||
| 224 | pd->dwInterruptCount = 0; |
||
| 225 | pd->wIrqStatus = 0; |
||
| 226 | } |
||
| 227 | } |
||
| 228 | |||
| 229 | int test_connection(CC32_DESCRIPTOR *wd) |
||
| 230 | { |
||
| 231 | u16 intCSR_store; |
||
| 232 | u16 cntrl_store; |
||
| 233 | int i = 1000; |
||
| 234 | u32 access_store; |
||
| 235 | int error = 0; |
||
| 236 | __u32 dwData; |
||
| 237 | void *dwAdr = wd->pUsr + MAKE_CC32_ADR(26,0,0); |
||
| 238 | |||
| 239 | cntrl_store = readw(wd->pLCR + PLX9050_CNTRL); /* read CONTROL register */ |
||
| 240 | intCSR_store = readw(wd->pLCR + PLX9050_INTCSR); /* read interrupt + CSR register */ |
||
| 241 | |||
| 242 | writew(0, wd->pLCR + PLX9050_INTCSR); /* disable interrupts */ |
||
| 243 | writew(cntrl_store | 0x0180, wd->pLCR + PLX9050_CNTRL); /* enable access */ |
||
| 244 | |||
| 245 | access_store = readl(dwAdr); |
||
| 246 | while (i--) |
||
| 247 | { |
||
| 248 | writel(0x55555555, dwAdr); |
||
| 249 | dwData = readl(dwAdr) & 0x00FFFFFF; |
||
| 250 | if (0x00555555 != dwData) |
||
| 251 | { |
||
| 252 | DPRINTK(KERN_DEBUG "pcicc32 : write 0x55555555, read 0x%08x\n", dwData); |
||
| 253 | error = 1; |
||
| 254 | break; |
||
| 255 | } |
||
| 256 | writel(0xAAAAAAAA, dwAdr); |
||
| 257 | dwData = readl(dwAdr) & 0x00FFFFFF; |
||
| 258 | if (0x00AAAAAA != dwData) |
||
| 259 | { |
||
| 260 | DPRINTK(KERN_DEBUG "pcicc32 : write 0xAAAAAAAA, read 0x%08x\n", dwData); |
||
| 261 | error = 1; |
||
| 262 | break; |
||
| 263 | } |
||
| 264 | writel(0xFFFFFFFF, dwAdr); |
||
| 265 | dwData = readl(dwAdr) & 0x00FFFFFF; |
||
| 266 | if (0x00FFFFFF != dwData) |
||
| 267 | { |
||
| 268 | DPRINTK(KERN_DEBUG "pcicc32 : write 0xFFFFFFFF, read 0x%08x\n", dwData); |
||
| 269 | error = 1; |
||
| 270 | break; |
||
| 271 | } |
||
| 272 | writel(0x00000000, dwAdr); |
||
| 273 | dwData = readl(dwAdr) & 0x00FFFFFF; |
||
| 274 | if (0x00000000 != dwData) |
||
| 275 | { |
||
| 276 | DPRINTK(KERN_DEBUG "pcicc32 : write 0x00000000, read 0x%08x\n", dwData); |
||
| 277 | error = 1; |
||
| 278 | break; |
||
| 279 | } |
||
| 280 | } |
||
| 281 | |||
| 282 | writew(cntrl_store & ~0x0100, wd->pLCR + PLX9050_CNTRL); /* clear potential interrupt */ |
||
| 283 | |||
| 284 | /* restore all contents */ |
||
| 285 | writel(access_store, dwAdr); |
||
| 286 | writew(cntrl_store, wd->pLCR + PLX9050_CNTRL); |
||
| 287 | writew(intCSR_store, wd->pLCR + PLX9050_INTCSR); |
||
| 288 | |||
| 289 | return error; |
||
| 290 | } |
||
| 291 | |||
| 292 | int get_module_info(CC32_DESCRIPTOR *wd, u8 *cModuleNumber, u8 *cFPGAVersion) |
||
| 293 | { |
||
| 294 | u16 intCSR_store; |
||
| 295 | u16 cntrl_store; |
||
| 296 | int found = 0; |
||
| 297 | u16 data; |
||
| 298 | |||
| 299 | cntrl_store = readw(wd->pLCR + PLX9050_CNTRL); /* read CONTROL register */ |
||
| 300 | intCSR_store = readw(wd->pLCR + PLX9050_INTCSR); /* read interrupt + CSR register */ |
||
| 301 | |||
| 302 | DPRINTK(KERN_DEBUG "pcicc32 : get_module_info(), cntrl=0x%04x, intCSR=0x%04x\n", cntrl_store, intCSR_store); |
||
| 303 | |||
| 304 | if (cntrl_store & 0x0800) /* a CC32 is connected */ |
||
| 305 | { |
||
| 306 | u16 bla = cntrl_store | 0x0180; |
||
| 307 | |||
| 308 | writew(0, wd->pLCR + PLX9050_INTCSR); /* disable interrupts */ |
||
| 309 | writew(bla, wd->pLCR + PLX9050_CNTRL); /* enable access */ |
||
| 310 | |||
| 311 | data = readw((wd->pUsr + MAKE_CC32_ADR(0,0,0))); |
||
| 312 | |||
| 313 | DPRINTK(KERN_DEBUG "pcicc32 : CC32 status=0x%04x\n", data); |
||
| 314 | |||
| 315 | if ((data & 0xF000) != 0x8000) |
||
| 316 | { |
||
| 317 | *cModuleNumber = *cFPGAVersion = 255; |
||
| 318 | printk(KERN_ERR "pcicc32 : Wrong module type connected @ index %d (0x%04x)!\n", wd->wIndex, data); |
||
| 319 | } |
||
| 320 | else |
||
| 321 | { |
||
| 322 | found = 1; |
||
| 323 | *cModuleNumber = (data >> 4) & 0xF; |
||
| 324 | *cFPGAVersion = (data >> 8) & 0xF; |
||
| 325 | } |
||
| 326 | |||
| 327 | writew(cntrl_store & ~0x0080, wd->pLCR + PLX9050_CNTRL); /* clear potential interrupt */ |
||
| 328 | |||
| 329 | /* restore all contents */ |
||
| 330 | writew(cntrl_store, wd->pLCR + PLX9050_CNTRL); |
||
| 331 | writew(intCSR_store, wd->pLCR + PLX9050_INTCSR); |
||
| 332 | } |
||
| 333 | |||
| 334 | return found; |
||
| 335 | } |
||
| 336 | |||
| 337 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) |
||
| 338 | static int pcicc32_read_proc(char *buf, char **start, off_t offset, int len) |
||
| 339 | #else |
||
| 340 | static int pcicc32_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *data) |
||
| 341 | #endif |
||
| 342 | { |
||
| 343 | int i; |
||
| 344 | int pos = 0; |
||
| 345 | CC32_DESCRIPTOR *pd; |
||
| 346 | PCIConfigHeader *ch; |
||
| 347 | Node *n; |
||
| 348 | u16 cntrl; |
||
| 349 | char *cause = "None"; |
||
| 350 | |||
| 351 | DPRINTK(KERN_DEBUG "pcicc32 : pcicc32_read_proc()\n"); |
||
| 352 | |||
| 353 | pos += sprintf(buf + pos, "\npcicc32 information. Version %d.%d of %s from Klaus Hitschler.\n", VERSION_HI, VERSION_LO, __DATE__); |
||
| 354 | |||
| 355 | i = getNumOfNodesInList(pcicc32_work_device_header); |
||
| 356 | pos += sprintf(buf + pos, " ---------------------\n"); |
||
| 357 | pos += sprintf(buf + pos, " Interfaces found : %d\n", i); |
||
| 358 | |||
| 359 | n = getFirstNode(pcicc32_work_device_header); |
||
| 360 | while (i--) |
||
| 361 | { |
||
| 362 | pd = (CC32_DESCRIPTOR *)getContent(n); |
||
| 363 | ch = pd->pPch; |
||
| 364 | cntrl = readw(pd->pLCR + PLX9050_CNTRL); |
||
| 365 | pos += sprintf(buf + pos, " --- %d ---------------\n", i + 1); |
||
| 366 | pos += sprintf(buf + pos, " LCR phys/virt/size : 0x%08x/0x%p/%d\n", ch->desc[0].base_address, pd->pLCR, ch->desc[0].size); |
||
| 367 | pos += sprintf(buf + pos, " User phys/virt/size : 0x%08x/0x%p/%d\n", ch->desc[3].base_address, pd->pUsr, ch->desc[3].size); |
||
| 368 | pos += sprintf(buf + pos, " Irq : %d\n", pd->wIrq); |
||
| 369 | |||
| 370 | if (pd->bConnected) |
||
| 371 | { |
||
| 372 | pos += sprintf(buf + pos, " CC32 is or was : (software) connected.\n"); |
||
| 373 | pos += sprintf(buf + pos, " Module-Number : %d\n", pd->cModuleNumber); |
||
| 374 | pos += sprintf(buf + pos, " FPGA-Version : %d\n", pd->cFPGAVersion); |
||
| 375 | } |
||
| 376 | else |
||
| 377 | pos += sprintf(buf + pos, " CC32 is or was : not (software) connected.\n"); |
||
| 378 | |||
| 379 | if (!((cntrl & 0x0800) && (!(cntrl & 0x0600)))) |
||
| 380 | pos += sprintf(buf + pos, " CC32 is : powered off or cable disconnected.\n"); |
||
| 381 | |||
| 382 | pos += sprintf(buf + pos, " IrqCount : %d\n", pd->dwInterruptCount); |
||
| 383 | if (pd->wIrqStatus & PCIADA_INTERRUPT) |
||
| 384 | cause = "Timeout"; |
||
| 385 | else |
||
| 386 | if (pd->wIrqStatus & CC32_INTERRUPT) |
||
| 387 | cause = "LAM"; |
||
| 388 | pos += sprintf(buf + pos, " Pending IrqStatus : %s\n", cause); |
||
| 389 | |||
| 390 | n = getNextNode(n); |
||
| 391 | } |
||
| 392 | |||
| 393 | pos += sprintf(buf + pos, "\n"); |
||
| 394 | |||
| 395 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,19) |
||
| 396 | *eof = 1; |
||
| 397 | #endif |
||
| 398 | |||
| 399 | return pos; |
||
| 400 | } |
||
| 401 | |||
| 402 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,2,19) |
||
| 403 | //---------------------------------------------------------------------------- |
||
| 404 | // replacement for kernel 2.4.x function |
||
| 405 | // original from sysdep.h out from the book |
||
| 406 | // "Linux Device Drivers" by Alessandro Rubini and Jonathan Corbet, published by O'Reilly & Associates. |
||
| 407 | static struct proc_dir_entry *create_proc_read_entry(const char *name, mode_t mode, |
||
| 408 | struct proc_dir_entry *base, read_proc_t *read_proc, void * data) |
||
| 409 | { |
||
| 410 | struct proc_dir_entry *res = create_proc_entry(name, mode, base); |
||
| 411 | if (res) |
||
| 412 | { |
||
| 413 | res->read_proc=read_proc; |
||
| 414 | res->data=data; |
||
| 415 | } |
||
| 416 | return res; |
||
| 417 | } |
||
| 418 | #endif |
||
| 419 | |||
| 420 | |||
| 421 | int init_module(void) |
||
| 422 | { |
||
| 423 | int i = 0; |
||
| 424 | PCIConfigHeader *ch; |
||
| 425 | CC32_DESCRIPTOR *wd; |
||
| 426 | Node *n; |
||
| 427 | int result = 0; |
||
| 428 | |||
| 429 | printk(KERN_INFO "pcicc32 : init_module\n"); |
||
| 430 | |||
| 431 | /* create list of PCIADAs and work devices */ |
||
| 432 | pci_device_header = newList(); |
||
| 433 | pcicc32_work_device_header = newList(); |
||
| 434 | |||
| 435 | /* search for all PCIADA modules */ |
||
| 436 | while ((ch = GetPCIConfigHeader(ARWVME_VENDOR_ID, ARWVME_DEVICE_ID, i))) |
||
| 437 | { |
||
| 438 | if (ch->subsystem_id != ARWVME_SUBSYSTEM_ID) |
||
| 439 | { |
||
| 440 | printk(KERN_ERR "pcicc32 : found 0x%x, 0x%x , but wrong subsystem id 0x%04x!\n", |
||
| 441 | ARWVME_VENDOR_ID, ARWVME_DEVICE_ID, ch->subsystem_id); |
||
| 442 | i++; |
||
| 443 | kfree_s(ch, sizeof(*ch)); // FREE(ch); |
||
| 444 | continue; |
||
| 445 | } |
||
| 446 | |||
| 447 | if (ch->subsystem_vendor_id != ARWVME_SUBSYSTEM_VENDOR_ID) |
||
| 448 | { |
||
| 449 | printk(KERN_ERR "pcicc32 : found 0x%x, 0x%x , but wrong subsystem vendor Id 0x%04x!\n", |
||
| 450 | ARWVME_VENDOR_ID, ARWVME_DEVICE_ID, ch->subsystem_vendor_id); |
||
| 451 | i++; |
||
| 452 | kfree_s(ch, sizeof(*ch)); // FREE(ch); |
||
| 453 | continue; |
||
| 454 | } |
||
| 455 | |||
| 456 | /* test if it is configured as PCICC32 or a PCIVME interface card */ |
||
| 457 | if (ch->desc[3].size != 0x8000) |
||
| 458 | { |
||
| 459 | printk(KERN_ERR "pcicc32 : found, but wrong memory window size 0x%08x!\n", ch->desc[3].size); |
||
| 460 | i++; |
||
| 461 | kfree_s(ch, sizeof(*ch)); // FREE(ch); |
||
| 462 | continue; |
||
| 463 | } |
||
| 464 | |||
| 465 | printk(KERN_INFO "pcicc32 : found %d. PCIADA card\n", i + 1); |
||
| 466 | addTail(pci_device_header, ch); /* add this header to list */ |
||
| 467 | i++; |
||
| 468 | } |
||
| 469 | |||
| 470 | /* fix the PLX bug in all PCIADAs */ |
||
| 471 | i = getNumOfNodesInList(pci_device_header); |
||
| 472 | n = getFirstNode(pci_device_header); |
||
| 473 | while (i--) |
||
| 474 | { |
||
| 475 | ch = (PCIConfigHeader *)getContent(n); |
||
| 476 | PLX9050BugFix(ch); |
||
| 477 | n = getNextNode(n); |
||
| 478 | } |
||
| 479 | |||
| 480 | /* create work_devices and translate the access addresses */ |
||
| 481 | i = getNumOfNodesInList(pci_device_header); |
||
| 482 | n = getFirstNode(pci_device_header); |
||
| 483 | while (i--) |
||
| 484 | { |
||
| 485 | wd = (CC32_DESCRIPTOR *)kmalloc(sizeof(CC32_DESCRIPTOR), GFP_ATOMIC); |
||
| 486 | soft_init(wd); |
||
| 487 | ch = (PCIConfigHeader *)getContent(n); |
||
| 488 | wd->pPch = ch; |
||
| 489 | wd->wIndex = i; |
||
| 490 | |||
| 491 | if (!request_io_memory(ch)) |
||
| 492 | { |
||
| 493 | // successful request_io_memory |
||
| 494 | wd->wInitStep = 1; |
||
| 495 | |||
| 496 | if (translate_addresses(wd, ch)) |
||
| 497 | { |
||
| 498 | printk(KERN_ERR "pcicc32 : translation of addresses failed!\n"); |
||
| 499 | release_io_memory(ch); |
||
| 500 | kfree_s(wd, sizeof(*wd)); // FREE(wd); |
||
| 501 | } |
||
| 502 | else |
||
| 503 | { |
||
| 504 | // successful translate_addresses |
||
| 505 | wd->wInitStep = 2; |
||
| 506 | |||
| 507 | if (request_irq(wd->pPch->PCI_dev->irq, cc32_irqhandler, IRQF_DISABLED| IRQF_SHARED, DEVICE_NAME, wd)) |
||
| 508 | { |
||
| 509 | printk(KERN_ERR "pcicc32 : can't get irq @ %d\n", wd->pPch->PCI_dev->irq); |
||
| 510 | un_translate_addresses(wd, ch); |
||
| 511 | release_io_memory(ch); |
||
| 512 | kfree_s(wd, sizeof(*wd)); // FREE(wd); |
||
| 513 | } |
||
| 514 | else |
||
| 515 | { |
||
| 516 | // successful request_irq |
||
| 517 | wd->wInitStep = 3; |
||
| 518 | wd->wIrq = wd->pPch->PCI_dev->irq; |
||
| 519 | |||
| 520 | addTail(pcicc32_work_device_header, wd); /* add this device to list */ |
||
| 521 | wd->bConnected = get_module_info(wd, &wd->cModuleNumber, &wd->cFPGAVersion); |
||
| 522 | if (wd->bConnected && test_connection(wd)) |
||
| 523 | { |
||
| 524 | printk(KERN_ERR "pcicc32 : connection test failed!\n"); |
||
| 525 | wd->bConnected = 0; |
||
| 526 | } |
||
| 527 | } |
||
| 528 | } |
||
| 529 | } |
||
| 530 | else |
||
| 531 | { |
||
| 532 | printk(KERN_ERR "pcicc32 : requested io-memory still claimed!\n"); |
||
| 533 | kfree_s(wd, sizeof(*wd)); // FREE(wd); |
||
| 534 | } |
||
| 535 | |||
| 536 | n = getNextNode(n); |
||
| 537 | } |
||
| 538 | |||
| 539 | nMajor = MAJOR_NO; |
||
| 540 | result = register_chrdev(nMajor, DEVICE_NAME, &pcicc32_fops); |
||
| 541 | if (result < 0) |
||
| 542 | { |
||
| 543 | printk(KERN_ERR "pcicc32: Can't install driver (%d)\n", result); |
||
| 544 | |||
| 545 | /* untranslate translated addresses */ |
||
| 546 | i = getNumOfNodesInList(pcicc32_work_device_header); |
||
| 547 | n = getFirstNode(pcicc32_work_device_header); |
||
| 548 | while (i--) |
||
| 549 | { |
||
| 550 | wd = (CC32_DESCRIPTOR *)getContent(n); |
||
| 551 | ch = wd->pPch; |
||
| 552 | un_translate_addresses(wd, ch); |
||
| 553 | n = getNextNode(n); |
||
| 554 | } |
||
| 555 | |||
| 556 | /* delete my lists */ |
||
| 557 | deleteList(pcicc32_work_device_header, (void (*)(void *))NULL); |
||
| 558 | deleteList(pci_device_header, (void (*)(void *))NULL); |
||
| 559 | |||
| 560 | return result; |
||
| 561 | } |
||
| 562 | else |
||
| 563 | { |
||
| 564 | if (nMajor == 0) nMajor = result; |
||
| 565 | printk(KERN_INFO "pcicc32 : major #%d assigned.\n", nMajor); |
||
| 566 | } |
||
| 567 | |||
| 568 | /* register the proc device */ |
||
| 569 | |||
| 570 | return proc_create_data(DEVICE_NAME, 0, NULL, &pcicc32_fops, NULL) ? 0 : -ENODEV; |
||
| 571 | } |
||
| 572 | |||
| 573 | void cleanup_module(void) |
||
| 574 | { |
||
| 575 | int i; |
||
| 576 | PCIConfigHeader *ch; |
||
| 577 | CC32_DESCRIPTOR *wd; |
||
| 578 | Node *n; |
||
| 579 | |||
| 580 | unregister_chrdev(nMajor, DEVICE_NAME); |
||
| 581 | |||
| 582 | DPRINTK(KERN_DEBUG "pcicc32 : cleanup_module()\n"); |
||
| 583 | |||
| 584 | /* unregister the proc device */ |
||
| 585 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) |
||
| 586 | proc_unregister(&proc_root, pcimod_proc_entry.low_ino); |
||
| 587 | #else |
||
| 588 | remove_proc_entry(DEVICE_NAME, NULL); |
||
| 589 | #endif |
||
| 590 | |||
| 591 | /* untranslate translated addresses */ |
||
| 592 | i = getNumOfNodesInList(pcicc32_work_device_header); |
||
| 593 | n = getFirstNode(pcicc32_work_device_header); |
||
| 594 | while (i--) |
||
| 595 | { |
||
| 596 | wd = (CC32_DESCRIPTOR *)getContent(n); |
||
| 597 | ch = wd->pPch; |
||
| 598 | switch (wd->wInitStep) |
||
| 599 | { |
||
| 600 | case 3: writew(readw(wd->pLCR + PLX9050_INTCSR) & ~0x40, wd->pLCR + PLX9050_INTCSR); // disable global interrupts |
||
| 601 | free_irq(wd->wIrq, wd); |
||
| 602 | case 2: un_translate_addresses(wd, ch); |
||
| 603 | case 1: release_io_memory(ch); |
||
| 604 | default: wd->wInitStep = 0; |
||
| 605 | } |
||
| 606 | |||
| 607 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) |
||
| 608 | if ((ch) && (ch->PCI_dev)) |
||
| 609 | pci_disable_device(ch->PCI_dev); |
||
| 610 | #endif |
||
| 611 | |||
| 612 | n = getNextNode(n); |
||
| 613 | } |
||
| 614 | |||
| 615 | /* delete my lists */ |
||
| 616 | deleteList(pcicc32_work_device_header, (void (*)(void *))NULL); |
||
| 617 | deleteList(pci_device_header, (void (*)(void *))NULL); |
||
| 618 | |||
| 619 | printk(KERN_INFO "pcicc32 : cleanup_module successful.\n"); |
||
| 620 | |||
| 621 | return; |
||
| 622 | } |
||
| 623 |