static void hs_exit_socket(hs_socket_t *sp) { unsigned short cscier, gcr; /* turn off interrupts in hardware */ cscier = hs_in(sp, CSCIER); cscier = (cscier & IER_MASK) | IER_OFF; hs_out(sp, cscier, CSCIER); /* hi-Z the outputs to the card */ gcr = hs_in(sp, GCR); gcr &= HD64465_PCCGCR_PDRV; hs_out(sp, gcr, GCR); /* power the card down */ hs_set_voltages(sp, 0, 0); if (sp->mem_base != 0) release_mem_region(sp->mem_base, sp->mem_length); if (sp->irq != 0) { free_irq(sp->irq, hs_interrupt); hd64465_unregister_irq_demux(sp->irq); } if (sp->io_vma != 0) vfree(sp->io_vma->addr); }
static int hs_init_socket(hs_socket_t *sp, int irq, unsigned long mem_base, unsigned int ctrl_base) { unsigned short v; int i, err; memset(sp, 0, sizeof(*sp)); sp->irq = irq; sp->mem_base = mem_base; sp->mem_length = 4*HD64465_PCC_WINDOW; /* 16MB */ sp->ctrl_base = ctrl_base; for (i=0 ; i<MAX_IO_WIN ; i++) sp->io_maps[i].map = i; for (i=0 ; i<MAX_WIN ; i++) sp->mem_maps[i].map = i; if ((sp->io_vma = get_vm_area(HS_IO_MAP_SIZE, VM_IOREMAP)) == 0) return -ENOMEM; hd64465_register_irq_demux(sp->irq, hs_irq_demux, sp); if ((err = request_irq(sp->irq, hs_interrupt, SA_INTERRUPT, MODNAME, sp)) < 0) return err; if (request_mem_region(sp->mem_base, sp->mem_length, MODNAME) == 0) { sp->mem_base = 0; return -ENOMEM; } /* According to section 3.2 of the PCMCIA standard, low-voltage * capable cards must implement cold insertion, i.e. Vpp and * Vcc set to 0 before card is inserted. */ /*hs_set_voltages(sp, 0, 0);*/ /* hi-Z the outputs to the card and set 16MB map mode */ v = hs_in(sp, GCR); v &= ~HD64465_PCCGCR_PCCT; /* memory-only card */ hs_out(sp, v, GCR); v = hs_in(sp, GCR); v |= HD64465_PCCGCR_PDRV; /* enable outputs to card */ hs_out(sp, v, GCR); v = hs_in(sp, GCR); v |= HD64465_PCCGCR_PMMOD; /* 16MB mapping mode */ hs_out(sp, v, GCR); v = hs_in(sp, GCR); /* lowest 16MB of Common */ v &= ~(HD64465_PCCGCR_PPA25|HD64465_PCCGCR_PPA24); hs_out(sp, v, GCR); hs_reset_socket(sp, 1); return 0; }
static void hs_socket_disable_ireq(hs_socket_t *sp) { unsigned short cscier; DPRINTK("hs_socket_disable_ireq(sock=%d)\n", hs_sockno(sp)); cscier = hs_in(sp, CSCIER); cscier &= ~HD64465_PCCCSCIER_PIREQE_MASK; hs_out(sp, cscier, CSCIER); }
/* * Drive the RESET line to the card. */ static void hs_reset_socket(hs_socket_t *sp, int on) { unsigned short v; v = hs_in(sp, GCR); if (on) v |= HD64465_PCCGCR_PCCR; else v &= ~HD64465_PCCGCR_PCCR; hs_out(sp, v, GCR); }
static void hs_socket_enable_ireq(hs_socket_t *sp) { unsigned short cscier; DPRINTK("hs_socket_enable_ireq(sock=%d)\n", sp->number); cscier = hs_in(sp, CSCIER); cscier &= ~HD64465_PCCCSCIER_PIREQE_MASK; cscier |= HD64465_PCCCSCIER_PIREQE_LEVEL; hs_out(sp, cscier, CSCIER); }
/* * This function is registered with the HD64465 glue code to do a * secondary demux step on the PCMCIA interrupts. It handles * mapping the IREQ request from the card to a standard Linux * IRQ, as requested by SocketServices. */ static int hs_irq_demux(int irq, void *dev) { hs_socket_t *sp = (hs_socket_t *)dev; u_int cscr; DPRINTK("hs_irq_demux(irq=%d)\n", irq); if (sp->state.io_irq && (cscr = hs_in(sp, CSCR)) & HD64465_PCCCSCR_PIREQ) { cscr &= ~HD64465_PCCCSCR_PIREQ; hs_out(sp, cscr, CSCR); return sp->state.io_irq; } return irq; }
static void hs_interrupt(int irq, void *dev, struct pt_regs *regs) { hs_socket_t *sp = (hs_socket_t *)dev; u_int events = 0; u_int cscr; cscr = hs_in(sp, CSCR); DPRINTK("hs_interrupt, cscr=%04x\n", cscr); /* check for bus-related changes to be reported to Socket Services */ if (cscr & HD64465_PCCCSCR_PCDC) { /* double-check for a 16-bit card, as we don't support CardBus */ if ((hs_in(sp, ISR) & HD64465_PCCISR_PCD_MASK) != 0) { printk(KERN_NOTICE MODNAME ": socket %d, card not a supported card type or not inserted correctly\n", hs_sockno(sp)); /* Don't do the rest unless a card is present */ cscr &= ~(HD64465_PCCCSCR_PCDC| HD64465_PCCCSCR_PRC| HD64465_PCCCSCR_PBW| HD64465_PCCCSCR_PBD| HD64465_PCCCSCR_PSC); } else { cscr &= ~HD64465_PCCCSCR_PCDC; events |= SS_DETECT; /* card insertion or removal */ } } if (cscr & HD64465_PCCCSCR_PRC) { cscr &= ~HD64465_PCCCSCR_PRC; events |= SS_READY; /* ready signal changed */ } if (cscr & HD64465_PCCCSCR_PBW) { cscr &= ~HD64465_PCCCSCR_PSC; events |= SS_BATWARN; /* battery warning */ } if (cscr & HD64465_PCCCSCR_PBD) { cscr &= ~HD64465_PCCCSCR_PSC; events |= SS_BATDEAD; /* battery dead */ } if (cscr & HD64465_PCCCSCR_PSC) { cscr &= ~HD64465_PCCCSCR_PSC; events |= SS_STSCHG; /* STSCHG (status changed) signal */ } if (cscr & HD64465_PCCCSCR_PIREQ) { cscr &= ~HD64465_PCCCSCR_PIREQ; /* This should have been dealt with during irq demux */ printk(KERN_NOTICE MODNAME ": unexpected IREQ from card\n"); } hs_out(sp, cscr, CSCR); if (events) { /* * Arrange for events to be reported to the registered * event handler function (from CardServices) in a process * context (keventd) "soon". */ spin_lock(&hs_pending_event_lock); sp->pending_events |= events; spin_unlock(&hs_pending_event_lock); schedule_task(&hs_events_task); } }
static int hs_set_socket(unsigned int sock, socket_state_t *state) { hs_socket_t *sp = &hs_sockets[sock]; u_long flags; u_int changed; unsigned short cscier; DPRINTK("hs_set_socket(sock=%d, flags=%x, csc_mask=%x, Vcc=%d, Vpp=%d, io_irq=%d)\n", sock, state->flags, state->csc_mask, state->Vcc, state->Vpp, state->io_irq); save_and_cli(flags); /* Don't want interrupts happening here */ if (state->Vpp != sp->state.Vpp || state->Vcc != sp->state.Vcc) { if (!hs_set_voltages(sp, state->Vcc, state->Vpp)) { restore_flags(flags); return -EINVAL; } } /* hd64465_io_debug = 1; */ /* * Handle changes in the Card Status Change mask, * by propagating to the CSCR register */ changed = sp->state.csc_mask ^ state->csc_mask; cscier = hs_in(sp, CSCIER); if (changed & SS_DETECT) { if (state->csc_mask & SS_DETECT) cscier |= HD64465_PCCCSCIER_PCDE; else cscier &= ~HD64465_PCCCSCIER_PCDE; } if (changed & SS_READY) { if (state->csc_mask & SS_READY) cscier |= HD64465_PCCCSCIER_PRE; else cscier &= ~HD64465_PCCCSCIER_PRE; } if (changed & SS_BATDEAD) { if (state->csc_mask & SS_BATDEAD) cscier |= HD64465_PCCCSCIER_PBDE; else cscier &= ~HD64465_PCCCSCIER_PBDE; } if (changed & SS_BATWARN) { if (state->csc_mask & SS_BATWARN) cscier |= HD64465_PCCCSCIER_PBWE; else cscier &= ~HD64465_PCCCSCIER_PBWE; } if (changed & SS_STSCHG) { if (state->csc_mask & SS_STSCHG) cscier |= HD64465_PCCCSCIER_PSCE; else cscier &= ~HD64465_PCCCSCIER_PSCE; } hs_out(sp, cscier, CSCIER); if (sp->state.io_irq && !state->io_irq) hs_unmap_irq(sp, sp->state.io_irq); else if (!sp->state.io_irq && state->io_irq) hs_map_irq(sp, state->io_irq); /* * Handle changes in the flags field, * by propagating to config registers. */ changed = sp->state.flags ^ state->flags; if (changed & SS_IOCARD) { DPRINTK("card type: %s\n", (state->flags & SS_IOCARD ? "i/o" : "memory" )); bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCT, state->flags & SS_IOCARD); } if (changed & SS_RESET) { DPRINTK("%s reset card\n", (state->flags & SS_RESET ? "start" : "stop")); bool_to_regbit(sp, GCR, HD64465_PCCGCR_PCCR, state->flags & SS_RESET); } if (changed & SS_OUTPUT_ENA) { DPRINTK("%sabling card output\n", (state->flags & SS_OUTPUT_ENA ? "en" : "dis")); bool_to_regbit(sp, GCR, HD64465_PCCGCR_PDRV, state->flags & SS_OUTPUT_ENA); } /* TODO: SS_SPKR_ENA */ /* hd64465_io_debug = 0; */ sp->state = *state; restore_flags(flags); #if HD64465_DEBUG > 10 if (state->flags & SS_OUTPUT_ENA) cis_hex_dump((const unsigned char*)sp->mem_base, 0x100); #endif return 0; }