/*
 * ide-cb.c:
 *
 * Copyright (c) 2002 James McKenzie <james@fishsoup.dhs.org>,
 * All rights reserved.
 *
 */

static char rcsid[] = "$Id: ide-cb.c,v 1.2 2002/06/25 17:56:41 root Exp $";

/*
 * $Log: ide-cb.c,v $
 * Revision 1.2  2002/06/25 17:56:41  root
 * *** empty log message ***
 *
 * Revision 1.1  2002/06/25 16:33:24  root
 * Initial revision
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <asm/io.h>

#include <linux/pci.h>
#include <linux/ide.h>
#include <linux/hdreg.h>


#ifdef DEBUG
#define enter()   printk("Enter: %s, %s line %i\n",__FUNCTION__,__FILE__,__LINE__)
#define leave()   printk("Leave: %s, %s line %i\n",__FUNCTION__,__FILE__,__LINE__)
#else
#define enter()   do {} while (0)
#define leave()   do {} while (0)
#endif


MODULE_DESCRIPTION("Cardbus ATA driver");
MODULE_AUTHOR("James McKenzie <james@fishsoup.dhs.org>");
MODULE_LICENSE("GPL");


/* Function prototypes */
static int atacb_probe(struct pci_dev *pdev, const struct pci_device_id *id);
static void atacb_remove(struct pci_dev *pdev);


static struct pci_device_id atacb_pci_table[] __devinitdata = {
	{0x14e6, 0x0001, PCI_ANY_ID, PCI_ANY_ID,},
	{0,},
};
MODULE_DEVICE_TABLE(pci, atacb_pci_table);

static struct pci_driver atacb_ops = {
	name:		"atacb_cb", 
	id_table:	atacb_pci_table, 
	probe:		atacb_probe, 
	remove:		__devexit_p(atacb_remove), 
};

struct atacb_private {
	int hd;
};

/* atacb_probe is the code that gets called on device insertion.
   it sets up the hardware and registers the device to the networklayer.
 */
static int __devinit atacb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
	struct atacb_private *priv;
 	int hd;
	int ret,foo;
	int ctl_base,io_base;
	int tries=4;
        hw_regs_t hw;
	ide_hwif_t *hwif;
	int chipset=ide_pci;

	enter();
	
	ret = pci_enable_device(pdev);
	if (ret)
		return ret;

	foo=pdev->resource[0].start&PCI_BASE_ADDRESS_IO_MASK;

	ctl_base  = foo+0x0e;
	io_base = foo;

 	outb(0x02,ctl_base);

        __set_current_state(TASK_UNINTERRUPTIBLE);
        schedule_timeout(HZ);

        ide_init_hwif_ports(&hw, (ide_ioreg_t) io_base, (ide_ioreg_t) ctl_base, NULL);
	hw.irq=pdev->irq;
	
	do {
		hd=ide_register_hw_with_chipset(&hw,&hwif,&chipset);
		if (hd<0) {
       			__set_current_state(TASK_UNINTERRUPTIBLE);
			schedule_timeout(HZ);
		}
	} while ((tries--)  && (hd<0)) ;

	if (hd<0) return -1;

	hwif->drives[0].io_32bit = 1;
	hwif->drives[1].io_32bit = 1;

	priv=kmalloc(sizeof(struct atacb_private),GFP_KERNEL);
	priv->hd=hd;
	pci_set_drvdata(pdev,priv);


	MOD_INC_USE_COUNT;


	return 0;
}
/*
 atacb_remove is called on module-unload or on device-eject.
 */
static void __devexit atacb_remove(struct pci_dev *pdev)
{
	struct atacb_private *priv=pdev->driver_data;
	enter();

	if (priv->hd>=0) {
	  ide_unregister(priv->hd);
	  MOD_DEC_USE_COUNT;
	}
	
	pci_set_drvdata(pdev,NULL);
	leave();
} 

static int __init atacb_init(void)
{
        pci_register_driver(&atacb_ops);
        return 0;
}

static void __exit atacb_exit(void)
{
        pci_unregister_driver(&atacb_ops);
}

module_init(atacb_init)
module_exit(atacb_exit)

