e0e48d78ef76d0088003053c24226c7742dd3427
[dragonfly.git] / sys / bus / pccard / pccard.c
1 /*
2  *      pccard.c - Interface code for PC-CARD controllers.
3  *
4  *      June 1995, Andrew McRae (andrew@mega.com.au)
5  *-------------------------------------------------------------------------
6  *
7  * Copyright (c) 2001 M. Warner Losh.  All rights reserved.
8  * Copyright (c) 1995 Andrew McRae.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/pccard/pccard.c,v 1.106.2.15 2003/02/26 18:42:00 imp Exp $
33  * $DragonFly: src/sys/bus/pccard/pccard.c,v 1.4 2003/06/25 03:56:09 dillon Exp $
34  */
35
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/sysctl.h>
42 #include <sys/conf.h>
43 #include <sys/uio.h>
44 #include <sys/poll.h>
45 #include <sys/bus.h>
46 #include <sys/proc.h>
47 #include <machine/bus.h>
48
49 #include <pccard/cardinfo.h>
50 #include <pccard/driver.h>
51 #include <pccard/slot.h>
52 #include <pccard/pccard_nbk.h>
53
54 #include <machine/md_var.h>
55
56 #define MIN(a,b)        ((a)<(b)?(a):(b))
57
58 static int              allocate_driver(struct slot *, struct dev_desc *);
59 static void             inserted(void *);
60 static void             disable_slot(struct slot *);
61 static void             disable_slot_to(struct slot *);
62 static void             power_off_slot(void *);
63
64 /*
65  *      The driver interface for read/write uses a block
66  *      of memory in the ISA I/O memory space allocated via
67  *      an ioctl setting.
68  *
69  *      Now that we have different bus attachments, we should really
70  *      use a better algorythm to allocate memory.
71  */
72 static unsigned long pccard_mem;        /* Physical memory */
73 static unsigned char *pccard_kmem;      /* Kernel virtual address */
74 static struct resource *pccard_mem_res;
75 static int pccard_mem_rid;
76
77 static  d_open_t        crdopen;
78 static  d_close_t       crdclose;
79 static  d_read_t        crdread;
80 static  d_write_t       crdwrite;
81 static  d_ioctl_t       crdioctl;
82 static  d_poll_t        crdpoll;
83
84 #define CDEV_MAJOR 50
85 static struct cdevsw crd_cdevsw = {
86         /* open */      crdopen,
87         /* close */     crdclose,
88         /* read */      crdread,
89         /* write */     crdwrite,
90         /* ioctl */     crdioctl,
91         /* poll */      crdpoll,
92         /* mmap */      nommap,
93         /* strategy */  nostrategy,
94         /* name */      "crd",
95         /* maj */       CDEV_MAJOR,
96         /* dump */      nodump,
97         /* psize */     nopsize,
98         /* flags */     0,
99 };
100
101 /*
102  *      Power off the slot.
103  *      (doing it immediately makes the removal of some cards unstable)
104  */
105 static void
106 power_off_slot(void *arg)
107 {
108         struct slot *slt = (struct slot *)arg;
109         int s;
110
111         /*
112          * The following will generate an interrupt.  So, to hold off
113          * the interrupt unitl after disable runs so that we can get rid
114          * rid of the interrupt before it becomes unsafe to touch the
115          * device.
116          *
117          * XXX In current, the spl stuff is a nop.
118          */
119         s = splhigh();
120         /* Power off the slot. */
121         slt->pwr_off_pending = 0;
122         slt->ctrl->disable(slt);
123         splx(s);
124 }
125
126 /*
127  *      disable_slot - Disables the slot by removing
128  *      the power and unmapping the I/O
129  */
130 static void
131 disable_slot(struct slot *slt)
132 {
133         device_t pccarddev;
134         device_t *kids;
135         int nkids;
136         int i;
137         int ret;
138
139         /*
140          * Note that a race condition is possible here; if a
141          * driver is accessing the device and it is removed, then
142          * all bets are off...
143          */
144         pccarddev = slt->dev;
145         device_get_children(pccarddev, &kids, &nkids);
146         for (i = 0; i < nkids; i++) {
147                 if ((ret = device_delete_child(pccarddev, kids[i])) != 0)
148                         printf("pccard: delete of %s failed: %d\n",
149                                 device_get_nameunit(kids[i]), ret);
150         }
151         free(kids, M_TEMP);
152
153         /* Power off the slot 1/2 second after removal of the card */
154         slt->poff_ch = timeout(power_off_slot, (caddr_t)slt, hz / 2);
155         slt->pwr_off_pending = 1;
156 }
157
158 static void
159 disable_slot_to(struct slot *slt)
160 {
161         disable_slot(slt);
162         if (slt->state == empty)
163                 printf("pccard: card removed, slot %d\n", slt->slotnum);
164         else
165                 printf("pccard: card deactivated, slot %d\n", slt->slotnum);
166         pccard_remove_beep();
167         selwakeup(&slt->selp);
168 }
169
170 /*
171  *      pccard_init_slot - Initialize the slot controller and attach various
172  * things to it.  We also make the device for it.  We create the device that
173  * will be exported to devfs.
174  */
175 struct slot *
176 pccard_init_slot(device_t dev, struct slot_ctrl *ctrl)
177 {
178         int             slotno;
179         struct slot     *slt;
180
181         slt = PCCARD_DEVICE2SOFTC(dev);
182         slotno = device_get_unit(dev);
183         slt->dev = dev;
184         slt->d = make_dev(&crd_cdevsw, slotno, 0, 0, 0600, "card%d", slotno);
185         slt->d->si_drv1 = slt;
186         slt->ctrl = ctrl;
187         slt->slotnum = slotno;
188         callout_handle_init(&slt->insert_ch);
189         callout_handle_init(&slt->poff_ch);
190
191         return (slt);
192 }
193
194 /*
195  *      allocate_driver - Create a new device entry for this
196  *      slot, and attach a driver to it.
197  */
198 static int
199 allocate_driver(struct slot *slt, struct dev_desc *desc)
200 {
201         struct pccard_devinfo *devi;
202         device_t pccarddev;
203         int err, irq = 0;
204         device_t child;
205         device_t *devs;
206         int count;
207
208         pccarddev = slt->dev;
209         err = device_get_children(pccarddev, &devs, &count);
210         if (err != 0)
211                 return (err);
212         free(devs, M_TEMP);
213         if (count) {
214                 device_printf(pccarddev,
215                     "Can not attach more than one child.\n");
216                 return (EIO);
217         }
218         irq = ffs(desc->irqmask) - 1;
219         MALLOC(devi, struct pccard_devinfo *, sizeof(*devi), M_DEVBUF,
220             M_WAITOK | M_ZERO);
221         strcpy(devi->name, desc->name);
222         /*
223          *      Create an entry for the device under this slot.
224          */
225         devi->running = 1;
226         devi->slt = slt;
227         bcopy(desc->misc, devi->misc, sizeof(desc->misc));
228         strcpy(devi->manufstr, desc->manufstr);
229         strcpy(devi->versstr, desc->versstr);
230         devi->manufacturer = desc->manufacturer;
231         devi->product = desc->product;
232         devi->prodext = desc->prodext;
233         resource_list_init(&devi->resources);
234         child = device_add_child(pccarddev, devi->name, desc->unit);
235         if (child == NULL) {
236                 if (desc->unit != -1)
237                         device_printf(pccarddev,
238                             "Unit %d failed for %s, try a different unit\n",
239                             desc->unit, devi->name);
240                 else
241                         device_printf(pccarddev,
242                             "No units available for %s.  Impossible?\n",
243                             devi->name);
244                 return (EIO);
245         }
246         device_set_flags(child, desc->flags);
247         device_set_ivars(child, devi);
248         if (bootverbose) {
249                 device_printf(pccarddev, "Assigning %s:",
250                     device_get_nameunit(child));
251                 if (desc->iobase)
252                         printf(" io 0x%x-0x%x",
253                             desc->iobase, desc->iobase + desc->iosize - 1);
254                 if (irq)
255                         printf(" irq %d", irq);
256                 if (desc->mem)
257                         printf(" mem 0x%lx-0x%lx", desc->mem,
258                             desc->mem + desc->memsize - 1);
259                 printf(" flags 0x%x\n", desc->flags);
260         }
261         err = bus_set_resource(child, SYS_RES_IOPORT, 0, desc->iobase,
262             desc->iosize);
263         if (err)
264                 goto err;
265         if (irq)
266                 err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
267         if (err)
268                 goto err;
269         if (desc->memsize) {
270                 err = bus_set_resource(child, SYS_RES_MEMORY, 0, desc->mem,
271                     desc->memsize);
272                 if (err)
273                         goto err;
274         }
275         err = device_probe_and_attach(child);
276         /*
277          * XXX We unwisely assume that the detach code won't run while the
278          * XXX the attach code is attaching.  Someone should put some
279          * XXX interlock code.  This can happen if probe/attach takes a while
280          * XXX and the user ejects the card, which causes the detach
281          * XXX function to be called.
282          */
283         strncpy(desc->name, device_get_nameunit(child), sizeof(desc->name));
284         desc->name[sizeof(desc->name) - 1] = '\0';
285 err:
286         if (err)
287                 device_delete_child(pccarddev, child);
288         return (err);
289 }
290
291 /*
292  *      card insert routine - Called from a timeout to debounce
293  *      insertion events.
294  */
295 static void
296 inserted(void *arg)
297 {
298         struct slot *slt = arg;
299
300         slt->state = filled;
301         /*
302          * Disable any pending timeouts for this slot, and explicitly
303          * power it off right now.  Then, re-enable the power using
304          * the (possibly new) power settings.
305          */
306         untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch);
307         power_off_slot(slt);
308
309         /*
310          *      Enable 5V to the card so that the CIS can be read.  Well,
311          * enable the most natural voltage so that the CIS can be read.
312          */
313         slt->pwr.vcc = -1;
314         slt->pwr.vpp = -1;
315         slt->ctrl->power(slt);
316
317         printf("pccard: card inserted, slot %d\n", slt->slotnum);
318         pccard_insert_beep();
319         slt->ctrl->reset(slt);
320 }
321
322 /*
323  *      Card event callback. Called at splhigh to prevent
324  *      device interrupts from interceding.
325  */
326 void
327 pccard_event(struct slot *slt, enum card_event event)
328 {
329         if (slt->insert_seq) {
330                 slt->insert_seq = 0;
331                 untimeout(inserted, (void *)slt, slt->insert_ch);
332         }
333
334         switch(event) {
335         case card_removed:
336         case card_deactivated:
337                 if (slt->state == filled || slt->state == inactive) {
338                         if (event == card_removed)
339                                 slt->state = empty;
340                         else
341                                 slt->state = inactive;
342                         disable_slot_to(slt);
343                 }
344                 break;
345         case card_inserted:
346                 slt->insert_seq = 1;
347                 slt->insert_ch = timeout(inserted, (void *)slt, hz/4);
348                 break;
349         }
350 }
351
352 /*
353  *      Device driver interface.
354  */
355 static  int
356 crdopen(dev_t dev, int oflags, int devtype, d_thread_t *td)
357 {
358         struct slot *slt = PCCARD_DEV2SOFTC(dev);
359
360         if (slt == NULL)
361                 return (ENXIO);
362         if (slt->rwmem == 0)
363                 slt->rwmem = MDF_ATTR;
364         return (0);
365 }
366
367 /*
368  *      Close doesn't de-allocate any resources, since
369  *      slots may be assigned to drivers already.
370  */
371 static  int
372 crdclose(dev_t dev, int fflag, int devtype, d_thread_t *td)
373 {
374         return (0);
375 }
376
377 /*
378  *      read interface. Map memory at lseek offset,
379  *      then transfer to user space.
380  */
381 static  int
382 crdread(dev_t dev, struct uio *uio, int ioflag)
383 {
384         struct slot *slt = PCCARD_DEV2SOFTC(dev);
385         struct mem_desc *mp, oldmap;
386         unsigned char *p;
387         unsigned int offs;
388         int error = 0, win, count;
389
390         if (slt == 0 || slt->state != filled)
391                 return (ENXIO);
392         if (pccard_mem == 0)
393                 return (ENOMEM);
394         for (win = slt->ctrl->maxmem - 1; win >= 0; win--)
395                 if ((slt->mem[win].flags & MDF_ACTIVE) == 0)
396                         break;
397         if (win < 0)
398                 return (EBUSY);
399         mp = &slt->mem[win];
400         oldmap = *mp;
401         mp->flags = slt->rwmem | MDF_ACTIVE;
402         while (uio->uio_resid && error == 0) {
403                 mp->card = uio->uio_offset;
404                 mp->size = PCCARD_MEMSIZE;
405                 mp->start = (caddr_t)(void *)(uintptr_t)pccard_mem;
406                 if ((error = slt->ctrl->mapmem(slt, win)) != 0)
407                         break;
408                 offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1);
409                 p = pccard_kmem + offs;
410                 count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid);
411                 error = uiomove(p, count, uio);
412         }
413         /*
414          *      Restore original map.
415          */
416         *mp = oldmap;
417         slt->ctrl->mapmem(slt, win);
418
419         return (error);
420 }
421
422 /*
423  *      crdwrite - Write data to card memory.
424  *      Handles wrap around so that only one memory
425  *      window is used.
426  */
427 static  int
428 crdwrite(dev_t dev, struct uio *uio, int ioflag)
429 {
430         struct slot *slt = PCCARD_DEV2SOFTC(dev);
431         struct mem_desc *mp, oldmap;
432         unsigned char *p;
433         unsigned int offs;
434         int error = 0, win, count;
435
436         if (slt == 0 || slt->state != filled)
437                 return (ENXIO);
438         if (pccard_mem == 0)
439                 return (ENOMEM);
440         for (win = slt->ctrl->maxmem - 1; win >= 0; win--)
441                 if ((slt->mem[win].flags & MDF_ACTIVE) == 0)
442                         break;
443         if (win < 0)
444                 return (EBUSY);
445         mp = &slt->mem[win];
446         oldmap = *mp;
447         mp->flags = slt->rwmem | MDF_ACTIVE;
448         while (uio->uio_resid && error == 0) {
449                 mp->card = uio->uio_offset;
450                 mp->size = PCCARD_MEMSIZE;
451                 mp->start = (caddr_t)(void *)(uintptr_t)pccard_mem;
452                 if ((error = slt->ctrl->mapmem(slt, win)) != 0)
453                         break;
454                 offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1);
455                 p = pccard_kmem + offs;
456                 count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid);
457                 error = uiomove(p, count, uio);
458         }
459         /*
460          *      Restore original map.
461          */
462         *mp = oldmap;
463         slt->ctrl->mapmem(slt, win);
464
465         return (error);
466 }
467
468 /*
469  *      ioctl calls - allows setting/getting of memory and I/O
470  *      descriptors, and assignment of drivers.
471  */
472 static  int
473 crdioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, d_thread_t *td)
474 {
475         u_int32_t       addr;
476         int             err;
477         struct io_desc  *ip;
478         struct mem_desc *mp;
479         device_t        pccarddev;
480         int             pwval;
481         int             s;
482         struct slot     *slt = PCCARD_DEV2SOFTC(dev);
483
484         if (slt == 0 && cmd != PIOCRWMEM)
485                 return (ENXIO);
486
487         KKASSERT(td->td_proc != NULL);
488
489         switch(cmd) {
490         default:
491                 if (slt->ctrl->ioctl)
492                         return (slt->ctrl->ioctl(slt, cmd, data));
493                 return (ENOTTY);
494         /*
495          * Get slot state.
496          */
497         case PIOCGSTATE:
498                 s = splhigh();
499                 ((struct slotstate *)data)->state = slt->state;
500                 ((struct slotstate *)data)->laststate = slt->laststate;
501                 slt->laststate = slt->state;
502                 splx(s);
503                 ((struct slotstate *)data)->maxmem = slt->ctrl->maxmem;
504                 ((struct slotstate *)data)->maxio = slt->ctrl->maxio;
505                 ((struct slotstate *)data)->irqs = 0;
506                 break;
507         /*
508          * Get memory context.
509          */
510         case PIOCGMEM:
511                 s = ((struct mem_desc *)data)->window;
512                 if (s < 0 || s >= slt->ctrl->maxmem)
513                         return (EINVAL);
514                 mp = &slt->mem[s];
515                 ((struct mem_desc *)data)->flags = mp->flags;
516                 ((struct mem_desc *)data)->start = mp->start;
517                 ((struct mem_desc *)data)->size = mp->size;
518                 ((struct mem_desc *)data)->card = mp->card;
519                 break;
520         /*
521          * Set memory context. If context already active, then unmap it.
522          * It is hard to see how the parameters can be checked.
523          * At the very least, we only allow root to set the context.
524          */
525         case PIOCSMEM:
526                 if (suser(td))
527                         return (EPERM);
528                 if (slt->state != filled)
529                         return (ENXIO);
530                 s = ((struct mem_desc *)data)->window;
531                 if (s < 0 || s >= slt->ctrl->maxmem)
532                         return (EINVAL);
533                 slt->mem[s] = *((struct mem_desc *)data);
534                 return (slt->ctrl->mapmem(slt, s));
535         /*
536          * Get I/O port context.
537          */
538         case PIOCGIO:
539                 s = ((struct io_desc *)data)->window;
540                 if (s < 0 || s >= slt->ctrl->maxio)
541                         return (EINVAL);
542                 ip = &slt->io[s];
543                 ((struct io_desc *)data)->flags = ip->flags;
544                 ((struct io_desc *)data)->start = ip->start;
545                 ((struct io_desc *)data)->size = ip->size;
546                 break;
547         /*
548          * Set I/O port context.
549          */
550         case PIOCSIO:
551                 if (suser(td))
552                         return (EPERM);
553                 if (slt->state != filled)
554                         return (ENXIO);
555                 s = ((struct io_desc *)data)->window;
556                 if (s < 0 || s >= slt->ctrl->maxio)
557                         return (EINVAL);
558                 slt->io[s] = *((struct io_desc *)data);
559                 /* XXX Don't actually map */
560                 return (0);
561                 break;
562         /*
563          * Set memory window flags for read/write interface.
564          */
565         case PIOCRWFLAG:
566                 slt->rwmem = *(int *)data;
567                 break;
568         /*
569          * Set the memory window to be used for the read/write interface.
570          */
571         case PIOCRWMEM:
572                 if (*(unsigned long *)data == 0) {
573                         *(unsigned long *)data = pccard_mem;
574                         break;
575                 }
576                 if (suser(td))
577                         return (EPERM);
578                 /*
579                  * Validate the memory by checking it against the I/O
580                  * memory range. It must also start on an aligned block size.
581                  */
582                 if (*(unsigned long *)data & (PCCARD_MEMSIZE-1))
583                         return (EINVAL);
584                 pccarddev = PCCARD_DEV2SOFTC(dev)->dev;
585                 pccard_mem_rid = 0;
586                 addr = *(unsigned long *)data;
587                 if (pccard_mem_res)
588                         bus_release_resource(pccarddev, SYS_RES_MEMORY,
589                             pccard_mem_rid, pccard_mem_res);
590                 pccard_mem_res = bus_alloc_resource(pccarddev, SYS_RES_MEMORY,
591                     &pccard_mem_rid, addr, addr, PCCARD_MEMSIZE,
592                     RF_ACTIVE | rman_make_alignment_flags(PCCARD_MEMSIZE));
593                 if (pccard_mem_res == NULL)
594                         return (EINVAL);
595                 pccard_mem = rman_get_start(pccard_mem_res);
596                 pccard_kmem = rman_get_virtual(pccard_mem_res);
597                 break;
598         /*
599          * Set power values.
600          */
601         case PIOCSPOW:
602                 slt->pwr = *(struct power *)data;
603                 return (slt->ctrl->power(slt));
604         /*
605          * Allocate a driver to this slot.
606          */
607         case PIOCSDRV:
608                 if (suser(td))
609                         return (EPERM);
610                 err = allocate_driver(slt, (struct dev_desc *)data);
611                 if (!err)
612                         pccard_success_beep();
613                 else
614                         pccard_failure_beep();
615                 return (err);
616         /*
617          * Virtual removal/insertion
618          */
619         case PIOCSVIR:
620                 pwval = *(int *)data;
621                 if (!pwval) {
622                         if (slt->state != filled)
623                                 return (EINVAL);
624                         pccard_event(slt, card_deactivated);
625                 } else {
626                         if (slt->state != empty && slt->state != inactive)
627                                 return (EINVAL);
628                         pccard_event(slt, card_inserted);
629                 }
630                 break;
631         case PIOCSBEEP:
632                 if (pccard_beep_select(*(int *)data)) {
633                         return (EINVAL);
634                 }
635                 break;
636         }
637         return (0);
638 }
639
640 /*
641  *      poll - Poll on exceptions will return true
642  *      when a change in card status occurs.
643  */
644 static  int
645 crdpoll(dev_t dev, int events, d_thread_t *td)
646 {
647         int     revents = 0;
648         int     s;
649         struct slot *slt = PCCARD_DEV2SOFTC(dev);
650
651         if (events & (POLLIN | POLLRDNORM))
652                 revents |= events & (POLLIN | POLLRDNORM);
653
654         if (events & (POLLOUT | POLLWRNORM))
655                 revents |= events & (POLLIN | POLLRDNORM);
656
657         s = splhigh();
658         /*
659          *      select for exception - card event.
660          */
661         if (events & POLLRDBAND)
662                 if (slt == 0 || slt->laststate != slt->state)
663                         revents |= POLLRDBAND;
664
665         if (revents == 0)
666                 selrecord(td, &slt->selp);
667
668         splx(s);
669         return (revents);
670 }
671
672 /*
673  *      APM hooks for suspending and resuming.
674  */
675 int
676 pccard_suspend(device_t dev)
677 {
678         struct slot *slt = PCCARD_DEVICE2SOFTC(dev);
679
680         /* This code stolen from pccard_event:card_removed */
681         if (slt->state == filled) {
682                 int s = splhigh();              /* nop on current */
683                 disable_slot(slt);
684                 slt->laststate = suspend;       /* for pccardd */
685                 slt->state = empty;
686                 splx(s);
687                 printf("pccard: card disabled, slot %d\n", slt->slotnum);
688         }
689         /*
690          * Disable any pending timeouts for this slot since we're
691          * powering it down/disabling now.
692          */
693         untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch);
694         slt->ctrl->disable(slt);
695         return (0);
696 }
697
698 int
699 pccard_resume(device_t dev)
700 {
701         struct slot *slt = PCCARD_DEVICE2SOFTC(dev);
702
703         slt->ctrl->resume(slt);
704         return (0);
705 }