/* Work around bug in some SiS chipsets. */
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/pm.h>

#ifdef CONFIG_VT
/* Note: must be initialised after main apm */
extern int (*console_blank_hook)(int);
static int (*save_console_blank_hook)(int) = NULL;
#endif

static struct pci_dev *pdev;
static struct pm_dev *pm_dev;

static void sisbug_fix(void)
{
	unsigned char keybd_ctrl;

	pci_read_config_byte(pdev, 0x47, &keybd_ctrl);

	if ((keybd_ctrl & 0x3) == 0x2) 
	{
		keybd_ctrl &= ~0x2;
		pci_write_config_byte(pdev,0x47,keybd_ctrl);
	}	
}

static int sisbug_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
{
	sisbug_fix();
	return 0;
}

static int sis_console_blank_wrap(int val)
{
	sisbug_fix();
	return save_console_blank_hook(val);
}

int __init sisbug_init(void)
{
	/*
	 * here we can add a list off all SiS chipsets affected
         */
	pdev = pci_find_device(0x1039,0x0008,NULL);
	if (pdev) 
	{	
		pm_dev = pm_register(PM_PCI_DEV, 
			PM_PCI_ID(pdev) , sisbug_callback);
		if (!pm_dev)
			return -ENOMEM;

#ifdef CONFIG_VT
		/*
		 * We need to wrap the console_blank_hook()
		 * this needs to be done after apm is initialized.
		 */
		if (console_blank_hook) { 
			save_console_blank_hook = console_blank_hook;
			console_blank_hook = sis_console_blank_wrap;
		} 
#endif

		printk(KERN_INFO "sisbugfix registered successfully\n");
		return 0;
	}
	return -ENODEV;
}

static void __exit sisbug_cleanup (void)
{
	pm_unregister(pm_dev);
	/*
	 * We will be in trouble if another module wraps
	 * this after we do: When we unload the driver
	 * we kick that module out of the call chain.
	 * If anyone knows a better way to do this please
	 * tell me.
	 */
#ifdef CONFIG_VT
	console_blank_hook = save_console_blank_hook;
#endif
}

module_init(sisbug_init);
module_exit(sisbug_cleanup);

MODULE_LICENSE("GPL");
