2 * NEC MECIA controller.
3 *-------------------------------------------------------------------------
5 * Copyright (c) 2001 M. Warner Losh. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 * $FreeBSD: src/sys/pccard/mecia.c,v 1.2.2.4 2001/08/14 23:36:18 imp Exp $
29 * Based heavily on the FreeBSD pcic driver's pcic98 support, derived
30 * from PAO3 tree. This copyright notice likely needs modification for
31 * such a linage. The only authorship I could find was:
33 * PC9801 original PCMCIA controller code for NS/A,Ne,NX/C,NR/L.
34 * by Noriyuki Hosobuchi <hoso@ce.mbn.or.jp>
37 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
43 #include <pccard/meciareg.h>
44 #include <pccard/cardinfo.h>
45 #include <pccard/slot.h>
47 #define MECIA_IOBASE 0x80d0
51 #include <isa/isavar.h>
53 #include <dev/pccard/pccardvar.h>
56 #define MECIA_DEVICE2SOFTC(dev) ((struct mecia_slot *) device_get_softc(dev))
59 * Prototypes for interrupt handler.
61 static driver_intr_t meciaintr;
62 static int mecia_ioctl(struct slot *, int, caddr_t);
63 static int mecia_power(struct slot *);
64 static void mecia_mapirq(struct slot *, int);
65 static timeout_t mecia_reset;
66 static void mecia_resume(struct slot *);
67 static void mecia_disable(struct slot *);
68 static timeout_t meciatimeout;
69 static struct callout_handle meciatimeout_ch
70 = CALLOUT_HANDLE_INITIALIZER(&meciatimeout_ch);
71 static int mecia_memory(struct slot *, int);
72 static int mecia_io(struct slot *, int);
75 * Per-slot data table.
78 int unit; /* Unit number */
79 int slotnum; /* My slot number */
80 struct slot *slt; /* Back ptr to slot */
81 device_t dev; /* My device */
82 u_char last_reg1; /* Last value of change reg */
85 static struct slot_ctrl mecia_cinfo = {
98 2 /* Fake for UE2212 LAN card */
102 static int validunits = 0;
105 * Look for an NEC MECIA.
106 * For each available slot, allocate a PC-CARD slot.
110 mecia_probe(device_t dev)
114 /* Check isapnp ids */
115 if (isa_get_logicalid(dev)) /* skip PnP probes */
118 if (inb(MECIA_REG0) != 0xff) {
120 /* XXX need to allocated the port resources */
121 device_set_desc(dev, "MECIA PC98 Original PCMCIA Controller");
123 return (validslots ? 0 : ENXIO);
127 mecia_attach(device_t dev)
136 struct mecia_slot *sp;
138 sp = MECIA_DEVICE2SOFTC(dev);
139 sp->unit = validunits++;
140 kid = device_add_child(dev, NULL, -1);
142 device_printf(dev, "Can't add pccard bus slot 0\n");
145 device_probe_and_attach(kid);
146 slt = pccard_init_slot(kid, &mecia_cinfo);
148 device_printf(dev, "Can't get pccard info slot 0\n");
156 r = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE);
160 irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0);
162 /* See if the user has requested a specific IRQ */
163 if (!getenv_int("machdep.pccard.mecia_irq", &irq))
169 r = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, irq,
172 if (r && ((1 << (rman_get_start(r))) & MECIA_INT_MASK_ALLOWED) == 0) {
174 "Hardware does not support irq %d, trying polling.\n",
176 bus_release_resource(dev, SYS_RES_IRQ, rid, r);
181 error = bus_setup_intr(dev, r, INTR_TYPE_MISC,
182 meciaintr, (void *) sp, &ih);
184 bus_release_resource(dev, SYS_RES_IRQ, rid, r);
187 irq = rman_get_start(r);
188 device_printf(dev, "management irq %d\n", irq);
193 meciatimeout_ch = timeout(meciatimeout, (void *) sp, hz/2);
194 device_printf(dev, "Polling mode\n");
197 sp->last_reg1 = inb(MECIA_REG1);
198 if (sp->last_reg1 & MECIA_CARDEXIST) {
199 /* PCMCIA card exist */
200 sp->slt->laststate = sp->slt->state = filled;
201 pccard_event(sp->slt, card_inserted);
203 sp->slt->laststate = sp->slt->state = empty;
207 return (bus_generic_attach(dev));
211 mecia_sresource(struct slot *slt, caddr_t data)
213 struct pccard_resource *pr;
217 device_t pccarddev = slt->dev;
219 pr = (struct pccard_resource *)data;
220 pr->resource_addr = ~0ul;
229 flags = rman_make_alignment_flags(pr->size);
230 r = bus_alloc_resource(pccarddev, pr->type, &rid, pr->min, pr->max,
233 pr->resource_addr = (u_long)rman_get_start(r);
234 bus_release_resource(bridgedev, pr->type, rid, r);
240 * ioctl calls - Controller specific ioctls
243 mecia_ioctl(struct slot *slt, int cmd, caddr_t data)
248 case PIOCSRESOURCE: /* Can I use this resource? */
249 mecia_sresource(slt, data);
256 * MECIA timer. If the controller doesn't have a free IRQ to use
257 * or if interrupt steering doesn't work, poll the controller for
258 * insertion/removal events.
261 meciatimeout(void *chan)
264 meciatimeout_ch = timeout(meciatimeout, chan, hz/2);
268 * MECIA Interrupt handler.
269 * Check the slot and report any changes.
276 struct mecia_slot *sp = (struct mecia_slot *) arg;
279 /* Check for a card in this slot */
280 reg1 = inb(MECIA_REG1);
281 if ((sp->last_reg1 ^ reg1) & MECIA_CARDEXIST) {
282 sp->last_reg1 = reg1;
283 if (reg1 & MECIA_CARDEXIST)
284 pccard_event(sp->slt, card_inserted);
286 pccard_event(sp->slt, card_removed);
292 * local functions for PC-98 Original PC-Card controller
294 #define MECIA_ALWAYS_128MAPPING 1 /* trick for using UE2212 */
296 int mecia_mode = 0; /* almost the same as the value in MECIA_REG2 */
298 static unsigned char reg_winsel = MECIA_UNMAPWIN;
299 static unsigned short reg_pagofs = 0;
302 mecia_memory(struct slot *slt, int win)
304 struct mem_desc *mp = &slt->mem[win];
307 if (mp->flags & MDF_ACTIVE) {
308 /* slot = 0, window = 0, sys_addr = 0xda000, length = 8KB */
309 if ((unsigned long)mp->start != 0xda000) {
311 "sys_addr must be 0xda000. requested address = %p\n",
322 reg_winsel = inb(MECIA_REG_WINSEL);
323 reg_pagofs = inw(MECIA_REG_PAGOFS);
324 outb(MECIA_REG_WINSEL, MECIA_MAPWIN);
325 outw(MECIA_REG_PAGOFS, (mp->card >> 13)); /* 8KB */
327 if (mp->flags & MDF_ATTR)
328 outb(MECIA_REG7, inb(MECIA_REG7) | MECIA_ATTRMEM);
330 outb(MECIA_REG7, inb(MECIA_REG7) & (~MECIA_ATTRMEM));
332 outb(MECIA_REG_WINSEL, MECIA_MAPWIN);
334 if ((mp->flags & MDF_16BITS) == 1) /* 16bit */
335 outb(MECIA_REG2, inb(MECIA_REG2) & (~MECIA_8BIT));
337 outb(MECIA_REG2, inb(MECIA_REG2) | MECIA_8BIT);
339 } else { /* !(mp->flags & MDF_ACTIVE) */
346 outb(MECIA_REG_WINSEL, MECIA_UNMAPWIN);
347 outw(MECIA_REG_PAGOFS, 0);
349 outb(MECIA_REG_WINSEL, reg_winsel);
350 outw(MECIA_REG_PAGOFS, reg_pagofs);
357 mecia_io(struct slot *slt, int win)
359 struct io_desc *ip = &slt->io[win];
361 unsigned short cardbase;
365 /* ignore for UE2212 */
367 "mecia:Illegal MECIA I/O window(%d) request! Ignored.\n", win);
368 /* return (EINVAL);*/
372 if (ip->flags & IODF_ACTIVE) {
373 x = inb(MECIA_REG2) & 0x0f;
375 if (! (ip->flags & IODF_CS16))
378 if (! (ip->flags & IODF_16BIT)) {
380 mecia_mode |= MECIA_8BIT;
384 ofst = ip->start & 0xf;
385 cardbase = ip->start & ~0xf;
386 #ifndef MECIA_ALWAYS_128MAPPING
387 if (ip->size + ofst > 16)
389 { /* 128bytes mapping */
391 mecia_mode |= MECIA_MAP128;
392 ofst |= ((cardbase & 0x70) << 4);
399 outw(MECIA_REG4, MECIA_IOBASE); /* 98side I/O base */
400 outw(MECIA_REG5, cardbase); /* card side I/O base */
403 printf("mecia: I/O mapped 0x%04x(98) -> "
404 "0x%04x(Card) and width %d bytes\n",
405 MECIA_IOBASE+ofst, ip->start, ip->size);
406 printf("mecia: reg2=0x%02x reg3=0x%02x reg7=0x%02x\n",
407 inb(MECIA_REG2), inb(MECIA_REG3),
409 printf("mecia: mode=%d\n", mecia_mode);
412 ip->start = MECIA_IOBASE + ofst;
414 outb(MECIA_REG2, inb(MECIA_REG2) & (~MECIA_MAPIO));
421 mecia_power(struct slot *slt)
425 reg = inb(MECIA_REG7) & (~MECIA_VPP12V);
426 switch(slt->pwr.vpp) {
435 outb(MECIA_REG7, reg);
438 reg = inb(MECIA_REG2) & (~MECIA_VCC3P3V);
439 switch(slt->pwr.vcc) {
443 reg |= MECIA_VCC3P3V;
448 outb(MECIA_REG2, reg);
454 mecia_mapirq(struct slot *slt, int irq)
474 case 0: /* disable */
475 x = MECIA_INTDISABLE;
478 printf("mecia: illegal irq %d\n", irq);
482 printf("mecia: irq=%d mapped.\n", irq);
488 mecia_reset(void *chan)
490 struct slot *slt = chan;
493 outb(MECIA_REG2, inb(MECIA_REG2) & (~MECIA_MAPIO));
494 outb(MECIA_REG3, MECIA_INTDISABLE);
496 /* mecia_reset() is called after mecia_power() */
497 outb(MECIA_REG2, inb(MECIA_REG2) & (~MECIA_VCC3P3V));
498 outb(MECIA_REG7, inb(MECIA_REG7) & (~MECIA_VPP12V));
502 selwakeup(&slt->selp);
506 mecia_disable(struct slot *slt)
512 mecia_resume(struct slot *slt)
514 /* XXX MECIA How ? */
518 mecia_activate_resource(device_t dev, device_t child, int type, int rid,
521 struct pccard_devinfo *devi = device_get_ivars(child);
524 if (dev != device_get_parent(device_get_parent(child)) || devi == NULL)
525 return (bus_generic_activate_resource(dev, child, type,
529 case SYS_RES_IOPORT: {
531 ip = &devi->slt->io[rid];
532 if (ip->flags == 0) {
534 ip->flags = IODF_WS | IODF_16BIT | IODF_CS16;
536 ip->flags = devi->slt->io[0].flags;
538 ip->flags |= IODF_ACTIVE;
539 ip->start = rman_get_start(r);
540 ip->size = rman_get_end(r) - rman_get_start(r) + 1;
541 err = mecia_cinfo.mapio(devi->slt, rid);
548 * We actually defer the activation of the IRQ resource
549 * until the interrupt is registered to avoid stray
550 * interrupt messages.
553 case SYS_RES_MEMORY: {
555 if (rid >= NUM_MEM_WINDOWS)
557 mp = &devi->slt->mem[rid];
558 mp->flags |= MDF_ACTIVE;
559 mp->start = (caddr_t) rman_get_start(r);
560 mp->size = rman_get_end(r) - rman_get_start(r) + 1;
561 err = mecia_cinfo.mapmem(devi->slt, rid);
569 err = bus_generic_activate_resource(dev, child, type, rid, r);
574 mecia_deactivate_resource(device_t dev, device_t child, int type, int rid,
577 struct pccard_devinfo *devi = device_get_ivars(child);
580 if (dev != device_get_parent(device_get_parent(child)) || devi == NULL)
581 return (bus_generic_deactivate_resource(dev, child, type,
585 case SYS_RES_IOPORT: {
586 struct io_desc *ip = &devi->slt->io[rid];
587 ip->flags &= ~IODF_ACTIVE;
588 err = mecia_cinfo.mapio(devi->slt, rid);
595 case SYS_RES_MEMORY: {
596 struct mem_desc *mp = &devi->slt->mem[rid];
597 mp->flags &= ~(MDF_ACTIVE | MDF_ATTR);
598 err = mecia_cinfo.mapmem(devi->slt, rid);
606 err = bus_generic_deactivate_resource(dev, child, type, rid, r);
611 mecia_setup_intr(device_t dev, device_t child, struct resource *irq,
612 int flags, driver_intr_t *intr, void *arg, void **cookiep)
614 struct pccard_devinfo *devi = device_get_ivars(child);
617 if (((1 << rman_get_start(irq)) & MECIA_INT_MASK_ALLOWED) == 0) {
618 device_printf(dev, "Hardware does not support irq %ld.\n",
619 rman_get_start(irq));
623 err = bus_generic_setup_intr(dev, child, irq, flags, intr, arg,
626 mecia_cinfo.mapirq(devi->slt, rman_get_start(irq));
628 device_printf(dev, "Error %d irq %ld\n", err,
629 rman_get_start(irq));
634 mecia_teardown_intr(device_t dev, device_t child, struct resource *irq,
637 struct pccard_devinfo *devi = device_get_ivars(child);
639 mecia_cinfo.mapirq(devi->slt, 0);
640 return (bus_generic_teardown_intr(dev, child, irq, cookie));
644 mecia_set_res_flags(device_t bus, device_t child, int restype, int rid,
647 struct pccard_devinfo *devi = device_get_ivars(child);
651 case SYS_RES_MEMORY: {
652 struct mem_desc *mp = &devi->slt->mem[rid];
654 case PCCARD_A_MEM_COM:
655 mp->flags &= ~MDF_ATTR;
657 case PCCARD_A_MEM_ATTR:
658 mp->flags |= MDF_ATTR;
660 case PCCARD_A_MEM_8BIT:
661 mp->flags &= ~MDF_16BITS;
663 case PCCARD_A_MEM_16BIT:
664 mp->flags |= MDF_16BITS;
667 err = mecia_cinfo.mapmem(devi->slt, rid);
677 mecia_get_res_flags(device_t bus, device_t child, int restype, int rid,
680 struct pccard_devinfo *devi = device_get_ivars(child);
687 case SYS_RES_IOPORT: {
688 struct io_desc *ip = &devi->slt->io[rid];
692 case SYS_RES_MEMORY: {
693 struct mem_desc *mp = &devi->slt->mem[rid];
704 mecia_set_memory_offset(device_t bus, device_t child, int rid,
705 u_int32_t offset, u_int32_t *deltap)
707 struct pccard_devinfo *devi = device_get_ivars(child);
708 struct mem_desc *mp = &devi->slt->mem[rid];
712 *deltap = 0; /* XXX BAD XXX */
713 return (mecia_cinfo.mapmem(devi->slt, rid));
717 mecia_get_memory_offset(device_t bus, device_t child, int rid,
720 struct pccard_devinfo *devi = device_get_ivars(child);
721 struct mem_desc *mp = &devi->slt->mem[rid];
731 static device_method_t mecia_methods[] = {
732 /* Device interface */
733 DEVMETHOD(device_probe, mecia_probe),
734 DEVMETHOD(device_attach, mecia_attach),
735 DEVMETHOD(device_detach, bus_generic_detach),
736 DEVMETHOD(device_shutdown, bus_generic_shutdown),
737 DEVMETHOD(device_suspend, bus_generic_suspend),
738 DEVMETHOD(device_resume, bus_generic_resume),
741 DEVMETHOD(bus_print_child, bus_generic_print_child),
742 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
743 DEVMETHOD(bus_release_resource, bus_generic_release_resource),
744 DEVMETHOD(bus_activate_resource, mecia_activate_resource),
745 DEVMETHOD(bus_deactivate_resource, mecia_deactivate_resource),
746 DEVMETHOD(bus_setup_intr, mecia_setup_intr),
747 DEVMETHOD(bus_teardown_intr, mecia_teardown_intr),
750 DEVMETHOD(card_set_res_flags, mecia_set_res_flags),
751 DEVMETHOD(card_get_res_flags, mecia_get_res_flags),
752 DEVMETHOD(card_set_memory_offset, mecia_set_memory_offset),
753 DEVMETHOD(card_get_memory_offset, mecia_get_memory_offset),
758 devclass_t mecia_devclass;
760 static driver_t mecia_driver = {
763 sizeof(struct mecia_slot)
766 DRIVER_MODULE(mecia, isa, mecia_driver, mecia_devclass, 0, 0);