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