/*
 * iPAQ h4000 PCMCIA support
 * 
 * Copyright © 2004 H4000 porting team <h4100-port@handhelds.org>
 * Based on h2200_pcmcia.c
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive for
 * more details.
 *
 * H4000 series should have 802.11 card connected to a the pxa pcmcia socket
 * 
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/soc-device.h>

#include <asm/hardware.h>
#include <asm/arch/pcmcia.h>
#include <asm/irq.h>

#include <asm/arch/ipaq.h>
#include <asm/arch/h4000-asic.h>
#include <asm/arch/h4000-gpio.h>
#include <asm/hardware/ipaq-asic3.h>

#if 0
#  define debug(s, args...) printk (KERN_INFO s, ##args)
#else
#  define debug(s, args...)
#endif
#define debug_func(s, args...) debug ("%s: " s, __FUNCTION__, ##args)

static void h4000_cf_rst (int state)
{
	debug_func ("\n");

	printk("%s: Resetting cf\n", __FUNCTION__);
//      IPAQ_ASIC3_GPIO_A_OUT(adata->mapping) &= ~GPIOA_WLAN_LED_ON;
//      IPAQ_ASIC3_GPIO_C_OUT(adata->mapping) |= GPIOC_WLAN_POWER_ON;
//	usleep(100);
//      IPAQ_ASIC3_GPIO_C_OUT(adata->mapping) |= GPIOC_WLAN_POWER_ON;
//      IPAQ_ASIC3_GPIO_A_OUT(adata->mapping) |= GPIOA_WLAN_LED_ON;

}

static int h4000_pcmcia_hw_init (struct pxa2xx_pcmcia_socket *skt)
{
//	unsigned long flags;

	debug_func ("\n");
	printk("%s: Hw init cf\n", __FUNCTION__);

	skt->irq = IRQ_GPIO (GPIO_NR_H4000_WLAN_MAC_IRQ_N);

	// This must go away as we'll fill h4000-init.h
//	local_irq_save (flags);
//	pxa_gpio_mode (GPIO_NR_H4000_CF_RESET | GPIO_OUT);
//	pxa_gpio_mode (GPIO_NR_H4000_CF_ADD_EN_N | GPIO_OUT);
//	pxa_gpio_mode (GPIO_NR_H4000_CF_POWER_EN | GPIO_OUT);
//	pxa_gpio_mode (GPIO_NR_H4000_CF_BUFF_EN | GPIO_OUT);
//	pxa_gpio_mode (GPIO_NR_H4000_CF_DETECT_N | GPIO_IN);
//      pxa_gpio_mode (GPIO_NR_H4000_CF_INT_N | GPIO_IN);
//	local_irq_restore (flags);

	return 0;
}

/*
 * Release all resources.
 */
static void h4000_pcmcia_hw_shutdown (struct pxa2xx_pcmcia_socket *skt)
{
	debug_func ("\n");

	printk("%s: Hw init cf\n", __FUNCTION__);

//      IPAQ_ASIC3_GPIO_C_OUT(adata->mapping) ~= ~GPIOC_WLAN_POWER_ON;
//      IPAQ_ASIC3_GPIO_A_OUT(adata->mapping) &= ~GPIOA_WLAN_LED_ON;

}

static void h4000_pcmcia_socket_state (struct pxa2xx_pcmcia_socket *skt, struct pcmcia_state *state)
{
//	state->detect = GET_H4000_GPIO (CF_DETECT_N) ? 0 : 1;
	state->detect = 1;
//	state->ready  = GET_H4000_GPIO (CF_INT_N) ? 1 : 0;
	state->ready  = 1;
	state->bvd1   = 1;
	state->bvd2   = 1;
	state->wrprot = 0;
//	state->vs_3v  = GET_H4000_GPIO (CF_POWER_EN) ? 1 : 0;
	state->vs_3v  = 1;
	state->vs_Xv  = 0;

	debug ("detect:%d ready:%d vcc:%d\n",
	       state->detect, state->ready, state->vs_3v);
}

static int h4000_pcmcia_configure_socket (struct pxa2xx_pcmcia_socket *skt, const socket_state_t *state)
{
	/* Silently ignore Vpp, output enable, speaker enable. */
	debug_func ("Reset:%d Vcc:%d\n", (state->flags & SS_RESET) ? 1 : 0,
		    state->Vcc);

//	h4000_cf_rst (state->flags & SS_RESET);

	/* Apply socket voltage */
	switch (state->Vcc) {
	case 0:
		printk("%s: FIXME - turn off acx100 by change some bit on asic3\n", __FUNCTION__);
//		SET_H4000_GPIO (CF_POWER_EN, 0);
//		SET_H4000_GPIO_N (CF_ADD_EN, 0);
//		SET_H4000_GPIO (CF_BUFF_EN, 0);
		break;
	case 50:
	case 33:
		printk("%s: FIXME - turn on acx100 by change some bit on asic3\n", __FUNCTION__);
		/* Apply power to socket */
//		SET_H4000_GPIO (CF_POWER_EN, 1);
//		SET_H4000_GPIO_N (CF_ADD_EN, 1);
//		SET_H4000_GPIO (CF_BUFF_EN, 1);
		break;
	default:
		printk (KERN_ERR "%s: Unsupported Vcc:%d\n",
			__FUNCTION__, state->Vcc);
	}

	return 0;
}

/*
 * Enable card status IRQs on (re-)initialisation.  This can
 * be called at initialisation, power management event, or
 * pcmcia event.
 */
static void h4000_pcmcia_socket_init(struct pxa2xx_pcmcia_socket *skt)
{
	debug_func ("\n");
	pxa2xx_core_pcmcia_interrupt;
}

/*
 * Disable card status IRQs on suspend.
 */
static void h4000_pcmcia_socket_suspend (struct pxa2xx_pcmcia_socket *skt)
{
	debug_func ("\n");
	h4000_cf_rst (1);
}

// We don't have additional irq for pcmcia
//static struct pcmcia_irqs h4000_cd_irq[] = {
//	{ 0, IRQ_GPIO (GPIO_NR_H4000_WLAN_MAC_IRQ_N),  "PCMCIA CD SKT0" },
//	{ 1, IRQ_GPIO (11),  "PCMCIA CD SKT1" }
//};

static struct pcmcia_low_level h4000_pcmcia_ops = {
	.owner          	= THIS_MODULE,

	.first          	= 1,
	.nr         		= 1,
//	.cd_irq         	= h4000_cd_irq,

	.hw_init        	= h4000_pcmcia_hw_init,
	.hw_shutdown		= h4000_pcmcia_hw_shutdown,

	.socket_state		= h4000_pcmcia_socket_state,
	.configure_socket	= h4000_pcmcia_configure_socket,

	.socket_init		= h4000_pcmcia_socket_init,
	.socket_suspend		= h4000_pcmcia_socket_suspend,
};

static void h4000_pcmcia_release (struct device * dev)
{
	/* No need to free the structure since it is a static variable */
}

static struct platform_device h4000_pcmcia_device = {
	.name			= "pxa2xx-pcmcia",
	.id			= 0,
	.dev			= {
		.platform_data	= &h4000_pcmcia_ops,
		.release	= h4000_pcmcia_release
	}
};

static int __init h4000_pcmcia_init (void)
{
	debug_func ("\n");
	return platform_add_device (&h4000_pcmcia_device);
}

static void __exit
h4000_pcmcia_exit(void)
{
	platform_remove_device (&h4000_pcmcia_device);
}

module_init(h4000_pcmcia_init);
module_exit(h4000_pcmcia_exit);

MODULE_AUTHOR("H4000 Porting Team <h4100-port@handhelds.org>");
MODULE_DESCRIPTION("iPAQ H4000 PCMCIA/CF platform-specific driver");
MODULE_LICENSE("GPL");
