⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hd64465_ss.c

📁 linux和2410结合开发 用他可以生成2410所需的zImage文件
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * $Id$ * * Device driver for the PCMCIA controller module of the * Hitachi HD64465 handheld companion chip. * * Note that the HD64465 provides a very thin PCMCIA host bridge * layer, requiring a lot of the work of supporting cards to be * performed by the processor.  For example: mapping of card * interrupts to processor IRQs is done by IRQ demuxing software; * IO and memory mappings are fixed; setting voltages according * to card Voltage Select pins etc is done in software. * * Note also that this driver uses only the simple, fixed, * 16MB, 16-bit wide mappings to PCMCIA spaces defined by the * HD64465.  Larger mappings, smaller mappings, or mappings of * different width to the same socket, are all possible only by * involving the SH7750's MMU, which is considered unnecessary here. * The downside is that it may be possible for some drivers to * break because they need or expect 8-bit mappings. * * This driver currently supports only the following configuration: * SH7750 CPU, HD64465, TPS2206 voltage control chip. * * by Greg Banks <gbanks@pocketpenguins.com> * (c) 2000 PocketPenguins Inc * */#include <linux/types.h>#include <linux/module.h>#include <linux/init.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/ioport.h>#include <linux/mm.h>#include <linux/vmalloc.h>#include <asm/errno.h>#include <linux/irq.h>#include <asm/io.h>#include <asm/hd64465.h>#include <pcmcia/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/ss.h>#include <pcmcia/bulkmem.h>#include <pcmcia/cistpl.h>#include "cs_internal.h"#define MODNAME "hd64465_ss"/* #define HD64465_DEBUG 1 */#ifndef HD64465_DEBUG#define HD64465_DEBUG 0#endif#if HD64465_DEBUG#define DPRINTK(args...)	printk(MODNAME ": " args)#else#define DPRINTK(args...)#endifextern int hd64465_io_debug;/*============================================================*/#define HS_IO_MAP_SIZE 	(64*1024)typedef struct hs_socket_t{    u_int   	    	irq;    u_long  	    	mem_base;    u_long  	    	mem_length;    void	    	(*handler)(void *info, u_int events);    void    	    	*handler_info;    u_int   	    	pending_events;    u_int   	    	ctrl_base;    socket_state_t  	state;    pccard_io_map     	io_maps[MAX_IO_WIN];    pccard_mem_map  	mem_maps[MAX_WIN];    struct vm_struct	*io_vma;    /* allocated kernel vm for mapping IO space */} hs_socket_t;#define HS_MAX_SOCKETS 2static hs_socket_t hs_sockets[HS_MAX_SOCKETS];static spinlock_t hs_pending_event_lock = SPIN_LOCK_UNLOCKED;/* Calculate socket number from ptr into hs_sockets[] */#define hs_sockno(sp) 	(sp - hs_sockets)static socket_cap_t hs_socket_cap ={    SS_CAP_PCCARD   	    /* support 16 bit cards */    |SS_CAP_STATIC_MAP      /* mappings are fixed in host memory */    ,    0xffde/*0xffff*/, 	    /* IRQs mapped in s/w so can do any, really */    HD64465_PCC_WINDOW,     /* 16MB fixed window size */    0,	    	    	    /* no PCI support */    0,	    	    	    /* no CardBus support */    0	    	    	    /* no bus operations needed */};#define hs_in(sp, r)	    inb((sp)->ctrl_base + (r))#define hs_out(sp, v, r)    outb(v, (sp)->ctrl_base + (r))/* translate a boolean value to a bit in a register */#define bool_to_regbit(sp, r, bi, bo)	    	\    do {    	    	    	    	    	\    	unsigned short v = hs_in(sp, r);    	\	if (bo)     	    	    	    	\	    v |= (bi);	    	    	    	\	else	    	    	    	    	\	    v &= ~(bi);     	    	    	\	hs_out(sp, v, r);   	    	    	\    } while(0)    /* register offsets from HD64465_REG_PCC[01]ISR */#define ISR 	0x0#define GCR 	0x2#define CSCR 	0x4#define CSCIER 	0x6#define SCR 	0x8/* Mask and values for CSCIER register */#define IER_MASK    0x80#define IER_ON	    0x3f    	/* interrupts on */#define IER_OFF     0x00    	/* interrupts off *//*============================================================*/#if HD64465_DEBUG > 10static void cis_hex_dump(const unsigned char *x, int len){    	int i;	    	for (i=0 ; i<len ; i++)	{	    if (!(i & 0xf))	    	printk("\n%08x", (unsigned)(x + i));	    printk(" %02x", *(volatile unsigned short*)x);	    x += 2;	}	printk("\n");}#endif/*============================================================*//* * This code helps create the illusion that the IREQ line from * the PC card is mapped to one of the CPU's IRQ lines by the * host bridge hardware (which is how every host bridge *except* * the HD64465 works).  In particular, it supports enabling * and disabling the IREQ line by code which knows nothing * about the host bridge (e.g. device drivers, IDE code) using * the request_irq(), free_irq(), probe_irq_on() and probe_irq_off() * functions.  Also, it supports sharing the mapped IRQ with * real hardware IRQs from the -IRL0-3 lines. */#define HS_NUM_MAPPED_IRQS  16	/* Limitation of the PCMCIA code */static struct{    /* index is mapped irq number */    hs_socket_t *sock;    hw_irq_controller *old_handler;} hs_mapped_irq[HS_NUM_MAPPED_IRQS];static void hs_socket_enable_ireq(hs_socket_t *sp){    	unsigned short cscier;	    	DPRINTK("hs_socket_enable_ireq(sock=%d)\n", hs_sockno(sp));    	cscier = hs_in(sp, CSCIER);	cscier &= ~HD64465_PCCCSCIER_PIREQE_MASK;    	cscier |= HD64465_PCCCSCIER_PIREQE_LEVEL;	hs_out(sp, cscier, CSCIER);}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);}static unsigned int hs_startup_irq(unsigned int irq){	hs_socket_enable_ireq(hs_mapped_irq[irq].sock);	hs_mapped_irq[irq].old_handler->startup(irq);	return 0;}static void hs_shutdown_irq(unsigned int irq){	hs_socket_disable_ireq(hs_mapped_irq[irq].sock);	hs_mapped_irq[irq].old_handler->shutdown(irq);}static void hs_enable_irq(unsigned int irq){	hs_socket_enable_ireq(hs_mapped_irq[irq].sock);	hs_mapped_irq[irq].old_handler->enable(irq);}static void hs_disable_irq(unsigned int irq){	hs_socket_disable_ireq(hs_mapped_irq[irq].sock);	hs_mapped_irq[irq].old_handler->disable(irq);}extern struct hw_interrupt_type no_irq_type;static void hs_mask_and_ack_irq(unsigned int irq){	hs_socket_disable_ireq(hs_mapped_irq[irq].sock);	/* ack_none() spuriously complains about an unexpected IRQ */	if (hs_mapped_irq[irq].old_handler != &no_irq_type)	    hs_mapped_irq[irq].old_handler->ack(irq);}static void hs_end_irq(unsigned int irq){	hs_socket_enable_ireq(hs_mapped_irq[irq].sock);	hs_mapped_irq[irq].old_handler->end(irq);}static struct hw_interrupt_type hd64465_ss_irq_type = {	typename:	"PCMCIA-IRQ",	startup:	hs_startup_irq,	shutdown:	hs_shutdown_irq,	enable:		hs_enable_irq,	disable:	hs_disable_irq,	ack:		hs_mask_and_ack_irq,	end:		hs_end_irq};/*  * This function should only ever be called with interrupts disabled. */static void hs_map_irq(hs_socket_t *sp, unsigned int irq){    	DPRINTK("hs_map_irq(sock=%d irq=%d)\n", hs_sockno(sp), irq);		if (irq >= HS_NUM_MAPPED_IRQS)	    return;    	hs_mapped_irq[irq].sock = sp;	/* insert ourselves as the irq controller */	hs_mapped_irq[irq].old_handler = irq_desc[irq].handler;	irq_desc[irq].handler = &hd64465_ss_irq_type;}/*  * This function should only ever be called with interrupts disabled. */static void hs_unmap_irq(hs_socket_t *sp, unsigned int irq){    	DPRINTK("hs_unmap_irq(sock=%d irq=%d)\n", hs_sockno(sp), irq);		if (irq >= HS_NUM_MAPPED_IRQS)	    return;			/* restore the original irq controller */	irq_desc[irq].handler = hs_mapped_irq[irq].old_handler;}/*============================================================*//* * Set Vpp and Vcc (in tenths of a Volt).  Does not * support the hi-Z state. * * Note, this assumes the board uses a TPS2206 chip to control * the Vcc and Vpp voltages to the hs_sockets.  If your board * uses the MIC2563 (also supported by the HD64465) then you * will have to modify this function. */    	    	    	    	         /* 0V   3.3V  5.5V */static const u_char hs_tps2206_avcc[3] = { 0x00, 0x04, 0x08 };static const u_char hs_tps2206_bvcc[3] = { 0x00, 0x80, 0x40 };static int hs_set_voltages(hs_socket_t *sp, int Vcc, int Vpp){    	u_int psr;	u_int vcci = 0;	u_int sock = hs_sockno(sp);	    	DPRINTK("hs_set_voltage(%d, %d, %d)\n", sock, Vcc, Vpp);    	switch (Vcc)	{	case 0:  vcci = 0; break;	case 33: vcci = 1; break;	case 50: vcci = 2; break;	default: return 0;	}    	/* Note: Vpp = 120 not supported -- Greg Banks */	if (Vpp != 0 && Vpp != Vcc)	    return 0;		/* The PSR register holds 8 of the 9 bits which control	 * the TPS2206 via its serial interface.	 */	psr = inw(HD64465_REG_PCCPSR);	switch (sock)	{	case 0:	    psr &= 0x0f;	    psr |= hs_tps2206_avcc[vcci];	    psr |= (Vpp == 0 ? 0x00 : 0x02);	    break;	case 1:	    psr &= 0xf0;	    psr |= hs_tps2206_bvcc[vcci];	    psr |= (Vpp == 0 ? 0x00 : 0x20);	    break;	};	outw(psr, HD64465_REG_PCCPSR);		return 1;}/*============================================================*//* * 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 int hs_init(unsigned int sock){    	hs_socket_t *sp = &hs_sockets[sock];	    	DPRINTK("hs_init(%d)\n", sock);		sp->pending_events = 0;	sp->state.Vcc = 0;	sp->state.Vpp = 0;	hs_set_voltages(sp, 0, 0);		return 0;}/*============================================================*/static int hs_suspend(unsigned int sock){    	DPRINTK("hs_suspend(%d)\n", sock);    	/* TODO */		return 0;}/*============================================================*/static int hs_register_callback(unsigned int sock,    	    void (*handler)(void *, unsigned int), void * info){    	hs_socket_t *sp = &hs_sockets[sock];	    	DPRINTK("hs_register_callback(%d)\n", sock);	sp->handler = handler;	sp->handler_info = info;	if (handler == 0) {	    MOD_DEC_USE_COUNT;	} else {	    MOD_INC_USE_COUNT;	}	return 0;}/*============================================================*/static int hs_inquire_socket(unsigned int sock, socket_cap_t *cap){    	DPRINTK("hs_inquire_socket(%d)\n", sock);	*cap = hs_socket_cap;	return 0;}/*============================================================*/static int hs_get_status(unsigned int sock, u_int *value){    	hs_socket_t *sp = &hs_sockets[sock];    	unsigned int isr;	u_int status = 0;			isr = hs_in(sp, ISR);    	/* Card is seated and powered when *both* CD pins are low */	if ((isr & HD64465_PCCISR_PCD_MASK) == 0)    	{	    status |= SS_DETECT;    /* card present */	    switch (isr & HD64465_PCCISR_PBVD_MASK)	    {	    case HD64465_PCCISR_PBVD_BATGOOD:   		break;	    case HD64465_PCCISR_PBVD_BATWARN:		status |= SS_BATWARN;		break;	    default:		status |= SS_BATDEAD;		break;	    }	    if (isr & HD64465_PCCISR_PREADY)		status |= SS_READY;	    if (isr & HD64465_PCCISR_PMWP)		status |= SS_WRPROT;			    /* Voltage Select pins interpreted as per Table 4-5 of the std.	     * Assuming we have the TPS2206, the socket is a "Low Voltage	     * key, 3.3V and 5V available, no X.XV available".	     */	    switch (isr & (HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1))	    {	    case HD64465_PCCISR_PVS1:	    	printk(KERN_NOTICE MODNAME ": cannot handle X.XV card, ignored\n");		status = 0;	    	break;	    case 0:	    case HD64465_PCCISR_PVS2:    	    	/* 3.3V */    	    	status |= SS_3VCARD;	    	break;	    case HD64465_PCCISR_PVS2|HD64465_PCCISR_PVS1:	    	/* 5V */	    	break;	    }			    /* TODO: SS_POWERON */	    /* TODO: SS_STSCHG */    	}		    	DPRINTK("hs_get_status(%d) = %x\n", sock, status);		*value = status;	return 0;}/*============================================================*/static int hs_get_socket(unsigned int sock, socket_state_t *state){    	hs_socket_t *sp = &hs_sockets[sock];    	DPRINTK("hs_get_socket(%d)\n", sock);		*state = sp->state;	return 0;}/*============================================================*/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;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -