hd64465_ss.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 983 行 · 第 1/2 页
C
983 行
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; local_irq_restore(flags);#if HD64465_DEBUG > 10 if (state->flags & SS_OUTPUT_ENA) cis_hex_dump((const unsigned char*)sp->mem_base, 0x100);#endif return 0;}/*============================================================*/static int hs_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io){ hs_socket_t *sp = container_of(s, struct hs_socket_t, socket); int map = io->map; int sock = sp->number; struct pccard_io_map *sio; pgprot_t prot; DPRINTK("hs_set_io_map(sock=%d, map=%d, flags=0x%x, speed=%dns, start=0x%04x, stop=0x%04x)\n", sock, map, io->flags, io->speed, io->start, io->stop); if (map >= MAX_IO_WIN) return -EINVAL; sio = &sp->io_maps[map]; /* check for null changes */ if (io->flags == sio->flags && io->start == sio->start && io->stop == sio->stop) return 0; if (io->flags & MAP_AUTOSZ) prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IODYN); else if (io->flags & MAP_16BIT) prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO16); else prot = PAGE_KERNEL_PCC(sock, _PAGE_PCC_IO8); /* TODO: handle MAP_USE_WAIT */ if (io->flags & MAP_USE_WAIT) printk(KERN_INFO MODNAME ": MAP_USE_WAIT unimplemented\n"); /* TODO: handle MAP_PREFETCH */ if (io->flags & MAP_PREFETCH) printk(KERN_INFO MODNAME ": MAP_PREFETCH unimplemented\n"); /* TODO: handle MAP_WRPROT */ if (io->flags & MAP_WRPROT) printk(KERN_INFO MODNAME ": MAP_WRPROT unimplemented\n"); /* TODO: handle MAP_0WS */ if (io->flags & MAP_0WS) printk(KERN_INFO MODNAME ": MAP_0WS unimplemented\n"); if (io->flags & MAP_ACTIVE) { unsigned long pstart, psize, paddrbase; paddrbase = virt_to_phys((void*)(sp->mem_base + 2 * HD64465_PCC_WINDOW)); pstart = io->start & PAGE_MASK; psize = ((io->stop + PAGE_SIZE) & PAGE_MASK) - pstart; /* * Change PTEs in only that portion of the mapping requested * by the caller. This means that most of the time, most of * the PTEs in the io_vma will be unmapped and only the bottom * page will be mapped. But the code allows for weird cards * that might want IO ports > 4K. */ sp->io_base = p3_ioremap(paddrbase + pstart, psize, pgprot_val(prot)); /* * Change the mapping used by inb() outb() etc */ hd64465_port_map(io->start, io->stop - io->start + 1, (unsigned long)sp->io_base + io->start, 0); } else { hd64465_port_unmap(sio->start, sio->stop - sio->start + 1); p3_iounmap(sp->io_base); } *sio = *io; return 0;}/*============================================================*/static int hs_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem){ hs_socket_t *sp = container_of(s, struct hs_socket_t, socket); struct pccard_mem_map *smem; int map = mem->map; unsigned long paddr;#if 0 DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, card_start=0x%08x)\n", sock, map, mem->flags, mem->card_start);#endif if (map >= MAX_WIN) return -EINVAL; smem = &sp->mem_maps[map]; paddr = sp->mem_base; /* base of Attribute mapping */ if (!(mem->flags & MAP_ATTRIB)) paddr += HD64465_PCC_WINDOW; /* base of Common mapping */ paddr += mem->card_start; /* Because we specified SS_CAP_STATIC_MAP, we are obliged * at this time to report the system address corresponding * to the card address requested. This is how Socket Services * queries our fixed mapping. I wish this fact had been * documented - Greg Banks. */ mem->static_start = paddr; *smem = *mem; return 0;}/* TODO: do we need to use the MMU to access Common memory ??? *//*============================================================*//* * 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;}/*============================================================*//* * Interrupt handling routine. */ static irqreturn_t 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", sp->number); /* 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) pcmcia_parse_events(&sp->socket, events); return IRQ_HANDLED;}/*============================================================*/static struct pccard_operations hs_operations = { .init = hs_init, .suspend = hs_suspend, .get_status = hs_get_status, .get_socket = hs_get_socket, .set_socket = hs_set_socket, .set_io_map = hs_set_io_map, .set_mem_map = hs_set_mem_map,};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; 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); printk(KERN_INFO "HD64465 PCMCIA bridge socket %d at 0x%08lx irq %d\n", i, sp->mem_base, sp->irq); return 0;}static void hs_exit_socket(hs_socket_t *sp){ unsigned short cscier, gcr; unsigned long flags; local_irq_save(flags); /* 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); } local_irq_restore(flags);}static int hd64465_suspend(struct device *dev, u32 state, u32 level){ int ret = 0; if (level == SUSPEND_SAVE_STATE) ret = pcmcia_socket_dev_suspend(dev, state); return ret;}static int hd64465_resume(struct device *dev, u32 level){ int ret = 0; if (level == RESUME_RESTORE_STATE) ret = pcmcia_socket_dev_resume(dev); return ret;}static struct device_driver hd64465_driver = { .name = "hd64465-pcmcia", .bus = &platform_bus_type, .suspend = hd64465_suspend, .resume = hd64465_resume,};static struct platform_device hd64465_device = { .name = "hd64465-pcmcia", .id = 0,};static int __init init_hs(void){ int i; unsigned short v;/* hd64465_io_debug = 1; */ if (driver_register(&hd64465_driver)) return -EINVAL; /* Wake both sockets out of STANDBY mode */ /* TODO: wait 15ms */ v = inw(HD64465_REG_SMSCR); v &= ~(HD64465_SMSCR_PC0ST|HD64465_SMSCR_PC1ST); outw(v, HD64465_REG_SMSCR); /* keep power controller out of shutdown mode */ v = inb(HD64465_REG_PCC0SCR); v |= HD64465_PCCSCR_SHDN; outb(v, HD64465_REG_PCC0SCR); /* use serial (TPS2206) power controller */ v = inb(HD64465_REG_PCC0CSCR); v |= HD64465_PCCCSCR_PSWSEL; outb(v, HD64465_REG_PCC0CSCR); /* * Setup hs_sockets[] structures and request system resources. * TODO: on memory allocation failure, power down the socket * before quitting. */ for (i=0; i<HS_MAX_SOCKETS; i++) { hs_set_voltages(&hs_sockets[i], 0, 0); hs_sockets[i].socket.features |= SS_CAP_PCCARD | SS_CAP_STATIC_MAP; /* mappings are fixed in host memory */ hs_sockets[i].socket.irq_mask = 0xffde;/*0xffff*/ /* IRQs mapped in s/w so can do any, really */ hs_sockets[i].socket.map_size = HD64465_PCC_WINDOW; /* 16MB fixed window size */ hs_sockets[i].socket.owner = THIS_MODULE; hs_sockets[i].socket.ss_entry = &hs_operations; } i = hs_init_socket(&hs_sockets[0], HD64465_IRQ_PCMCIA0, HD64465_PCC0_BASE, HD64465_REG_PCC0ISR); if (i < 0) { unregister_driver(&hd64465_driver); return i; } i = hs_init_socket(&hs_sockets[1], HD64465_IRQ_PCMCIA1, HD64465_PCC1_BASE, HD64465_REG_PCC1ISR); if (i < 0) { unregister_driver(&hd64465_driver); return i; }/* hd64465_io_debug = 0; */ platform_device_register(&hd64465_device); for (i=0; i<HS_MAX_SOCKETS; i++) { unsigned int ret; hs_sockets[i].socket.dev.dev = &hd64465_device.dev; hs_sockets[i].number = i; ret = pcmcia_register_socket(&hs_sockets[i].socket); if (ret && i) pcmcia_unregister_socket(&hs_sockets[0].socket); } return 0;}static void __exit exit_hs(void){ int i; for (i=0 ; i<HS_MAX_SOCKETS ; i++) { pcmcia_unregister_socket(&hs_sockets[i].socket); hs_exit_socket(&hs_sockets[i]); } platform_device_unregister(&hd64465_device); unregister_driver(&hd64465_driver);}module_init(init_hs);module_exit(exit_hs);/*============================================================*//*END*/
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?