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