linux/drivers/pcmcia/i82092.c
<<
>>
Prefs
   1/* 
   2 * Driver for Intel I82092AA PCI-PCMCIA bridge.
   3 *
   4 * (C) 2001 Red Hat, Inc.
   5 *
   6 * Author: Arjan Van De Ven <arjanv@redhat.com>
   7 * Loosly based on i82365.c from the pcmcia-cs package
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/pci.h>
  13#include <linux/init.h>
  14#include <linux/workqueue.h>
  15#include <linux/interrupt.h>
  16#include <linux/device.h>
  17
  18#include <pcmcia/ss.h>
  19
  20#include <asm/io.h>
  21
  22#include "i82092aa.h"
  23#include "i82365.h"
  24
  25MODULE_LICENSE("GPL");
  26
  27/* PCI core routines */
  28static DEFINE_PCI_DEVICE_TABLE(i82092aa_pci_ids) = {
  29        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) },
  30        { }
  31};
  32MODULE_DEVICE_TABLE(pci, i82092aa_pci_ids);
  33
  34static struct pci_driver i82092aa_pci_driver = {
  35        .name           = "i82092aa",
  36        .id_table       = i82092aa_pci_ids,
  37        .probe          = i82092aa_pci_probe,
  38        .remove         = i82092aa_pci_remove,
  39};
  40
  41
  42/* the pccard structure and its functions */
  43static struct pccard_operations i82092aa_operations = {
  44        .init                   = i82092aa_init,
  45        .get_status             = i82092aa_get_status,
  46        .set_socket             = i82092aa_set_socket,
  47        .set_io_map             = i82092aa_set_io_map,
  48        .set_mem_map            = i82092aa_set_mem_map,
  49};
  50
  51/* The card can do up to 4 sockets, allocate a structure for each of them */
  52
  53struct socket_info {
  54        int     number;
  55        int     card_state;     /*  0 = no socket,
  56                                    1 = empty socket, 
  57                                    2 = card but not initialized,
  58                                    3 = operational card */
  59        unsigned int io_base;   /* base io address of the socket */
  60        
  61        struct pcmcia_socket socket;
  62        struct pci_dev *dev;    /* The PCI device for the socket */
  63};
  64
  65#define MAX_SOCKETS 4
  66static struct socket_info sockets[MAX_SOCKETS];
  67static int socket_count;  /* shortcut */                                                                                
  68
  69
  70static int i82092aa_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
  71{
  72        unsigned char configbyte;
  73        int i, ret;
  74        
  75        enter("i82092aa_pci_probe");
  76        
  77        if ((ret = pci_enable_device(dev)))
  78                return ret;
  79                
  80        pci_read_config_byte(dev, 0x40, &configbyte);  /* PCI Configuration Control */
  81        switch(configbyte&6) {
  82                case 0:
  83                        socket_count = 2;
  84                        break;
  85                case 2:
  86                        socket_count = 1;
  87                        break;
  88                case 4:
  89                case 6:
  90                        socket_count = 4;
  91                        break;
  92                        
  93                default:
  94                        printk(KERN_ERR "i82092aa: Oops, you did something we didn't think of.\n");
  95                        ret = -EIO;
  96                        goto err_out_disable;
  97        }
  98        printk(KERN_INFO "i82092aa: configured as a %d socket device.\n", socket_count);
  99
 100        if (!request_region(pci_resource_start(dev, 0), 2, "i82092aa")) {
 101                ret = -EBUSY;
 102                goto err_out_disable;
 103        }
 104        
 105        for (i = 0;i<socket_count;i++) {
 106                sockets[i].card_state = 1; /* 1 = present but empty */
 107                sockets[i].io_base = pci_resource_start(dev, 0);
 108                sockets[i].socket.features |= SS_CAP_PCCARD;
 109                sockets[i].socket.map_size = 0x1000;
 110                sockets[i].socket.irq_mask = 0;
 111                sockets[i].socket.pci_irq  = dev->irq;
 112                sockets[i].socket.cb_dev  = dev;
 113                sockets[i].socket.owner = THIS_MODULE;
 114
 115                sockets[i].number = i;
 116                
 117                if (card_present(i)) {
 118                        sockets[i].card_state = 3;
 119                        dprintk(KERN_DEBUG "i82092aa: slot %i is occupied\n",i);
 120                } else {
 121                        dprintk(KERN_DEBUG "i82092aa: slot %i is vacant\n",i);
 122                }
 123        }
 124                
 125        /* Now, specifiy that all interrupts are to be done as PCI interrupts */
 126        configbyte = 0xFF; /* bitmask, one bit per event, 1 = PCI interrupt, 0 = ISA interrupt */
 127        pci_write_config_byte(dev, 0x50, configbyte); /* PCI Interrupt Routing Register */
 128
 129        /* Register the interrupt handler */
 130        dprintk(KERN_DEBUG "Requesting interrupt %i \n",dev->irq);
 131        if ((ret = request_irq(dev->irq, i82092aa_interrupt, IRQF_SHARED, "i82092aa", i82092aa_interrupt))) {
 132                printk(KERN_ERR "i82092aa: Failed to register IRQ %d, aborting\n", dev->irq);
 133                goto err_out_free_res;
 134        }
 135
 136        for (i = 0; i<socket_count; i++) {
 137                sockets[i].socket.dev.parent = &dev->dev;
 138                sockets[i].socket.ops = &i82092aa_operations;
 139                sockets[i].socket.resource_ops = &pccard_nonstatic_ops;
 140                ret = pcmcia_register_socket(&sockets[i].socket);
 141                if (ret) {
 142                        goto err_out_free_sockets;
 143                }
 144        }
 145
 146        leave("i82092aa_pci_probe");
 147        return 0;
 148
 149err_out_free_sockets:
 150        if (i) {
 151                for (i--;i>=0;i--) {
 152                        pcmcia_unregister_socket(&sockets[i].socket);
 153                }
 154        }
 155        free_irq(dev->irq, i82092aa_interrupt);
 156err_out_free_res:
 157        release_region(pci_resource_start(dev, 0), 2);
 158err_out_disable:
 159        pci_disable_device(dev);
 160        return ret;                     
 161}
 162
 163static void i82092aa_pci_remove(struct pci_dev *dev)
 164{
 165        int i;
 166
 167        enter("i82092aa_pci_remove");
 168        
 169        free_irq(dev->irq, i82092aa_interrupt);
 170
 171        for (i = 0; i < socket_count; i++)
 172                pcmcia_unregister_socket(&sockets[i].socket);
 173
 174        leave("i82092aa_pci_remove");
 175}
 176
 177static DEFINE_SPINLOCK(port_lock);
 178
 179/* basic value read/write functions */
 180
 181static unsigned char indirect_read(int socket, unsigned short reg)
 182{
 183        unsigned short int port;
 184        unsigned char val;
 185        unsigned long flags;
 186        spin_lock_irqsave(&port_lock,flags);
 187        reg += socket * 0x40;
 188        port = sockets[socket].io_base;
 189        outb(reg,port);
 190        val = inb(port+1);
 191        spin_unlock_irqrestore(&port_lock,flags);
 192        return val;
 193}
 194
 195#if 0
 196static unsigned short indirect_read16(int socket, unsigned short reg)
 197{
 198        unsigned short int port;
 199        unsigned short tmp;
 200        unsigned long flags;
 201        spin_lock_irqsave(&port_lock,flags);
 202        reg  = reg + socket * 0x40;
 203        port = sockets[socket].io_base;
 204        outb(reg,port);
 205        tmp = inb(port+1);
 206        reg++;
 207        outb(reg,port);
 208        tmp = tmp | (inb(port+1)<<8);
 209        spin_unlock_irqrestore(&port_lock,flags);
 210        return tmp;
 211}
 212#endif
 213
 214static void indirect_write(int socket, unsigned short reg, unsigned char value)
 215{
 216        unsigned short int port;
 217        unsigned long flags;
 218        spin_lock_irqsave(&port_lock,flags);
 219        reg = reg + socket * 0x40;
 220        port = sockets[socket].io_base; 
 221        outb(reg,port);
 222        outb(value,port+1);
 223        spin_unlock_irqrestore(&port_lock,flags);
 224}
 225
 226static void indirect_setbit(int socket, unsigned short reg, unsigned char mask)
 227{
 228        unsigned short int port;
 229        unsigned char val;
 230        unsigned long flags;
 231        spin_lock_irqsave(&port_lock,flags);
 232        reg = reg + socket * 0x40;
 233        port = sockets[socket].io_base; 
 234        outb(reg,port);
 235        val = inb(port+1);
 236        val |= mask;
 237        outb(reg,port);
 238        outb(val,port+1);
 239        spin_unlock_irqrestore(&port_lock,flags);
 240}
 241
 242
 243static void indirect_resetbit(int socket, unsigned short reg, unsigned char mask)
 244{
 245        unsigned short int port;
 246        unsigned char val;
 247        unsigned long flags;
 248        spin_lock_irqsave(&port_lock,flags);
 249        reg = reg + socket * 0x40;
 250        port = sockets[socket].io_base; 
 251        outb(reg,port);
 252        val = inb(port+1);
 253        val &= ~mask;
 254        outb(reg,port);
 255        outb(val,port+1);
 256        spin_unlock_irqrestore(&port_lock,flags);
 257}
 258
 259static void indirect_write16(int socket, unsigned short reg, unsigned short value)
 260{
 261        unsigned short int port;
 262        unsigned char val;
 263        unsigned long flags;
 264        spin_lock_irqsave(&port_lock,flags);
 265        reg = reg + socket * 0x40;
 266        port = sockets[socket].io_base; 
 267        
 268        outb(reg,port);
 269        val = value & 255;
 270        outb(val,port+1);
 271        
 272        reg++;
 273        
 274        outb(reg,port);
 275        val = value>>8;
 276        outb(val,port+1);
 277        spin_unlock_irqrestore(&port_lock,flags);
 278}
 279
 280/* simple helper functions */
 281/* External clock time, in nanoseconds.  120 ns = 8.33 MHz */
 282static int cycle_time = 120;
 283
 284static int to_cycles(int ns)
 285{
 286        if (cycle_time!=0)
 287                return ns/cycle_time;
 288        else
 289                return 0;
 290}
 291    
 292
 293/* Interrupt handler functionality */
 294
 295static irqreturn_t i82092aa_interrupt(int irq, void *dev)
 296{
 297        int i;
 298        int loopcount = 0;
 299        int handled = 0;
 300
 301        unsigned int events, active=0;
 302        
 303/*      enter("i82092aa_interrupt");*/
 304        
 305        while (1) {
 306                loopcount++;
 307                if (loopcount>20) {
 308                        printk(KERN_ERR "i82092aa: infinite eventloop in interrupt \n");
 309                        break;
 310                }
 311                
 312                active = 0;
 313                
 314                for (i=0;i<socket_count;i++) {
 315                        int csc;
 316                        if (sockets[i].card_state==0) /* Inactive socket, should not happen */
 317                                continue;
 318                        
 319                        csc = indirect_read(i,I365_CSC); /* card status change register */
 320                        
 321                        if (csc==0)  /* no events on this socket */
 322                                continue;
 323                        handled = 1;
 324                        events = 0;
 325                         
 326                        if (csc & I365_CSC_DETECT) {
 327                                events |= SS_DETECT;
 328                                printk("Card detected in socket %i!\n",i);
 329                         }
 330                        
 331                        if (indirect_read(i,I365_INTCTL) & I365_PC_IOCARD) { 
 332                                /* For IO/CARDS, bit 0 means "read the card" */
 333                                events |= (csc & I365_CSC_STSCHG) ? SS_STSCHG : 0; 
 334                        } else {
 335                                /* Check for battery/ready events */
 336                                events |= (csc & I365_CSC_BVD1) ? SS_BATDEAD : 0;
 337                                events |= (csc & I365_CSC_BVD2) ? SS_BATWARN : 0;
 338                                events |= (csc & I365_CSC_READY) ? SS_READY : 0;
 339                        }
 340                        
 341                        if (events) {
 342                                pcmcia_parse_events(&sockets[i].socket, events);
 343                        }
 344                        active |= events;
 345                }
 346                                
 347                if (active==0) /* no more events to handle */
 348                        break;                          
 349                
 350        }
 351        return IRQ_RETVAL(handled);
 352/*      leave("i82092aa_interrupt");*/
 353}
 354
 355
 356
 357/* socket functions */
 358
 359static int card_present(int socketno)
 360{       
 361        unsigned int val;
 362        enter("card_present");
 363        
 364        if ((socketno<0) || (socketno >= MAX_SOCKETS))
 365                return 0;
 366        if (sockets[socketno].io_base == 0)
 367                return 0;
 368
 369                
 370        val = indirect_read(socketno, 1); /* Interface status register */
 371        if ((val&12)==12) {
 372                leave("card_present 1");
 373                return 1;
 374        }
 375                
 376        leave("card_present 0");
 377        return 0;
 378}
 379
 380static void set_bridge_state(int sock)
 381{
 382        enter("set_bridge_state");
 383        indirect_write(sock, I365_GBLCTL,0x00);
 384        indirect_write(sock, I365_GENCTL,0x00);
 385        
 386        indirect_setbit(sock, I365_INTCTL,0x08);
 387        leave("set_bridge_state");
 388}
 389
 390
 391
 392
 393
 394      
 395static int i82092aa_init(struct pcmcia_socket *sock)
 396{
 397        int i;
 398        struct resource res = { .start = 0, .end = 0x0fff };
 399        pccard_io_map io = { 0, 0, 0, 0, 1 };
 400        pccard_mem_map mem = { .res = &res, };
 401        
 402        enter("i82092aa_init");
 403                        
 404        for (i = 0; i < 2; i++) {
 405                io.map = i;
 406                i82092aa_set_io_map(sock, &io);
 407        }
 408        for (i = 0; i < 5; i++) {
 409                mem.map = i;
 410                i82092aa_set_mem_map(sock, &mem);
 411        }
 412        
 413        leave("i82092aa_init");
 414        return 0;
 415}
 416                                                                                                                                                                                                                                              
 417static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value)
 418{
 419        unsigned int sock = container_of(socket, struct socket_info, socket)->number;
 420        unsigned int status;
 421        
 422        enter("i82092aa_get_status");
 423        
 424        status = indirect_read(sock,I365_STATUS); /* Interface Status Register */
 425        *value = 0;
 426        
 427        if ((status & I365_CS_DETECT) == I365_CS_DETECT) {
 428                *value |= SS_DETECT;
 429        }
 430                
 431        /* IO cards have a different meaning of bits 0,1 */
 432        /* Also notice the inverse-logic on the bits */
 433         if (indirect_read(sock, I365_INTCTL) & I365_PC_IOCARD) {
 434                /* IO card */
 435                if (!(status & I365_CS_STSCHG))
 436                        *value |= SS_STSCHG;
 437         } else { /* non I/O card */
 438                if (!(status & I365_CS_BVD1))
 439                        *value |= SS_BATDEAD;
 440                if (!(status & I365_CS_BVD2))
 441                        *value |= SS_BATWARN;
 442                        
 443         }
 444         
 445         if (status & I365_CS_WRPROT)
 446                (*value) |= SS_WRPROT;  /* card is write protected */
 447         
 448         if (status & I365_CS_READY)
 449                (*value) |= SS_READY;    /* card is not busy */
 450                
 451         if (status & I365_CS_POWERON)
 452                (*value) |= SS_POWERON;  /* power is applied to the card */
 453
 454
 455        leave("i82092aa_get_status");
 456        return 0;
 457}
 458
 459
 460static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state) 
 461{
 462        unsigned int sock = container_of(socket, struct socket_info, socket)->number;
 463        unsigned char reg;
 464        
 465        enter("i82092aa_set_socket");
 466        
 467        /* First, set the global controller options */
 468        
 469        set_bridge_state(sock);
 470        
 471        /* Values for the IGENC register */
 472        
 473        reg = 0;
 474        if (!(state->flags & SS_RESET))         /* The reset bit has "inverse" logic */
 475                reg = reg | I365_PC_RESET;  
 476        if (state->flags & SS_IOCARD) 
 477                reg = reg | I365_PC_IOCARD;
 478                
 479        indirect_write(sock,I365_INTCTL,reg); /* IGENC, Interrupt and General Control Register */
 480        
 481        /* Power registers */
 482        
 483        reg = I365_PWR_NORESET; /* default: disable resetdrv on resume */
 484        
 485        if (state->flags & SS_PWR_AUTO) {
 486                printk("Auto power\n");
 487                reg |= I365_PWR_AUTO;   /* automatic power mngmnt */
 488        }
 489        if (state->flags & SS_OUTPUT_ENA) {
 490                printk("Power Enabled \n");
 491                reg |= I365_PWR_OUT;    /* enable power */
 492        }
 493        
 494        switch (state->Vcc) {
 495                case 0: 
 496                        break;
 497                case 50: 
 498                        printk("setting voltage to Vcc to 5V on socket %i\n",sock);
 499                        reg |= I365_VCC_5V;
 500                        break;
 501                default:
 502                        printk("i82092aa: i82092aa_set_socket called with invalid VCC power value: %i ", state->Vcc);
 503                        leave("i82092aa_set_socket");
 504                        return -EINVAL;
 505        }
 506        
 507        
 508        switch (state->Vpp) {
 509                case 0: 
 510                        printk("not setting Vpp on socket %i\n",sock);
 511                        break;
 512                case 50: 
 513                        printk("setting Vpp to 5.0 for socket %i\n",sock);
 514                        reg |= I365_VPP1_5V | I365_VPP2_5V;
 515                        break;
 516                case 120: 
 517                        printk("setting Vpp to 12.0\n");
 518                        reg |= I365_VPP1_12V | I365_VPP2_12V;
 519                        break;
 520                default:
 521                        printk("i82092aa: i82092aa_set_socket called with invalid VPP power value: %i ", state->Vcc);
 522                        leave("i82092aa_set_socket");
 523                        return -EINVAL;
 524        }
 525        
 526        if (reg != indirect_read(sock,I365_POWER)) /* only write if changed */
 527                indirect_write(sock,I365_POWER,reg);
 528                
 529        /* Enable specific interrupt events */
 530        
 531        reg = 0x00;
 532        if (state->csc_mask & SS_DETECT) {
 533                reg |= I365_CSC_DETECT;
 534        }
 535        if (state->flags & SS_IOCARD) {
 536                if (state->csc_mask & SS_STSCHG)
 537                        reg |= I365_CSC_STSCHG;
 538        } else {
 539                if (state->csc_mask & SS_BATDEAD) 
 540                        reg |= I365_CSC_BVD1;
 541                if (state->csc_mask & SS_BATWARN) 
 542                        reg |= I365_CSC_BVD2;
 543                if (state->csc_mask & SS_READY) 
 544                        reg |= I365_CSC_READY; 
 545                                        
 546        }
 547        
 548        /* now write the value and clear the (probably bogus) pending stuff by doing a dummy read*/
 549        
 550        indirect_write(sock,I365_CSCINT,reg);
 551        (void)indirect_read(sock,I365_CSC);
 552
 553        leave("i82092aa_set_socket");
 554        return 0;
 555}
 556
 557static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io)
 558{
 559        unsigned int sock = container_of(socket, struct socket_info, socket)->number;
 560        unsigned char map, ioctl;
 561        
 562        enter("i82092aa_set_io_map");
 563        
 564        map = io->map;
 565        
 566        /* Check error conditions */    
 567        if (map > 1) {
 568                leave("i82092aa_set_io_map with invalid map");
 569                return -EINVAL;
 570        }
 571        if ((io->start > 0xffff) || (io->stop > 0xffff) || (io->stop < io->start)){
 572                leave("i82092aa_set_io_map with invalid io");
 573                return -EINVAL;
 574        }
 575
 576        /* Turn off the window before changing anything */ 
 577        if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_IO(map))
 578                indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_IO(map));
 579
 580/*      printk("set_io_map: Setting range to %x - %x \n",io->start,io->stop);  */
 581        
 582        /* write the new values */
 583        indirect_write16(sock,I365_IO(map)+I365_W_START,io->start);             
 584        indirect_write16(sock,I365_IO(map)+I365_W_STOP,io->stop);               
 585                        
 586        ioctl = indirect_read(sock,I365_IOCTL) & ~I365_IOCTL_MASK(map);
 587        
 588        if (io->flags & (MAP_16BIT|MAP_AUTOSZ))
 589                ioctl |= I365_IOCTL_16BIT(map);
 590                
 591        indirect_write(sock,I365_IOCTL,ioctl);
 592        
 593        /* Turn the window back on if needed */
 594        if (io->flags & MAP_ACTIVE)
 595                indirect_setbit(sock,I365_ADDRWIN,I365_ENA_IO(map));
 596                        
 597        leave("i82092aa_set_io_map");   
 598        return 0;
 599}
 600
 601static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem)
 602{
 603        struct socket_info *sock_info = container_of(socket, struct socket_info, socket);
 604        unsigned int sock = sock_info->number;
 605        struct pci_bus_region region;
 606        unsigned short base, i;
 607        unsigned char map;
 608        
 609        enter("i82092aa_set_mem_map");
 610
 611        pcibios_resource_to_bus(sock_info->dev, &region, mem->res);
 612        
 613        map = mem->map;
 614        if (map > 4) {
 615                leave("i82092aa_set_mem_map: invalid map");
 616                return -EINVAL;
 617        }
 618        
 619        
 620        if ( (mem->card_start > 0x3ffffff) || (region.start > region.end) ||
 621             (mem->speed > 1000) ) {
 622                leave("i82092aa_set_mem_map: invalid address / speed");
 623                printk("invalid mem map for socket %i: %llx to %llx with a "
 624                        "start of %x\n",
 625                        sock,
 626                        (unsigned long long)region.start,
 627                        (unsigned long long)region.end,
 628                        mem->card_start);
 629                return -EINVAL;
 630        }
 631        
 632        /* Turn off the window before changing anything */
 633        if (indirect_read(sock, I365_ADDRWIN) & I365_ENA_MEM(map))
 634                      indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
 635                         
 636                         
 637/*      printk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, region.start,region.end,sock,mem->speed,mem->flags & MAP_ACTIVE);  */
 638
 639        /* write the start address */
 640        base = I365_MEM(map);
 641        i = (region.start >> 12) & 0x0fff;
 642        if (mem->flags & MAP_16BIT) 
 643                i |= I365_MEM_16BIT;
 644        if (mem->flags & MAP_0WS)
 645                i |= I365_MEM_0WS;      
 646        indirect_write16(sock,base+I365_W_START,i);
 647                               
 648        /* write the stop address */
 649        
 650        i= (region.end >> 12) & 0x0fff;
 651        switch (to_cycles(mem->speed)) {
 652                case 0:
 653                        break;
 654                case 1:
 655                        i |= I365_MEM_WS0;
 656                        break;
 657                case 2:
 658                        i |= I365_MEM_WS1;
 659                        break;
 660                default:
 661                        i |= I365_MEM_WS1 | I365_MEM_WS0;
 662                        break;
 663        }
 664        
 665        indirect_write16(sock,base+I365_W_STOP,i);
 666        
 667        /* card start */
 668        
 669        i = ((mem->card_start - region.start) >> 12) & 0x3fff;
 670        if (mem->flags & MAP_WRPROT)
 671                i |= I365_MEM_WRPROT;
 672        if (mem->flags & MAP_ATTRIB) {
 673/*              printk("requesting attribute memory for socket %i\n",sock);*/
 674                i |= I365_MEM_REG;
 675        } else {
 676/*              printk("requesting normal memory for socket %i\n",sock);*/
 677        }
 678        indirect_write16(sock,base+I365_W_OFF,i);
 679        
 680        /* Enable the window if necessary */
 681        if (mem->flags & MAP_ACTIVE)
 682                indirect_setbit(sock, I365_ADDRWIN, I365_ENA_MEM(map));
 683                    
 684        leave("i82092aa_set_mem_map");
 685        return 0;
 686}
 687
 688static int i82092aa_module_init(void)
 689{
 690        return pci_register_driver(&i82092aa_pci_driver);
 691}
 692
 693static void i82092aa_module_exit(void)
 694{
 695        enter("i82092aa_module_exit");
 696        pci_unregister_driver(&i82092aa_pci_driver);
 697        if (sockets[0].io_base>0)
 698                         release_region(sockets[0].io_base, 2);
 699        leave("i82092aa_module_exit");
 700}
 701
 702module_init(i82092aa_module_init);
 703module_exit(i82092aa_module_exit);
 704
 705
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.