/*
** z2ram - Amiga pseudo-driver to access 16bit-RAM in ZorroII space
**         as a block device, to be used as a RAM disk or swap space
** 
** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
**
** ++Geert: support for zorro_unused_z2ram, better range checking
** ++roman: translate accesses via an array
** ++Milan: support for ChipRAM usage
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/fd.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/major.h>

#include <asm/bitops.h>
#include <asm/bootinfo.h>
#include <asm/amigahw.h>
#include <asm/zorro.h>


#define MAJOR_NR    Z2RAM_MAJOR
#include "blk.h"


static u_long *z2ram_map;
static u_long z2ram_size = 0;


static void
do_z2_request(void)
{
    u_long start, len, addr, size;

    INIT_REQUEST;

    start = CURRENT->sector << 9;
    len  = CURRENT->current_nr_sectors << 9;

    if (start+len > z2ram_size) {
        printk("z2ram: bad access: block=%ld, count=%ld\n", CURRENT->sector,
               CURRENT->current_nr_sectors);
        end_request(0);
        return;
    }

    if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) {
        printk("z2ram: bad command: %d\n", CURRENT->cmd);
        end_request(0);
        return;
    }
    while (len) {
        addr = start & Z2RAM_CHUNKMASK;
        size = Z2RAM_CHUNKSIZE-addr;
        if (len < size)
           size = len;
        addr += z2ram_map[start>>Z2RAM_CHUNKSHIFT];

        if (CURRENT->cmd == READ)
            memcpy(CURRENT->buffer, (char *)addr, size);
        else
            memcpy((char *)addr, CURRENT->buffer, size);
		  start += size;
		  len -= size;
    }

    end_request(1);
}


static int
z2_open(struct inode *inode, struct file *filp)
{
    int device;

    device = MINOR(inode->i_rdev);
    if (device)
        return -EINVAL;
    return 0;
}


static void 
z2_release(struct inode *inode, struct file *filp)
{
    sync_dev(inode->i_rdev);
}


static struct file_operations z2_fops = {
        NULL,                   /* lseek - default */
        block_read,             /* read - general block-dev read */
        block_write,            /* write - general block-dev write */
        NULL,                   /* readdir - bad */
        NULL,                   /* select */
        NULL,                   /* ioctl */
        NULL,                   /* mmap */
        z2_open,                /* open */
        z2_release,             /* release */
        block_fsync             /* fsync */
};

static int z2_blocksizes[1] = { 1024 };
static int z2_sizes[1] = { 0 };

unsigned long
z2_init(unsigned long mem_start, unsigned long mem_end)
{
    int i,dummy;

	if (!MACH_IS_AMIGA)
		return mem_start;
   
   z2ram_size = 0;
   printk("z2ram: ");
    z2ram_map = (unsigned long *)mem_start;
    for (i = 0; i < Z2RAM_SIZE/Z2RAM_CHUNKSIZE; i++) {
        if (test_bit( i, zorro_unused_z2ram )) {
            z2ram_map[z2ram_size++] =
                ZTWO_VADDR(Z2RAM_START) + (i << Z2RAM_CHUNKSHIFT);
        }
    }
   printk("using %ldK Zorro II RAM and ",(z2ram_size*Z2RAM_CHUNKSIZE) >> 10 );
   /*MILAN*/
   if( (dummy = (amiga_chip_avail()/Z2RAM_CHUNKSIZE) -4) > 0)
	 {	
		for(i=0; i<dummy; i++)
		  {
			 z2ram_map[z2ram_size++]= (u_long) amiga_chip_alloc( Z2RAM_CHUNKSIZE );
		  }/*MILAN END*/
	 }
   
    printk("%iK CHIPRAM ",(dummy*Z2RAM_CHUNKSIZE) >> 10 );
    if (!z2ram_size) {
        printk("no unused Zorro II RAM found\n");
        return mem_start;
    }
    mem_start = (unsigned long)(z2ram_map + z2ram_size);
    z2ram_size <<= Z2RAM_CHUNKSHIFT;
    memset( zorro_unused_z2ram, 0, sizeof(zorro_unused_z2ram) );

    if( register_blkdev(MAJOR_NR, "z2ram", &z2_fops) ) {
        printk("Unable to get major %d for Z2 RAM\n", MAJOR_NR);
        return mem_start;
    }
    blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;  /* do_z2_request(), defined in blk.h */
    blksize_size[MAJOR_NR] = z2_blocksizes;
    z2_sizes[0] = z2ram_size >> 10;
    blk_size[MAJOR_NR] = z2_sizes;

    printk("(%ldK) for ramdisk\n", z2ram_size>>10);
    return mem_start;
}
