📄 pcic.c
字号:
*/ cinfo.mapmem = pcic_memory; cinfo.mapio = pcic_io; cinfo.ioctl = pcic_ioctl; cinfo.power = pcic_power; cinfo.mapirq = pcic_mapirq; cinfo.reset = pcic_reset; cinfo.disable = pcic_disable; cinfo.resume = pcic_resume; cinfo.maxmem = PCIC_MEM_WIN; cinfo.maxio = PCIC_IO_WIN; cinfo.irqs = free_irqs; cinfo.imask = &pcic_imask;#ifdef LKM bzero(pcic_slots, sizeof(pcic_slots));#endif sp = pcic_slots; for (slotnum = 0; slotnum < PCIC_MAX_SLOTS; slotnum++, sp++) { /* * Initialise the PCIC slot table. */ sp->getb = getb1; sp->putb = putb1; if (slotnum < 4) { sp->index = PCIC_INDEX_0; sp->data = PCIC_DATA_0; sp->offset = slotnum * PCIC_SLOT_SIZE; } else { sp->index = PCIC_INDEX_1; sp->data = PCIC_DATA_1; sp->offset = (slotnum - 4) * PCIC_SLOT_SIZE; } /* * XXX - Screwed up slot 1 on the VLSI chips. According to * the Linux PCMCIA code from David Hinds, working chipsets * return 0x84 from their (correct) ID ports, while the broken * ones would need to be probed at the new offset we set after * we assume it's broken. */ if (slotnum == 1 && maybe_vlsi && sp->getb(sp, PCIC_ID_REV) != 0x84) { sp->index += 4; sp->data += 4; sp->offset = PCIC_SLOT_SIZE << 1; } /* * see if there's a PCMCIA controller here * Intel PCMCIA controllers use 0x82 and 0x83 * IBM clone chips use 0x88 and 0x89, apparently */ c = sp->getb(sp, PCIC_ID_REV); sp->revision = -1; switch(c) { /* * 82365 or clones. */ case 0x82: case 0x83: sp->controller = PCIC_I82365; sp->revision = c & 1; /* * Now check for VADEM chips. */ outb(sp->index, 0x0E); outb(sp->index, 0x37); setb(sp, 0x3A, 0x40); c = sp->getb(sp, PCIC_ID_REV); if (c & 0x08) { sp->controller = ((sp->revision = c & 7) == 4) ? PCIC_VG469 : PCIC_VG468 ; clrb(sp, 0x3A, 0x40); } /* * Check for RICOH RF5C396 PCMCIA Controller */ c = sp->getb(sp, 0x3a); if (c == 0xb2) { sp->controller = PCIC_RF5C396; } break; /* * VLSI chips. */ case 0x84: sp->controller = PCIC_VLSI; maybe_vlsi = 1; break; case 0x88: case 0x89: sp->controller = PCIC_IBM; sp->revision = c & 1; break; case 0x8a: sp->controller = PCIC_IBM_KING; sp->revision = c & 1; break; default: continue; } /* * Check for Cirrus logic chips. */ sp->putb(sp, 0x1F, 0); c = sp->getb(sp, 0x1F); if ((c & 0xC0) == 0xC0) { c = sp->getb(sp, 0x1F); if ((c & 0xC0) == 0) { if (c & 0x20) sp->controller = PCIC_PD672X; else sp->controller = PCIC_PD6710; sp->revision = 8 - ((c & 0x1F) >> 2); } } switch(sp->controller) { case PCIC_I82365: cinfo.name = "Intel 82365"; break; case PCIC_IBM: cinfo.name = "IBM PCIC"; break; case PCIC_IBM_KING: cinfo.name = "IBM KING PCMCIA Controller"; break; case PCIC_PD672X: cinfo.name = "Cirrus Logic PD672X"; break; case PCIC_PD6710: cinfo.name = "Cirrus Logic PD6710"; break; case PCIC_VG468: cinfo.name = "Vadem 468"; break; case PCIC_VG469: cinfo.name = "Vadem 469"; break; case PCIC_RF5C396: cinfo.name = "Ricoh RF5C396"; break; case PCIC_VLSI: cinfo.name = "VLSI 82C146"; break; default: cinfo.name = "Unknown!"; break; } /* * OK it seems we have a PCIC or lookalike. * Allocate a slot and initialise the data structures. */ validslots++; sp->slotnum = slotnum; slt = pccard_alloc_slot(&cinfo); if (slt == 0) continue; slt->cdata = sp; sp->slt = slt; /* * If we haven't allocated an interrupt for the controller, * then attempt to get one. */ if (pcic_irq == 0) { pcic_imask = SWI_MASK; pcic_irq = pccard_alloc_intr(free_irqs, pcicintr, 0, &pcic_imask, NULL); if (pcic_irq < 0) printf("pcic: failed to allocate IRQ\n"); else printf("pcic: controller irq %d\n", pcic_irq); } /* * Modem cards send the speaker audio (dialing noises) * to the host's speaker. Cirrus Logic PCIC chips must * enable this. There is also a Low Power Dynamic Mode bit * that claims to reduce power consumption by 30%, so * enable it and hope for the best. */ if (sp->controller == PCIC_PD672X) { setb(sp, PCIC_MISC1, PCIC_SPKR_EN); setb(sp, PCIC_MISC2, PCIC_LPDM_EN); } /* * Check for a card in this slot. */ setb(sp, PCIC_POWER, PCIC_PCPWRE| PCIC_DISRST); if ((sp->getb(sp, PCIC_STATUS) & PCIC_CD) != PCIC_CD) { slt->laststate = slt->state = empty; } else { slt->laststate = slt->state = filled; pccard_event(sp->slt, card_inserted); } /* * Assign IRQ for slot changes */ if (pcic_irq > 0) sp->putb(sp, PCIC_STAT_INT, (pcic_irq << 4) | 0xF); }#ifdef PC98 if (validslots == 0) { sp = pcic_slots; slotnum = 0; if (inb(PCIC98_REG0) != 0xff) { sp->controller = PCIC_PC98; sp->revision = 0; cinfo.name = "PC98 Original"; cinfo.maxmem = 1; cinfo.maxio = 1;/* cinfo.irqs = PCIC_INT_MASK_ALLOWED;*/ cinfo.irqs = 0x1468; validslots++; sp->slotnum = slotnum; slt = pccard_alloc_slot(&cinfo); if (slt == 0) { printf("pcic98: slt == NULL\n"); goto pcic98_probe_end; } slt->cdata = sp; sp->slt = slt; /* Check for a card in this slot */ if (inb(PCIC98_REG1) & PCIC98_CARDEXIST) { /* PCMCIA card exist */ slt->laststate = slt->state = filled; pccard_event(sp->slt, card_inserted); } else { slt->laststate = slt->state = empty; } } pcic98_probe_end: }#endif /* PC98 */ if (validslots) timeout(pcictimeout, 0, hz/2); return(validslots);}/* * ioctl calls - Controller specific ioctls */static intpcic_ioctl(struct slot *slt, int cmd, caddr_t data){ struct pcic_slot *sp = slt->cdata; switch(cmd) { default: return(EINVAL); /* * Get/set PCIC registers */ case PIOCGREG: ((struct pcic_reg *)data)->value = sp->getb(sp, ((struct pcic_reg *)data)->reg); break; case PIOCSREG: sp->putb(sp, ((struct pcic_reg *)data)->reg, ((struct pcic_reg *)data)->value); break; } return(0);}/* * pcic_power - Enable the power of the slot according to * the parameters in the power structure(s). */static intpcic_power(struct slot *slt){ unsigned char reg = PCIC_DISRST|PCIC_PCPWRE; struct pcic_slot *sp = slt->cdata; switch(sp->controller) {#ifdef PC98 case PCIC_PC98: reg = inb(PCIC98_REG6) & (~PCIC98_VPP12V); switch(slt->pwr.vpp) { default: return(EINVAL); case 50: break; case 120: reg |= PCIC98_VPP12V; break; } outb(PCIC98_REG6, reg); DELAY(100*1000); reg = inb(PCIC98_REG2) & (~PCIC98_VCC3P3V); switch(slt->pwr.vcc) { default: return(EINVAL); case 33: reg |= PCIC98_VCC3P3V; break; case 50: break; } outb(PCIC98_REG2, reg); DELAY(100*1000); return (0);#endif case PCIC_PD672X: case PCIC_PD6710: case PCIC_VG468: case PCIC_VG469: case PCIC_RF5C396: case PCIC_VLSI: case PCIC_IBM_KING: switch(slt->pwr.vpp) { default: return(EINVAL); case 0: break; case 50: case 33: reg |= PCIC_VPP_5V; break; case 120: reg |= PCIC_VPP_12V; break; } switch(slt->pwr.vcc) { default: return(EINVAL); case 0: break; case 33: if (sp->controller == PCIC_IBM_KING) { reg |= PCIC_VCC_5V_KING; break; } reg |= PCIC_VCC_3V; if ((sp->controller == PCIC_VG468)|| (sp->controller == PCIC_VG469)) setb(sp, 0x2f, 0x03) ; else setb(sp, 0x16, 0x02); break; case 50: if (sp->controller == PCIC_IBM_KING) { reg |= PCIC_VCC_5V_KING; break; } reg |= PCIC_VCC_5V; if ((sp->controller == PCIC_VG468)|| (sp->controller == PCIC_VG469)) clrb(sp, 0x2f, 0x03) ; else clrb(sp, 0x16, 0x02); break; } break; } sp->putb(sp, PCIC_POWER, reg); DELAY(300*1000); if (slt->pwr.vcc) { reg |= PCIC_OUTENA; sp->putb(sp, PCIC_POWER, reg); DELAY(100*1000); } /* Some chips are smarter than us it seems, so if we weren't * allowed to use 5V, try 3.3 instead */ if (!(sp->getb(sp, PCIC_STATUS) & 0x40) && slt->pwr.vcc == 50) { slt->pwr.vcc = 33; slt->pwr.vpp = 0; return (pcic_power(slt)); } return(0);}/* * tell the PCIC which irq we want to use. only the following are legal: * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 */static voidpcic_mapirq(struct slot *slt, int irq){ struct pcic_slot *sp = slt->cdata;#ifdef PC98 if (sp->controller == PCIC_PC98) { unsigned char x; switch (irq) { case 3: x = PCIC98_INT0; break; case 5: x = PCIC98_INT1; break; case 6: x = PCIC98_INT2; break; case 10: x = PCIC98_INT4; break; case 12: x = PCIC98_INT5; break; case 0: /* disable */ x = PCIC98_INTDISABLE; break; default: printf("pcic98: illegal irq %d\n", irq); return; }#ifdef PCIC_DEBUG printf("pcic98: irq=%d mapped.\n", irq);#endif outb(PCIC98_REG3, x); return; } #endif if (irq == 0) clrb(sp, PCIC_INT_GEN, 0xF); else sp->putb(sp, PCIC_INT_GEN, (sp->getb(sp, PCIC_INT_GEN) & 0xF0) | irq);}/* * pcic_reset - Reset the card and enable initial power. */static voidpcic_reset(void *chan){ struct slot *slt = chan; struct pcic_slot *sp = slt->cdata;#ifdef PC98 if (sp->controller == PCIC_PC98) { outb(PCIC98_REG0, 0); outb(PCIC98_REG2, inb(PCIC98_REG2) & (~PCIC98_IOMEMORY)); outb(PCIC98_REG3, PCIC98_INTDISABLE); outb(PCIC98_REG2, inb(PCIC98_REG2) & (~PCIC98_VCC3P3V)); outb(PCIC98_REG6, inb(PCIC98_REG6) & (~PCIC98_VPP12V)); outb(PCIC98_REG1, 0); selwakeup(&slt->selp); return; }#endif switch (slt->insert_seq) { case 0: /* Something funny happended on the way to the pub... */ return; case 1: /* Assert reset */ clrb(sp, PCIC_INT_GEN, PCIC_CARDRESET); slt->insert_seq = 2; timeout(pcic_reset, (void *)slt, hz/4); return; case 2: /* Deassert it again */ setb(sp, PCIC_INT_GEN, PCIC_CARDRESET|PCIC_IOCARD); slt->insert_seq = 3; timeout(pcic_reset, (void *)slt, hz/4); return; case 3: /* Wait if card needs more time */ if (!sp->getb(sp, PCIC_STATUS) & PCIC_READY) { timeout(pcic_reset, (void *)slt, hz/10); return; } } slt->insert_seq = 0; if (sp->controller == PCIC_PD672X || sp->controller == PCIC_PD6710) { sp->putb(sp, PCIC_TIME_SETUP0, 0x1); sp->putb(sp, PCIC_TIME_CMD0, 0x6); sp->putb(sp, PCIC_TIME_RECOV0, 0x0); sp->putb(sp, PCIC_TIME_SETUP1, 1); sp->putb(sp, PCIC_TIME_CMD1, 0xf); sp->putb(sp, PCIC_TIME_RECOV1, 0); } selwakeup(&slt->selp);}/* * pcic_disable - Disable the slot. */static voidpcic_disable(struct slot *slt){ struct pcic_slot *sp = slt->cdata;#ifdef PC98 if (sp->controller == PCIC_PC98) { return; }#endif sp->putb(sp, PCIC_INT_GEN, 0); sp->putb(sp, PCIC_POWER, 0);}/* * PCIC timer, it seems that we lose interrupts sometimes * so poll just in case... */static voidpcictimeout(void *chan){ pcicintr(0); timeout(pcictimeout, 0, hz/2);}/* * PCIC Interrupt handler. * Check each slot in turn, and read the card status change * register. If this is non-zero, then a change has occurred * on this card, so send an event to the main code. */static voidpcicintr(int unit){ int slot, s; unsigned char chg; struct pcic_slot *sp = pcic_slots;#ifdef PC98 if (sp->controller == PCIC_PC98) { slot = 0; s = splhigh(); /* Check for a card in this slot */ if (inb(PCIC98_REG1) & PCIC98_CARDEXIST) { if (sp->slt->laststate != filled) { pccard_event(sp->slt, card_inserted); } } else { if (sp->slt->laststate != empty) { pccard_event(sp->slt, card_removed); } } splx(s); return; }#endif /* PC98 */ s = splhigh(); for (slot = 0; slot < PCIC_MAX_SLOTS; slot++, sp++) if (sp->slt && (chg = sp->getb(sp, PCIC_STAT_CHG)) != 0) if (chg & PCIC_CDTCH) { if ((sp->getb(sp, PCIC_STATUS) & PCIC_CD) == PCIC_CD) { pccard_event(sp->slt, card_inserted); } else { pccard_event(sp->slt, card_removed); } } splx(s);}/* * pcic_resume - Suspend/resume support for PCIC */static voidpcic_resume(struct slot *slt){ struct pcic_slot *sp = slt->cdata; if (pcic_irq > 0) sp->putb(sp, PCIC_STAT_INT, (pcic_irq << 4) | 0xF); if (sp->controller == PCIC_PD672X) { setb(sp, PCIC_MISC1, PCIC_SPKR_EN); setb(sp, PCIC_MISC2, PCIC_LPDM_EN); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -