/* $Id: init.c,v 1.2 2005/06/16 13:46:21 cgoh Exp $
 *
 * 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.
 *
 * Copyright (C) Equator Technologies
 */
#include <linux/config.h>
#include <linux/init.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/pagemap.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/swapctl.h>
#ifdef CONFIG_BLK_DEV_INITRD
#include <linux/blk.h>
#endif


#include <eti/mm.h>
#include <asm/init.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/dma.h>

#include <asm/debug.h>


/*****************************************************************************/



static int nrof_pts;


pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset)
{
	pte_t *page;

#ifdef PAGE_TABLE_DEBUG
	printk("get_pte_slow(pmd=0x%08x, offset=0x%x): pmd_val=0x%08x\n",
	       (u32)pmd, offset, pmd_val(*pmd));
#endif
	
	if (pmd_none(*pmd)) {
	        page = (pte_t *) __get_free_page(GFP_KERNEL);

		if (page) {
		    int i;
		    
		    clear_page ((unsigned long)page);

        	    for (i=0; i<NROF_PTS_PER_PAGE; i++) {

                        free_pte_fast (page);
			page += PTRS_PER_PTE;
			nrof_pts++;
		    }
		
		    return pte_alloc(pmd,offset<<PAGE_SHIFT);
		    
		} else {
		    pmd_val(*pmd) = BAD_PAGETABLE;
		    return NULL;
		}
	} else 
	
	if (pmd_bad(*pmd)) {
		__bad_pte(pmd);
		return NULL;
	} else {
	
	        return (pte_t *) pmd_page(*pmd) + offset;
        }
}


pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset)
{
	return get_pte_slow(pmd,offset);
}


int do_check_pgt_cache(int low, int high)
{
/*
	printk("do_check_pgt_cache(low=%d, high=%d, allocd=%d, free=%d)\n",
	       low, high, nrof_pts, pgtable_cache_size);
*/
	return 0;
}


/*****************************************************************************/


void __bad_pte_kernel(pmd_t *pmd)
{
	printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
	pmd_val(*pmd) = BAD_PAGETABLE;
}

void __bad_pte(pmd_t *pmd)
{
	printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
	pmd_val(*pmd) = BAD_PAGETABLE;
}


pte_t * __bad_pagetable(void)
{
	extern char empty_bad_page_table[PAGE_SIZE];
	unsigned long page;

	page = (unsigned long) empty_bad_page_table;
	return (pte_t *)page;
}

pte_t __bad_page(void)
{
	extern char empty_bad_page[PAGE_SIZE];
	unsigned long page = (unsigned long)empty_bad_page;

	clear_page(page);
	return pte_mkdirty(mk_pte(page, PAGE_SHARED));
}

void show_mem(void)
{
	ETISTUB("show_mem()");
}


/*****************************************************************************/


extern unsigned long _start2, _end, _edata, _etext, __bss_start;
extern char __init_begin, __init_end;

extern unsigned long free_area_init(unsigned long, unsigned long);

/*
 * paging_init() sets up the page tables
 */
__initfunc(unsigned long paging_init(unsigned long start_mem, unsigned long end_mem))
{
	unsigned long *p = (unsigned long *)swapper_pg_dir;

        u32 index       = VMALLOC_START >> PGDIR_SHIFT;
        u32 index_limit = VMALLOC_END   >> PGDIR_SHIFT;

	/* Initialize the entire pgd.  */
	pgd_init((unsigned long)swapper_pg_dir);

	/* create page directory entries for vmalloc region */
        while (index < index_limit) {
            unsigned long ptsize;

            ptsize= PTRS_PER_PTE * sizeof(pte_t);
	    memset((void*)start_mem, 0, ptsize);
	    p[index++] = start_mem;

	    start_mem += ptsize;
        }

	return free_area_init(start_mem, end_mem);
}

unsigned long empty_zero_page;

__initfunc(void mem_init(unsigned long start_mem, unsigned long end_mem))
{
	int reservedpages = 0;
	int codepages = 0;
	int datapages = 0;
	unsigned long tmp;

	end_mem &= PAGE_MASK;
	max_mapnr = MAP_NR(end_mem);
	high_memory = (void *)end_mem;
	num_physpages = 0;

	/* mark usable pages in the mem_map[] */
	start_mem = PAGE_ALIGN(start_mem);

        empty_zero_page= start_mem;
        start_mem += PAGE_SIZE;
	clear_page(empty_zero_page);
        
	for( tmp = MAP_NR(start_mem); tmp < max_mapnr; tmp++ )
		clear_bit(PG_reserved, &mem_map[tmp].flags);

	for ( tmp = PAGE_OFFSET; tmp < end_mem; tmp += PAGE_SIZE )
       	{
#ifdef CONFIG_BLK_DEV_INITRD
	       /* Mark staticly linked ramdisk image as 'allocated'
                * so that it can be freed after loading
                */
	        int inramdisk= (tmp >= initrd_start && tmp < initrd_end);
#else
	        int inramdisk= 0;
#endif
                struct page *pg= mem_map+MAP_NR(tmp);
                
		if (tmp >= MAX_DMA_ADDRESS)
			clear_bit(PG_DMA, &pg->flags);
                
                if (inramdisk) {
                    pg->flags &= ~(1<<PG_reserved);
                } else
		if (PageReserved(pg)) {
			if ((tmp >= (unsigned long) &_start2) && 
			    (tmp < (unsigned long) &_edata)) {
				if (tmp < (unsigned long) &_etext) 
					codepages++;
				else
					datapages++;
			}
			else if ((tmp >= (unsigned long) &__bss_start) &&
				 (tmp < (unsigned long) start_mem))
				datapages++;
			else
				reservedpages++;
			continue;
		}
		num_physpages++;
		atomic_set(&pg->count, 1);
		if (!inramdisk) free_page(tmp);
	}

	tmp = nr_free_pages << PAGE_SHIFT;

	printk("Memory: %luk/%luk available (%dk kernel code, %dk data, %dk reserved)\n",
	       tmp >> 10,
	       max_mapnr << (PAGE_SHIFT-10),
	       codepages << (PAGE_SHIFT-10),
	       datapages << (PAGE_SHIFT-10),
	       reservedpages << (PAGE_SHIFT-10)
	       );
}

void free_initmem(void)
{
	unsigned long addr;

	addr = (unsigned long)(&__init_begin);
	for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
		mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved);
		atomic_set(&mem_map[MAP_NR(addr)].count, 1);
		free_page(addr);
	}
	printk ("Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10);
}

void si_meminfo(struct sysinfo *val)
{
	int page = MAP_NR(high_memory);

	val->totalram  = 0;
	val->sharedram = 0;
	val->freeram   = nr_free_pages << PAGE_SHIFT;
	val->bufferram = buffermem;

	while ( page-- > 0 )
      	{
		if (PageReserved(&mem_map[page]))
			continue;
		val->totalram++;
		if (!atomic_read(&mem_map[page].count))
			continue;
		val->sharedram += atomic_read(&mem_map[page].count) - 1;
	}
	val->totalram  <<= PAGE_SHIFT;
	val->sharedram <<= PAGE_SHIFT;

	return;
}
