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 + -
显示快捷键?