/*
 * drivers/pcmcia/sa1100_bitsy.c
 *
 * PCMCIA implementation routines for Bitsy
 *
 */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/i2c.h>

#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>

#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/pcmcia.h>
#include <asm/arch/h3600-sleeve.h>
#include <asm/arch/linkup-l1110.h>
#include <asm/arch/backpaq.h>




static int debug = 0;

#if defined(CONFIG_MERCURY_BACKPAQ) || defined(CONFIG_MERCURY_BACKPAQ_MODULE)
#include <asm/arch/backpaq.h>

#define VERSIONID_SOCKETNUM 1
#define VERSIONID_RESET_CAP 2 /* this bit set if supports per-socket reset */ 

static struct { 
        volatile short status;
        short pad1;
        volatile short versionid; 
        short pad2;
        volatile short reset_deassert; /* reading this location deasserts reset to this socket */
        short pad3;
        volatile short reset_assert; /* reading this location asserts reset to this socket */
        short pad4;
} *mercury_backpaq_socket[2];

#define USES5V_0 (((mercury_backpaq_socket[0]->status) & BACKPAQ_SOCKET_VS1) && \
		   ((mercury_backpaq_socket[0]->status) & BACKPAQ_SOCKET_VS2) && \
                   ((mercury_backpaq_socket[0]->status) & BACKPAQ_SOCKET_CD0) == 0)

#define USES5V_1 (((mercury_backpaq_socket[1]->status) & BACKPAQ_SOCKET_VS1) && \
		   ((mercury_backpaq_socket[1]->status) & BACKPAQ_SOCKET_VS2) && \
                   ((mercury_backpaq_socket[1]->status) & BACKPAQ_SOCKET_CD1) == 0)

#endif /* CONFIG_MERCURY_BACKPAQ */

#if defined(CONFIG_H3600_SLEEVE) || defined(CONFIG_H3600_SLEEVE_MODULE)
static enum sleeve_device sleeve_type = SLEEVE_NO_DEVICE;
#else
/* default to behavior of PCMCIA sleeve (also works for CF) */
static enum sleeve_device sleeve_type = SINGLE_PCMCIA_SLEEVE;
#endif /* CONFIG_H3600_SLEEVE */

static struct linkup_l1110 *dual_pcmcia_sleeve[2]; 
static struct pcmcia_init sa1100_bitsy_pcmcia_init;

static void msleep(unsigned int msec)
{
	current->state = TASK_INTERRUPTIBLE;
	schedule_timeout( (msec * HZ + 999) / 1000);
}


static int bitsy_pcmcia_init(struct pcmcia_init *init)
{
        int irq, res;
	int nsockets = 2;

	//printk("****" __FUNCTION__ ": initializing type %x\n", sleeve_type);

        sa1100_bitsy_pcmcia_init = *init;
        switch (sleeve_type) {
        case SLEEVE_NO_DEVICE: 
	case EXTENDED_BATTERY_SLEEVE:
	case GPRS_EXPANSION_PACK:
	case BLUETOOTH_EXPANSION_PACK:
                break;

        case MERCURY_BACKPAQ: 
	case MERCURY_BACKPAQ_UNPROGRAMMED:
#if defined(CONFIG_MERCURY_BACKPAQ) || defined(CONFIG_MERCURY_BACKPAQ_MODULE)
        {
                nsockets = 2;
		mercury_backpaq_socket[0] = (void *) &BackpaqSocketPCMCIABase_0;
		mercury_backpaq_socket[1] = (void *) &BackpaqSocketPCMCIABase_1;
                set_bitsy_egpio(EGPIO_BITSY_OPT_ON);
        }
        goto common_init;
#endif
        case DUAL_PCMCIA_SLEEVE: {
                nsockets = 2;
                dual_pcmcia_sleeve[0] = (struct linkup_l1110 *)__ioremap(0x1a000000, PAGE_SIZE, 0);
                dual_pcmcia_sleeve[1] = (struct linkup_l1110 *)__ioremap(0x19000000, PAGE_SIZE, 0);
                if (0) printk(__FUNCTION__ ":%d &prc0=%p &prc1=%p\n",
                              __LINE__, 
                              dual_pcmcia_sleeve[0], dual_pcmcia_sleeve[1]);
                dual_pcmcia_sleeve[0]->prc = (LINKUP_PRC_S2|LINKUP_PRC_S1);
                dual_pcmcia_sleeve[1]->prc = (LINKUP_PRC_S2|LINKUP_PRC_S1|LINKUP_PRC_SSP);
                set_bitsy_egpio(EGPIO_BITSY_OPT_NVRAM_ON|EGPIO_BITSY_OPT_ON);
	} goto common_init;

        case SINGLE_COMPACTFLASH_SLEEVE:
        case SINGLE_PCMCIA_SLEEVE:
	common_init:
        /* Enable PCMCIA/CF bus: */
        set_bitsy_egpio(EGPIO_BITSY_OPT_NVRAM_ON);
        clr_bitsy_egpio(EGPIO_BITSY_OPT_RESET);
        clr_bitsy_egpio(EGPIO_BITSY_CARD_RESET);

        /* All those are inputs */
        GPDR &= ~(GPIO_BITSY_PCMCIA_CD0 | GPIO_BITSY_PCMCIA_CD1 | GPIO_BITSY_PCMCIA_IRQ0| GPIO_BITSY_PCMCIA_IRQ1);

        /* Set transition detect */
        set_GPIO_IRQ_edge( GPIO_BITSY_PCMCIA_CD0 | GPIO_BITSY_PCMCIA_CD1, GPIO_BOTH_EDGES );
        set_GPIO_IRQ_edge( GPIO_BITSY_PCMCIA_IRQ0| GPIO_BITSY_PCMCIA_IRQ1, GPIO_FALLING_EDGE );

        /* Register interrupts */
        irq = IRQ_GPIO_BITSY_PCMCIA_CD0;
        res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD0", NULL );
        if( res < 0 ) goto irq_err;
        irq = IRQ_GPIO_BITSY_PCMCIA_CD1;
        res = request_irq( irq, init->handler, SA_INTERRUPT, "PCMCIA_CD1", NULL );
        if( res < 0 ) goto irq_err;
        }

        return 2;

irq_err:
        printk( KERN_ERR __FUNCTION__ ": Request for IRQ %u failed\n", irq );
        return -1;
}

static int bitsy_pcmcia_shutdown(void)
{
    //if (1) printk("*****" __FUNCTION__ " on %x\n", sleeve_type);
        switch (sleeve_type) {
        case DUAL_PCMCIA_SLEEVE: {
		__iounmap(dual_pcmcia_sleeve[0]);
		__iounmap(dual_pcmcia_sleeve[1]);
	} goto common_shutdown;
        case MERCURY_BACKPAQ:
	case MERCURY_BACKPAQ_UNPROGRAMMED:
		goto common_shutdown;
        case SINGLE_COMPACTFLASH_SLEEVE:
        case SINGLE_PCMCIA_SLEEVE:
	common_shutdown:
                /* disable IRQs */
                free_irq( IRQ_GPIO_BITSY_PCMCIA_CD0, NULL );
                free_irq( IRQ_GPIO_BITSY_PCMCIA_CD1, NULL );
  
                /* Disable CF bus: */
                clr_bitsy_egpio(EGPIO_BITSY_OPT_NVRAM_ON|EGPIO_BITSY_OPT_ON);
                set_bitsy_egpio(EGPIO_BITSY_OPT_RESET);
                break;
        case SLEEVE_NO_DEVICE:
	case EXTENDED_BATTERY_SLEEVE:
	case GPRS_EXPANSION_PACK:
	case BLUETOOTH_EXPANSION_PACK:
		break;
        }

        return 0;
}

static int bitsy_pcmcia_socket_state(struct pcmcia_state_array *state_array)
{
        unsigned long levels;
	int sock;
        if(state_array->size<2) return -1;

        memset(state_array->state, 0, 
               (state_array->size)*sizeof(struct pcmcia_state));


	
	levels=GPLR;
	
	state_array->state[0].detect=((levels & GPIO_BITSY_PCMCIA_CD0)==0)?1:0;
	state_array->state[0].ready=(levels & GPIO_BITSY_PCMCIA_IRQ0)?1:0;
	state_array->state[1].detect=((levels & GPIO_BITSY_PCMCIA_CD1)==0)?1:0;
	state_array->state[1].ready=(levels & GPIO_BITSY_PCMCIA_IRQ1)?1:0;



        switch (sleeve_type) {
        case SINGLE_PCMCIA_SLEEVE:
        case SINGLE_COMPACTFLASH_SLEEVE:
	    for (sock = 0; sock < 2; sock++) { 
		/* no bvd or vs bits on single pcmcia sleeve or CF sleeve */
		state_array->state[sock].bvd1=1;
		state_array->state[sock].bvd2=1;
		state_array->state[sock].wrprot=0; /* Not available on Bitsy. */
		state_array->state[sock].vs_3v=0;
		state_array->state[sock].vs_Xv=0;
	    }
	    break;	    
        case DUAL_PCMCIA_SLEEVE:
	    for (sock = 0; sock < 2; sock++) { 
		short prs = dual_pcmcia_sleeve[sock]->prc;
		state_array->state[sock].bvd1 = prs & LINKUP_PRS_BVD1;
		state_array->state[sock].bvd2 = prs & LINKUP_PRS_BVD2;
		state_array->state[sock].wrprot = 0;
		if ((prs & LINKUP_PRS_VS1) == 0)
		    state_array->state[sock].vs_3v = 1;
		if ((prs & LINKUP_PRS_VS2) == 0)
		    state_array->state[sock].vs_Xv = 1;
	    }
	    break;
	case MERCURY_BACKPAQ_UNPROGRAMMED:
	case MERCURY_BACKPAQ: {		
#if defined(CONFIG_MERCURY_BACKPAQ) || defined(CONFIG_MERCURY_BACKPAQ_MODULE)
       /*  ok, so here's the deal, we need to clean up
	*  after a 5 volt card has been removed
	*  unfortunately there isnt a callback for
	*  eject. So, we configure the power on insertion
	*  and we use this polling routine to clean up 
	*  after an eject
	* */
		int curval = BackpaqSysctlPCMCIAPower;
		// have 5v on and noone using it
		if ((curval & BACKPAQ_PWRCTL_50VEN) && !(USES5V_0 || USES5V_1)){
		    printk(__FUNCTION__ ": Turning off 5V power\n");
		    BackpaqSysctlPCMCIAPower &= ~BACKPAQ_PWRCTL_50VEN;
		}
		
		
		for (sock = 0; sock < 2; sock++) { 
		    int status = mercury_backpaq_socket[sock]->status;
		    int bvd1 = ((status & BACKPAQ_SOCKET_BVD1) ? 1 : 0);
		    int bvd2 = ((status & BACKPAQ_SOCKET_BVD2) ? 1 : 0);
		
		    if (0) printk(__FUNCTION__ ":%d socket[%d]->status=%x\n",
				  __LINE__, sock,
				  mercury_backpaq_socket[sock]->status);
		    state_array->state[sock].bvd1 = bvd1;
		    state_array->state[sock].bvd2 = bvd2;
		    state_array->state[sock].wrprot = 0;
		    if ((status & BACKPAQ_SOCKET_VS1) == 0)
			state_array->state[sock].vs_3v = 1;
		    if ((status & BACKPAQ_SOCKET_VS2) == 0)
			state_array->state[sock].vs_Xv = 1;
		}	    
#endif
	    }	   
	    break;
	    
        case SLEEVE_NO_DEVICE:
        default:
	    break;
        }	
        return 1;
}

static int bitsy_pcmcia_get_irq_info(struct pcmcia_irq_info *info)
{

        switch (info->sock) {
        case 0:
                info->irq=IRQ_GPIO_BITSY_PCMCIA_IRQ0;
                break;
        case 1:
                info->irq=IRQ_GPIO_BITSY_PCMCIA_IRQ1;
                break;
        default:
                return -1;
        }
        return 0;
}

static int bitsy_pcmcia_configure_socket(const struct pcmcia_configure *configure)
{

        int sock = configure->sock;
        if(sock>1) return -1;

        if (sleeve_type != SLEEVE_NO_DEVICE)
               printk(__FUNCTION__ ": socket=%d vcc=%d vpp=%d reset=%d\n", 
                       sock, configure->vcc, configure->vpp, configure->reset);

        switch (sleeve_type) {
        case SINGLE_COMPACTFLASH_SLEEVE:
        case SINGLE_PCMCIA_SLEEVE: { 
                unsigned long flags;

                save_flags_cli(flags);
                switch (configure->vcc) {
                case 0:
                        clr_bitsy_egpio(EGPIO_BITSY_OPT_ON);
                        break;

                case 50:
                case 33:
                        set_bitsy_egpio(EGPIO_BITSY_OPT_ON);
                        break;

                default:
                        printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
                               configure->vcc);
                        restore_flags(flags);
                        return -1;
                }

                if (configure->reset)
                        set_bitsy_egpio(EGPIO_BITSY_CARD_RESET);
                else
                        clr_bitsy_egpio(EGPIO_BITSY_CARD_RESET);
                restore_flags(flags);
        } break;

        case DUAL_PCMCIA_SLEEVE: {
                unsigned long flags;
		unsigned int prc = 
			(LINKUP_PRC_APOE | LINKUP_PRC_SOE | LINKUP_PRC_S1 | LINKUP_PRC_S2
			 | (sock * LINKUP_PRC_SSP));

                save_flags_cli(flags);

		/* Linkup Systems L1110 with TI TPS2205 PCMCIA Power Switch */
		/* S1 is VCC5#, S2 is VCC3# */ 
		/* S3 is VPP_VCC, S4 is VPP_PGM */
		/* PWR_ON is wired to #SHDN */
                switch (configure->vcc) {
                case 0:
                        break;
                case 50:
			prc &= ~LINKUP_PRC_S1;
			break;
                case 33:
			prc &= ~LINKUP_PRC_S2;
                        break;
                default:
                        printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
                               configure->vcc);
                        restore_flags(flags);
                        return -1;
                }
		if (configure->vpp == 12) {
			prc |= LINKUP_PRC_S4;
		} else if (configure->vpp == configure->vcc) {
			prc |= LINKUP_PRC_S3;
		}

                if (configure->reset)
                        prc |= LINKUP_PRC_RESET;

		dual_pcmcia_sleeve[sock]->prc = prc;

                restore_flags(flags);
	} break;

        case MERCURY_BACKPAQ:
	case MERCURY_BACKPAQ_UNPROGRAMMED: {
#if defined(CONFIG_MERCURY_BACKPAQ) || defined(CONFIG_MERCURY_BACKPAQ_MODULE)
                unsigned long flags;

		int shift = (sock ? 4 : 0);
	        int mask = 0xf << shift;
                int curval = BackpaqSysctlPCMCIAPower;
		int val = 0;

                save_flags_cli(flags);

                switch (configure->vcc) {
                case 0:
                        break;
                case 50:
		    val |= (BACKPAQ_PCMCIA_A5VEN << shift);
                        break;
                case 33:
			val |= (BACKPAQ_PCMCIA_A3VEN << shift);
                        break;
                default:
                        printk(KERN_ERR "%s(): unrecognized Vcc %u\n", __FUNCTION__,
                               configure->vcc);
                        restore_flags(flags);
                        return -1;
                }

                switch (configure->vpp) {
                case 0:
                        break;
                case 50:
                        break;
                case 33:
                        break;
                default:
                        printk(KERN_ERR "%s(): unrecognized Vpp %u\n", __FUNCTION__,
                               configure->vpp);
                        restore_flags(flags);
                        return -1;
                }

                if (0) printk("     BackpaqSysctlPCMCIAPower=%x mask=%x val=%x vcc=%d\n", 
                              BackpaqSysctlPCMCIAPower, mask, val, configure->vcc);
                //curval = curval & ~(short) mask;
                if (val & ((BACKPAQ_PCMCIA_A5VEN << 4) | BACKPAQ_PCMCIA_A5VEN)) {
                        val |= BACKPAQ_PWRCTL_50VEN;
                        if (!(curval & BACKPAQ_PWRCTL_50VEN)) {
                                /* if 5V was not enables, turn it on and wait 100ms */
                                printk(__FUNCTION__ ": enabling 5V and sleeping 100ms\n");
                                BackpaqSysctlPCMCIAPower = (curval|BACKPAQ_PWRCTL_50VEN);
				msleep(100);				
                        }
                }

                BackpaqSysctlPCMCIAPower = curval | val;

                if (!(mercury_backpaq_socket[sock]->versionid & VERSIONID_RESET_CAP)) {
                        printk(__FUNCTION__ ": PCMCIA_SLICE needs to be updated to support per-socket reset, using old method: versionid=%#x\n", 
                               mercury_backpaq_socket[sock]->versionid);
                        if (configure->reset)
                                set_bitsy_egpio(EGPIO_BITSY_CARD_RESET);
                        else
                                clr_bitsy_egpio(EGPIO_BITSY_CARD_RESET);

                } else {
                        if (configure->reset)
                                mercury_backpaq_socket[sock]->reset_assert;
                        else
                                mercury_backpaq_socket[sock]->reset_deassert;
                }
                restore_flags(flags);
#endif /* CONFIG_MERCURY_BACKPAQ */
        } break;

        case SLEEVE_NO_DEVICE: 
                printk("    no pcmcia sleeve\n");
		break;
	case EXTENDED_BATTERY_SLEEVE:
	case GPRS_EXPANSION_PACK:
	case BLUETOOTH_EXPANSION_PACK:
		printk("    no pcmcia in sleeve\n");
		break;
        }

        /* silently ignore vpp and speaker enables. */

        return 0;
}

struct pcmcia_low_level bitsy_pcmcia_ops = { 
        bitsy_pcmcia_init,
        bitsy_pcmcia_shutdown,
        bitsy_pcmcia_socket_state,
        bitsy_pcmcia_get_irq_info,
        bitsy_pcmcia_configure_socket
};

void sa1100_bitsy_change_sleeves(enum sleeve_device new_sleeve_type) 
{
//	printk("****" __FUNCTION__ " old=%x new=%x\n", sleeve_type, new_sleeve_type);
        if (sleeve_type == new_sleeve_type)
                return;
        if (sleeve_type != SLEEVE_NO_DEVICE)
                bitsy_pcmcia_shutdown();
        sleeve_type = new_sleeve_type;
        if (sa1100_bitsy_pcmcia_init.handler != NULL) {
                bitsy_pcmcia_init(&sa1100_bitsy_pcmcia_init);
        }
	printk("****" __FUNCTION__ " changed\n");
}

int h3600_current_sleeve( void )
{
	return sleeve_type;
}

EXPORT_SYMBOL(sa1100_bitsy_change_sleeves);
EXPORT_SYMBOL(h3600_current_sleeve);



/* 
 * support for hot plugging of CF, PCMCIA and Dual PCMCIA Sleeves 
 */
static int __devinit cf_probe_sleeve(struct sleeve_dev *sleeve_dev, const struct sleeve_device_id *ent)
{
        sprintf(sleeve_dev->name, "CompactFlash Sleeve");
        printk(__FUNCTION__ ": dev->name=%s\n", sleeve_dev->name);
        sa1100_bitsy_change_sleeves(SINGLE_COMPACTFLASH_SLEEVE);
        return 0;
}

static void __devexit cf_remove_sleeve(struct sleeve_dev *sleeve_dev)
{
        printk(__FUNCTION__ ": dev->name=%s\n", sleeve_dev->driver->name);
        sa1100_bitsy_change_sleeves(SLEEVE_NO_DEVICE);
}

static struct sleeve_device_id cf_tbl[] __devinitdata = {
        { COMPAQ_VENDOR_ID, SINGLE_COMPACTFLASH_SLEEVE, 0, 0, 0 },
        { 0, }
};

static struct sleeve_driver cf_driver = {
        name:   "Compaq Compact Flash Sleeve",
        id_table: cf_tbl,
        probe: cf_probe_sleeve,
        remove: cf_remove_sleeve,
};

struct mtd_partition pcmcia_sleeve_flash_partitions[] = {
	{
		name: "PCMCIA Sleeve Flash",
		offset: 0x00000000,
		size: 0  /* will expand to the end of the flash */
	}
};

#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))

#define WINDOW_ADDR 0xf1000000 /* static memory bank 1 */

static int offset_multiplier = 1; /* 2 for rev-A backpaqs -- don't ask -- Jamey 2/12/2001 */

static __u8 sa1100_read8(struct map_info *map, unsigned long ofs)
{
	return *(__u8 *)(WINDOW_ADDR + ofs*offset_multiplier);
}

static __u16 sa1100_read16(struct map_info *map, unsigned long ofs)
{
        __u16 d = *(__u16 *)(WINDOW_ADDR + ofs*offset_multiplier);
        if (debug >= 1) printk(__FUNCTION__ " ofs=%08lx (adr=%p) => d=%#x\n", ofs, (__u16 *)(WINDOW_ADDR + ofs*offset_multiplier), d);
	return d;
}

static __u32 sa1100_read32(struct map_info *map, unsigned long ofs)
{
	return *(__u32 *)(WINDOW_ADDR + ofs*offset_multiplier);
}

static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
        int i;
        if (0) printk(__FUNCTION__ " from=%08lx offset_multiplier=%d\n", WINDOW_ADDR + from, offset_multiplier);
        if (offset_multiplier == 1) {
                memcpy(to, (void *)(WINDOW_ADDR + from), len);
        } else {
                for (i = 0; i < len; i += 2) {
                        *(short *)(to++) = *(short *)(WINDOW_ADDR + from*offset_multiplier + i*offset_multiplier);
                }
        }
}

static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr)
{
	*(__u8 *)(WINDOW_ADDR + adr*offset_multiplier) = d;
}

static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr)
{
        if (debug >= 1) printk(__FUNCTION__ " adr=%08lx d=%x\n", WINDOW_ADDR + adr*offset_multiplier, d);
	*(__u16 *)(WINDOW_ADDR + adr*offset_multiplier) = d;
}

static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr)
{
	*(__u32 *)(WINDOW_ADDR + adr*offset_multiplier) = d;
}

static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
        int i;
        if (0) printk(__FUNCTION__ " to=%08lx offset_multiplier=%d\n", WINDOW_ADDR + to, offset_multiplier);
        if (offset_multiplier == 1) {
                memcpy((void *)(WINDOW_ADDR + to), from, len);
        } else {
                for (i = 0; i < len; i += 2) {
                        *(short *)(WINDOW_ADDR + to*offset_multiplier + i*offset_multiplier) = *(short *)(from + i);
                }
        }
}

static void pcmcia_set_vpp(struct map_info *map, int vpp)
{
	if (vpp)
           set_bitsy_egpio(EGPIO_BITSY_OPT_RESET);
	else
           clr_bitsy_egpio(EGPIO_BITSY_OPT_RESET);
}

static struct map_info pcmcia_sleeve_flash_map = {
	name:		"PCMCIA Sleeve Flash ",
	read8:		sa1100_read8,
	read16:		sa1100_read16,
	read32:		sa1100_read32,
	copy_from:	sa1100_copy_from,
	write8:		sa1100_write8,
	write16:	sa1100_write16,
	write32:	sa1100_write32,
	copy_to:	sa1100_copy_to,
        size:           0x02000000,
        set_vpp:        NULL,
        buswidth:       2
};

static struct mtd_info *pcmcia_sleeve_mtd = NULL;

int pcmcia_sleeve_attach_flash(void (*set_vpp)(struct map_info *, int), unsigned long map_size)
{
	unsigned long msc1_sm1_config = (MSC_NonBrst | MSC_16BitStMem 
					 | MSC_RdAcc((150 + 100)/4)
					 | MSC_NxtRdAcc((150 + 100)/4)
					 | MSC_Rec(100/4));
        void *(*cfi_probe)(void *) = inter_module_get("cfi_probe");
	if ( !cfi_probe )
		return -ENXIO;

	msc1_sm1_config = 0xFFFC;
	MSC1 = ((MSC1 & 0xFFFF) /* sm0 */
		| (msc1_sm1_config << 16));

	printk(__FUNCTION__ ": setting MSC1=0x%x\n", MSC1);

	pcmcia_sleeve_flash_map.set_vpp = set_vpp;
	pcmcia_sleeve_flash_map.size    = map_size;

	pcmcia_sleeve_mtd = cfi_probe(&pcmcia_sleeve_flash_map);
	if ( pcmcia_sleeve_mtd ) {
		pcmcia_sleeve_mtd->module = THIS_MODULE;
		add_mtd_device(pcmcia_sleeve_mtd);
		return 0;
	}

	printk(" *** " __FUNCTION__ ": unable to add flash device\n");
	return -ENXIO;
}

void pcmcia_sleeve_detach_flash(void)
{
	printk(" ### " __FUNCTION__ "\n");
        if (pcmcia_sleeve_mtd != NULL) {
		printk(" ### " __FUNCTION__ " 2\n");
                del_mtd_device(pcmcia_sleeve_mtd);
		printk(" #### " __FUNCTION__ " 3\n");
                map_destroy(pcmcia_sleeve_mtd);
		printk(" ### " __FUNCTION__ " 4\n"); 
		pcmcia_sleeve_mtd = NULL;
        }
}


int old_pcmcia_sleeve_attach_flash(void (*set_vpp)(struct map_info *, int), unsigned long map_size)
{
        void *(*cfi_probe)(void *) = inter_module_get("cfi_probe");

	printk(" ### " __FUNCTION__ "\n");

        if (cfi_probe != NULL) {
                /* 
                 * assuming 150ns Flash, 30ns extra to cross the expansion ASIC and control logic 
                 * and 206 MHz CPU clock (4ns ns cycle time, up to 250MHz)
                 */
                unsigned long msc1_sm1_config = (MSC_NonBrst | MSC_16BitStMem 
                                                 | MSC_RdAcc((150 + 100)/4)
                                                 | MSC_NxtRdAcc((150 + 100)/4)
                                                 | MSC_Rec(100/4));
                msc1_sm1_config = 0xFFFC;
                MSC1 = ((MSC1 & 0xFFFF) /* sm0 */
                        | (msc1_sm1_config << 16));

                printk(__FUNCTION__ ": setting MSC1=0x%x\n", MSC1);

                pcmcia_sleeve_flash_map.set_vpp = set_vpp;
                pcmcia_sleeve_flash_map.size = map_size;

                pcmcia_sleeve_mtd = cfi_probe(&pcmcia_sleeve_flash_map);
                if (pcmcia_sleeve_mtd) {
                        pcmcia_sleeve_mtd->module = THIS_MODULE;
                        if ( add_mtd_partitions(pcmcia_sleeve_mtd, pcmcia_sleeve_flash_partitions, 
						NB_OF(pcmcia_sleeve_flash_partitions)))
				printk(" *** " __FUNCTION__ ": unable to add flash partitions\n");
                        printk(KERN_NOTICE "PCMCIA flash access initialized\n");
                        return 0;
                }
                return -ENXIO;
        } else {
                return -EINVAL;
        }

}

void old_pcmcia_sleeve_detach_flash(void)
{
	printk(" ### " __FUNCTION__ "\n");
        if (pcmcia_sleeve_mtd != NULL) {
		printk(" ### " __FUNCTION__ " 2\n");
                del_mtd_partitions(pcmcia_sleeve_mtd);
		printk(" #### " __FUNCTION__ " 3\n");
                map_destroy(pcmcia_sleeve_mtd);
		printk(" ### " __FUNCTION__ " 4\n"); 
        }
}
EXPORT_SYMBOL(pcmcia_sleeve_attach_flash);
EXPORT_SYMBOL(pcmcia_sleeve_detach_flash);

static int __devinit pcmcia_probe_sleeve(struct sleeve_dev *sleeve_dev, const struct sleeve_device_id *ent)
{
        sprintf(sleeve_dev->name, "PC Card Sleeve");
        printk(__FUNCTION__ ": dev->name=%s\n", sleeve_dev->name);
        sa1100_bitsy_change_sleeves(SINGLE_PCMCIA_SLEEVE);
        pcmcia_sleeve_attach_flash(pcmcia_set_vpp, 0x02000000);
        return 0;
}

static void __devexit pcmcia_remove_sleeve(struct sleeve_dev *sleeve_dev)
{
        printk(__FUNCTION__ ": dev->name=%s\n", sleeve_dev->name);
        pcmcia_sleeve_detach_flash();
        sa1100_bitsy_change_sleeves(SLEEVE_NO_DEVICE);
}

static struct sleeve_device_id pcmcia_tbl[] __devinitdata = {
        { COMPAQ_VENDOR_ID, SINGLE_PCMCIA_SLEEVE, 0, 0, 0 },
        { 0, }
};

static struct sleeve_driver pcmcia_driver = {
        name:   "Compaq PC Card Sleeve",
        id_table: pcmcia_tbl,
        probe: pcmcia_probe_sleeve,
        remove: pcmcia_remove_sleeve,
};



static int __devinit dual_pcmcia_probe_sleeve(struct sleeve_dev *sleeve_dev, const struct sleeve_device_id *ent)
{
        sprintf(sleeve_dev->name, "Dual PC Card Sleeve");
        printk(__FUNCTION__ ": dev->name=%s\n", sleeve_dev->name);
#if 1
        sa1100_bitsy_change_sleeves(DUAL_PCMCIA_SLEEVE);
#endif
        return 0;
}

static void __devexit dual_pcmcia_remove_sleeve(struct sleeve_dev *sleeve_dev)
{
        printk(__FUNCTION__ ": dev->name=%s\n", sleeve_dev->name);
#if 1
        sa1100_bitsy_change_sleeves(SLEEVE_NO_DEVICE);
#endif
}

static struct sleeve_device_id dual_pcmcia_tbl[] __devinitdata = {
        { COMPAQ_VENDOR_ID, DUAL_PCMCIA_SLEEVE, 0, 0, 0 },
        { 0, }
};

static struct sleeve_driver dual_pcmcia_driver = {
        name:   "Compaq Dual PC Card Sleeve",
        id_table: dual_pcmcia_tbl,
        probe: dual_pcmcia_probe_sleeve,
        remove: dual_pcmcia_remove_sleeve,
};

void bitsy_pcmcia_sleeve_register(void)
{
#if defined(CONFIG_H3600_SLEEVE) || defined(CONFIG_H3600_SLEEVE_MODULE)
        h3600_sleeve_register_driver(&cf_driver);
        h3600_sleeve_register_driver(&pcmcia_driver);
        h3600_sleeve_register_driver(&dual_pcmcia_driver);
#endif
}

void bitsy_pcmcia_sleeve_unregister(void)
{
#if defined(CONFIG_H3600_SLEEVE) || defined(CONFIG_H3600_SLEEVE_MODULE)
        h3600_sleeve_unregister_driver(&cf_driver);
        h3600_sleeve_unregister_driver(&pcmcia_driver);
        h3600_sleeve_unregister_driver(&dual_pcmcia_driver);
#endif
} 
