/*
 *                     OpenBIOS - free your system! 
 *              ( firmware/flash device driver for Linux )
 *                          
 *  bios_core.c - core skeleton 
 *  
 *  This program is part of a free implementation of the IEEE 1275-1994 
 *  Standard for Boot (Initialization Configuration) Firmware.
 *
 *  Copyright (C) 1998-2004  Stefan Reinauer
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
 *
 */

#include <linux/config.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#ifdef MODULE
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#endif
#include <linux/module.h>
#endif
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/vmalloc.h>
#include <linux/init.h>

#include <asm/io.h>

#include "bios.h"
#include "pcisets.h"
#include "flashchips.h"
#include "programming.h"

extern struct file_operations bios_fops;
int bios_proc_register(void);
int bios_proc_unregister(void);

int write = 0;

spinlock_t bios_lock = SPIN_LOCK_UNLOCKED;

/*
 * ******************************************
 *
 *      Cleanup
 *
 * ******************************************
 */

static void free_iomaps(void)
{
	unsigned long lastmapped=0;
	unsigned int i;

	/* We remember the last mapped area to be sure that we only iounmap 
	 * every mapped area once. If two flash devices are in the same 
	 * area but do not occur sequentially during probing you have a
	 * seriously strange hardware
	 */
	for (i=0; i<flashcount; i++) {
		if (lastmapped==flashdevices[i].mapped)
			continue;
		iounmap((void *)flashdevices[i].mapped);
		lastmapped=flashdevices[i].mapped;
	}
}

/*
 * ******************************************
 *
 *	Initialization
 *
 * ****************************************** 
 */

void probe_system(void)
{
#ifdef __alpha__
	probe_alphafw();
#endif
	/* This function checks all flash media attached to
	 * PCI devices in the system. This means NON-PCI systems
	 * don't work. This causes machine checks on my LX164 test 
	 * machine, so leave it away until it's fixed. This is
	 * needed for Ruffians, so we check the machine type
	 * in probe_alphafw() and call probe_pcibus from there.
	 * This could use some cleanup
	 */
#ifndef __alpha__
	probe_pcibus();
#endif
}

static __init int bios_init(void)
{
	printk(KERN_INFO "BIOS driver v" BIOS_VERSION " (writing %s) for "
			UTS_RELEASE "\n", write?"enabled":"disabled");

#if !defined(UTC_BIOS) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	if (!pci_present()) {
		printk(KERN_WARNING "BIOS: No PCI system.");
		return -EBUSY;
	}
#endif

	/* Probe for flash devices */
	probe_system();

	if (flashcount==0) {
		printk(KERN_WARNING "BIOS: No flash devices found.\n");
		return -EBUSY;
	}
	
	if (register_chrdev(BIOS_MAJOR, "bios",  &bios_fops) == -EBUSY) {
		printk(KERN_WARNING "BIOS: Could not register bios device.\n");
		free_iomaps();
		return -EBUSY;
	}

#ifdef CONFIG_PROC_FS
	bios_proc_register();
#endif
	return 0;
}

/*
 * ******************************************
 *
 *	module handling
 *
 * ****************************************** 
 */

#ifdef MODULE
MODULE_PARM(write,"i");
MODULE_AUTHOR("Stefan Reinauer <stefan.reinauer@coreboot.org>");
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,10)
MODULE_LICENSE("GPL");
#endif

static __exit void cleanup_bios_module (void)
{
#ifdef CONFIG_PROC_FS
	bios_proc_unregister();
#endif
	free_iomaps();
	
	unregister_chrdev(BIOS_MAJOR, "bios");
	printk(KERN_INFO "BIOS driver removed.\n");
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
int init_module(void)
{
	return bios_init();
}

void cleanup_module(void)
{
	cleanup_bios_module();
}
#endif

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,74)
module_init(bios_init);
module_exit(cleanup_bios_module);
#endif

void inc_mod(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	MOD_INC_USE_COUNT; 
#endif
}

void dec_mod(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	MOD_DEC_USE_COUNT; 
#endif
}

#endif
