OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [block/] [blkmem.c] - Rev 1777

Go to most recent revision | Compare with Previous | Blame | View Log

/* blkmem.c: Block access to memory spaces
 *
 * Copyright (C) 1997, 1998  D. Jeff Dionne <jeff@pfnet.com>,
 *                           Kenneth Albanowski <kjahds@kjahds.com>,
 *                           The Silver Hammer Group, Ltd.
 *
 * Based z2ram - Amiga pseudo-driver to access 16bit-RAM in ZorroII space
 * Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
 *
 * 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/major.h>
#include <linux/malloc.h>
 
#define MAJOR_NR    BLKMEM_MAJOR
 
#define VERBOSE
#define DEBUG
 
#include <linux/blk.h>
 
#include <linux/blkmem.h>
 
#if 0
#include <linux/init.h>
#endif
 
#if defined(MODULE)
#include <linux/module.h>
#endif
 
#include <asm/bitops.h>
#include <asm/delay.h>
/*
#include <asm/shglports.h>
*/
#define TRUE                  (1)
#define FALSE                 (0)
 
/*
 * Please, configure the ROMFS for your system here
 */
#if defined( CONFIG_PILOT ) && defined( CONFIG_M68328 )
#define CAT_ROMARRAY
#endif
 
#if defined( CONFIG_PILOT ) && defined( CONFIG_M68EZ328 )
extern char _flashstart[];
#define FIXED_ROMARRAY _flashstart
#endif
 
#ifdef CONFIG_UCSIMM
#define CAT_ROMARRAY
#endif
 
#ifdef CONFIG_M68EZ328ADS
#ifdef CONFIG_M68EZ328ADS_RAM
extern char _flashstart[];
#define FIXED_ROMARRAY _flashstart
#else
#define CAT_ROMARRAY
#endif 
#endif 
 
#ifdef CONFIG_ALMA_ANS
#ifdef CONFIG_ALMA_ANS_RAM
extern char _flashstart[];
#define FIXED_ROMARRAY _flashstart
#else
#define CAT_ROMARRAY
#endif 
#endif 
 
 
 
 
 
 
 
/******* END OF BOARD-SPECIFIC CONFIGURATION ************/
 
/* Simple romfs, at internal, cat on the end of kernel, or seperate fixed adderess romfs. */
 
#ifdef INTERNAL_ROMARRAY
#include "testromfs.c"
#endif
 
#ifdef CAT_ROMARRAY
unsigned char *romarray;
extern char __data_rom_start[];
extern char _edata[];
extern char __data_start[];
#endif
 
#ifdef FIXED_ROMARRAY
unsigned char *romarray = (char *)(FIXED_ROMARRAY);
#endif
 
/* If defined, ROOT_ARENA causes the root device to be the specified arena, useful with romfs */
 
/* Now defining ROOT_DEV in arch/setup.c */
/*#define ROOT_ARENA 0*/
 
struct arena_t;
 
typedef void (*xfer_func_t)(struct arena_t *, unsigned long address, unsigned long length, char * buffer);
typedef void (*erase_func_t)(struct arena_t *, unsigned long address);
typedef void (*program_func_t)(struct arena_t *, struct blkmem_program_t * prog);
 
void program_main(struct arena_t *, struct blkmem_program_t *);
void read_spare(struct arena_t *, unsigned long, unsigned long, char *);
void write_spare(struct arena_t *, unsigned long, unsigned long, char *);
void erase_spare(struct arena_t *, unsigned long);
 
 
/* This array of structures defines the actual set of memory arenas, including
   access functions (if the memory isn't part of the main address space) */
 
struct arena_t {
	int rw;
 
	unsigned long address; /* Address of memory arena */
	unsigned long length;  /* Length of memory arena. If -1, try to get size from romfs header */
 
	program_func_t program_func; /* Function to program in one go */
 
	xfer_func_t read_func; /* Function to transfer data to main memory, or zero if none needed */
	xfer_func_t write_func; /* Function to transfer data from main memory, zero if none needed */
 
	erase_func_t erase_func; /* Function to erase a block of memory to zeros, or 0 if N/A */
	unsigned long blksize; /* Size of block that can be erased at one time, or 0 if N/A */
	unsigned long unitsize;
	unsigned char erasevalue; /* Contents of sectors when erased */
 
	/*unsigned int auto_erase_bits;
	unsigned int did_erase_bits;*/
 
} arena[] = {
 
#ifdef INTERNAL_ROMARRAY
	{romarray, sizeof(romarray)},
#endif
 
#ifdef CAT_ROMARRAY
	{0, 0, -1},
#define FIXUP_ARENAS \
	arena[0].address = (unsigned long)__data_rom_start + (unsigned long)_edata - (unsigned long)__data_start;
#endif
 
#ifdef FIXED_ROMARRAY
	{0, FIXED_ROMARRAY, -1},
#endif
 
#ifdef CONFIG_SHGLCORE
 
#ifdef CONFIG_SHGLCORE_2MEG
	{0, 0x0A0000, 0x200000-0x0A0000},	/* ROM FS */
	{1, SHGLCORE_FLASH_BANK_0_ADDR, 0x80000, 0, 0, write_spare, erase_spare, 0x10000, 0x80000, 0xff},
	{1, 0x000000, 0x200000, program_main, 0,0,0, 0x20000, 0x100000}, /* All main FLASH */
#else
	{0, 0x0A0000, 0x100000-0x0A0000},	/* ROM FS */
	{1, SHGLCORE_FLASH_BANK_0_ADDR, 0x80000, 0, 0, write_spare, erase_spare, 0x10000, 0x80000, 0xff},
	{1, 0x000000, 0x100000, program_main, 0,0,0, 0x20000, 0x100000}, /* All main FLASH */
#endif
 
#define FIXUP_ARENAS \
	extern unsigned long rom_length; \
	arena[0].length = (unsigned long)rom_length - 0xA0000; \
	arena[2].length = (unsigned long)rom_length;
 
#endif
};
 
#define arenas (sizeof(arena) / sizeof(struct arena_t))
 
 
static int blkmem_blocksizes[arenas];
static int blkmem_sizes[arenas];
 
 
#ifdef CONFIG_SHGLCORE
static struct semaphore spare_lock = MUTEX;
 
void read_spare(struct arena_t * a, unsigned long pos, unsigned long length, char * buffer)
{
 
#ifdef DEBUG
  printk("rsl\n");
#endif
 
  /* Mutex all access to FLASH */
  down(&spare_lock);
 
#ifdef DEBUG
  printk("rsld\n");
#endif
 
  /* Just copy the data into target buffer */
  memcpy( buffer, (void*)(a->address+pos), length);
 
  /* Release MUTEX */
  up(&spare_lock);
 
#ifdef DEBUG
  printk("rsud\n");
#endif
}
 
void write_spare(struct arena_t * a, unsigned long pos, unsigned long length, char * buffer)
{
  unsigned long start;
  unsigned char c;
  volatile unsigned char * address;
  unsigned char result;
  unsigned long fbase = a->address;
  unsigned long flags;
 
#if 0
  for(i = pos / a->blksize; i <= ((pos+length-1) / a->blksize); i++) {
    if (test_bit(i, &a->auto_erase_bits)) {
      /* erase sector start */
      printk("Autoerase of sector %d\n", i);
      erase_spare(a, i * a->blksize);
      clear_bit(i, &a->auto_erase_bits);
    }
  }
#endif
 
#ifdef DEBUG
  printk("wsl\n");
#endif
 
  down(&spare_lock);
 
#ifdef DEBUG
  printk("wsld\n");
#endif
 
  start = jiffies;
 
  address = (unsigned volatile char*)(fbase+pos);
 
  while (length>0) {
 
    c = *buffer++;
 
    /*printk("Checking spare_flash program of byte %lx, at address %p, value %x (%c), current %x (%c)\n", pos, address, c, c, *address, *address);*/
 
    if (*address != c) {
 
      /*printk("Starting spare_flash program of byte %lx, at address %p\n", pos, address);*/
 
 
      if (c & ~*address) {
        printk("Unable to write byte at %p (impossible bit transition in %x, actual %x)\n", address, c, *address);
        /*continue;*/
      }
 
	save_flags(flags); cli();
 
      *(unsigned volatile char *)(fbase | 0x5555)=0x0aa;
      *(unsigned volatile char *)(fbase | 0x2aaa)=0x055;
      *(unsigned volatile char *)(fbase | 0x5555)=0x0a0;
 
      *address = c;
 
      for(;;) {
        result = *address;
        /*printk("Read value %x (%c)\n", result, result);*/
        if ((result & 0x80) == (c & 0x80))
          break;
        if (result & 0x20) {
          printk("timeout of FLASH write at address %p of value %x (actual %x)\n", address, c, *address);
          *(unsigned volatile char *)(fbase)=0x0f0; /* Reset */
          break;
        }
      }
 
      restore_flags(flags);
 
      /*printk("Completed spare_flash program of byte %lx, at address %p\n", pos, address);*/
 
#if 0
      if (jiffies != start) {
        /*printk("Spare_flash rescheduling in write\n");*/
        current->state = TASK_INTERRUPTIBLE;
        current->timeout = jiffies;
        schedule();
        current->timeout = 0;
        /*schedule();*/
        start = jiffies;
      }
#endif
    }
 
    address++;
    length--;
  }
 
  up(&spare_lock);
 
#ifdef DEBUG
  printk("wsud\n");
#endif
}
 
void erase_spare(struct arena_t * a, unsigned long pos)
{
  unsigned long fbase = a->address;
  int delay;
  unsigned volatile char * address;
  unsigned long flags;
 
  if (pos >= a->length)
    return;
 
  /* Mutex all access to FLASH memory */
 
#ifdef DEBUG
  printk("esl\n");
#endif
 
  down(&spare_lock);
 
#ifdef DEBUG
  printk("esld\n");
#endif
 
  address = (unsigned volatile char*)(fbase + pos);
 
  printk("Starting spare_flash erase of byte %lx, at address %p\n", pos, address);
 
  save_flags(flags); cli();
 
again:
 
  delay = HZ/4+1;
 
  /* Initiate erase of FLASH sector */
 
  *(unsigned volatile char *)(fbase | 0x5555)=0x0aa;
  *(unsigned volatile char *)(fbase | 0x2aaa)=0x055;
  *(unsigned volatile char *)(fbase | 0x5555)=0x080;
  *(unsigned volatile char *)(fbase | 0x5555)=0x0aa;
  *(unsigned volatile char *)(fbase | 0x2aaa)=0x055;
 
  *address = 0x030;
 
  /* Delay until erase is complete */
 
  for (;;) {
    unsigned char result;
#ifdef original_spare_erase_delay
    struct wait_queue *wait = NULL;
#ifdef DEBUG
    printk("Spare_flash erase delaying for %d ticks, status is %x\n", delay, (unsigned int)*address);
#endif
 
    current->timeout = jiffies + delay;
#if 0    
    current->state = TASK_INTERRUPTIBLE;
    schedule();
    current->timeout = 0;
#endif
    interruptible_sleep_on(&wait);
#endif
    udelay(100000);
 
    result = *address;
    if (result & 0x80)
       break;
    if (result & 0x20) {
       printk("timeout of Spare_flash erase of address %p\n", address);
       *(unsigned volatile char *)(fbase)=0x0f0; /* Reset */
       printk("Sleeping a second and retrying\n");
 
       udelay(1000000);
 
       goto again;
    }
  }
 
  restore_flags(flags);
 
#ifdef DEBUG
  printk("Completed spare_flash erase of byte %lx, at address %p\n", pos, address);
#endif
 
  up(&spare_lock);
 
#ifdef DEBUG
  printk("esud\n");
#endif
 
}
 
#define VSP(X) (*(volatile unsigned short *)(X))
#define VSC(X) (*(volatile unsigned char *)(X))
 
#define SCSR  VSP(0xfffc0c)
#define SCSR_TDRE (1<<8)
 
#define SCDR  VSP(0xfffC0e)
 
#define print_char(x) ({			\
	while (!(SCSR & SCSR_TDRE))		\
		;				\
	SCDR = (x);				\
})
 
#define print_hexdigit(x) ({			\
	int digit = (x) & 0xf;			\
	if (digit>9)				\
		print_char('a'+digit-10);	\
	else					\
		print_char('0'+digit);		\
						\
})
 
#define print_num(x) ({				\
	unsigned long num = (x);		\
	print_hexdigit(num >> 28);		\
	print_hexdigit(num >> 24);		\
	print_hexdigit(num >> 20);		\
	print_hexdigit(num >> 16);		\
	print_hexdigit(num >> 12);		\
	print_hexdigit(num >> 8);		\
	print_hexdigit(num >> 4);		\
	print_hexdigit(num >> 0);		\
})
 
/* Note: sub_program_main must not reference _any_ data or code outside of itself,
   or leave interrupts enabled, due to the fact that it is probably erasing
   & reloading the kernel. */
 
#define SET_SHORT(x,y) VSP((x)) = (y)
/*#define SET_SHORT(x,y) ({})*/ /*print_char('>');print_num(x);*/ /*printk("%8.8lx <= %04x\n", (x), (y))*/
 
#define SET_CHAR(x,y) VSC((x)) = (y)
/*#define SET_CHAR(x,y) ({})*/ /*print_char('>');print_num(x);*/ /*printk("%8.8lx <= %02x\n", (x), (y))*/
 
#define GET_SHORT(x) VSP((x))
/*#define GET_SHORT(x) ({0;})*/ /*({print_char('<');print_num(x);0;})*/ /*(printk("%8.8lx => ....\n", (x)),0)*/
 
#define GET_CHAR(x) VSC((x))
/*#define GET_CHAR(x) ({0;})*/ /*({print_char('<');print_num(x);0;})*/ /*(printk("%8.8lx => ..\n", (x)),0)*/
 
 
void sub_program_main(struct arena_t * a, struct blkmem_program_t * prog)
{
  volatile int i,l;
  unsigned long base, offset, ptr, min, max;
  unsigned char * c;
  unsigned int erased = 0;
  int failures;
  int retry;
 
  cli();
 
  retry = 0;
 
again:
 
  SET_ALARM_LED(1);
 
  retry++;
 
  if (retry>5) {
  	goto give_up;
  }
 
    print_char('\r');
    print_char('\n');
    print_char('R');
    print_char('0' + retry);
 
  failures = 0;
  erased = 0;
 
/*  for(i=prog->blocks-1;i>=0;i--) {*/
  for(i=0;i<prog->blocks;i++) {
 
    SET_COMM_STATUS_LED(!GET_COMM_STATUS_LED());
 
    print_char('\r');
    print_char('\n');
    print_num(prog->block[i].pos+a->address);
    print_char('-');
    print_num(prog->block[i].pos+prog->block[i].length-1+a->address);
    print_char('\r');
    print_char('\n');
 
    if(prog->block[i].length > 0xE0000)
      break;
 
    for(l=prog->block[i].pos / a->blksize; l <= ((prog->block[i].pos+prog->block[i].length-1) / a->blksize); l++) {
      if (!test_bit(l, &erased)) {
 
 	print_char('E');
 	print_char('0' + l / 10);
 	print_char('0' + l % 10);
 	print_char('\r');
 	print_char('\n');
 
 	if (l <  1)
 	  break;
 	/*if (l >= 8)
 	  break;*/
 
	ptr = l * a->blksize;
	offset = ptr % a->unitsize;
	base = ptr - offset;
 
	base += a->address;
	ptr += a->address;
 
	print_char('b');
	print_char('a');
	print_char('s');
	print_char('e');
	print_char(' ');
	print_num(base);
	print_char('\r');
	print_char('\n');
	print_char('o');
	print_char('f');
	print_char('f');
	print_char(' ');
	print_num(offset);
	print_char('\r');
	print_char('\n');
	print_char('p');
	print_char('t');
	print_char('r');
	print_char(' ');
	print_num(ptr);
	print_char('\r');
	print_char('\n');
 
        set_bit(l, &erased);
 
        if (ptr <  0x020000)
          break;
        /*if (ptr >= 0x100000)
          break;*/
 
        print_num(ptr);
 
 	SET_COMM_ERROR_LED(1);
 
        /* Erase even half of sector */
        SET_SHORT( (base | (0x5555 << 1)), 0xaa00);
        SET_SHORT( (base | (0x2aaa << 1)), 0x5500);
        SET_SHORT( (base | (0x5555 << 1)), 0x8000);
        SET_SHORT( (base | (0x5555 << 1)), 0xaa00);
        SET_SHORT( (base | (0x2aaa << 1)), 0x5500);
 
        SET_SHORT( ptr, 0x3000);
#ifdef original_erase_logic
        while (!(GET_SHORT(ptr) & 0x8000))
          ;
#else
	for (;;) {
		unsigned int status = GET_SHORT(ptr);
		if (status & 0x8000) {
			/* Erase complete */
			break;
		}
		if (status & 0x2000) {
			/* Check again */
			status = GET_SHORT(ptr);
			if (status & 0x8000) {
				/* Erase complete */
				break;
			}
 
			/* Erase failed */
			print_char('F');
 
			/* Reset FLASH unit */
			SET_SHORT( base, 0xf000);
 
			failures++;
 
			/* Continue (with unerased sector) */
			break;
		}
        }
#endif
 
        print_char(':');
 
        /* Erase odd half of sector */
        SET_SHORT( (base | (0x5555 << 1)), 0x00aa);
        SET_SHORT( (base | (0x2aaa << 1)), 0x0055);
        SET_SHORT( (base | (0x5555 << 1)), 0x0080);
        SET_SHORT( (base | (0x5555 << 1)), 0x00aa);
        SET_SHORT( (base | (0x2aaa << 1)), 0x0055);
 
        SET_SHORT( ptr, 0x0030);
#ifdef original_erase_logic
        while (!(GET_SHORT(ptr) & 0x0080))
          ;
#else
	for (;;) {
		unsigned int status = GET_SHORT(ptr);
		if (status & 0x0080) {
			/* Erase complete */
			break;
		}
		if (status & 0x0020) {
 
			/* Check again */			
			status = GET_SHORT(ptr);
			if (status & 0x0080) {
				/* Erase complete */
				break;
			}
 
			/* Erase failed */
			print_char('F');
 
			/* Reset FLASH unit */
			SET_SHORT( base, 0x00f0);
 
			failures++;
 
			/* Continue (with unerased sector) */
			break;
		}
        }
#endif
 
        print_char(':');
 
#if 0
        probe = (volatile unsigned short*)(fbase + a->blksize * l);
        *probe = 0x3000;
        while (!(*probe & 0x8000))
          ;
 
        print_char('.');
 
        /* Erase odd half of sector */
        *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00aa;
        *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x0055;
        *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x0080;
        *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00aa;
        *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x0055;
 
        probe = (volatile unsigned short*)(fbase + a->blksize * l);
        *probe = 0x0030;
        while (!(*probe & 0x0080))
          break;
 
        print_char('.');
#endif
 
 	SET_COMM_ERROR_LED(0);
      }
    }
 
 
    min = prog->block[i].pos+a->address;
    max = prog->block[i].pos+prog->block[i].length+a->address;
    for(ptr=min, c=prog->block[i].data; ptr<max; ptr++, c++) {
 
      offset = (ptr-a->address) % a->unitsize;
      base = ptr - offset;
 
      if (ptr <  0x020000)
        break;
      /*if (ptr >= 0x100000)
        break;*/
 
      /*print_char('.');*/
 
#if 0
      if ((fbase & 1) == 0) { /* Even bank */
        *(unsigned volatile short *)(fbase | (0x5555 << 1))=0xaa00;
        *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x5500;
        *(unsigned volatile short *)(fbase | (0x5555 << 1))=0xa000;
        *(unsigned volatile short *)(fbase + (b & ~1))     =*c << 8;
      } else { /* Odd bank */
        *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00aa;
        *(unsigned volatile short *)(fbase | (0x2aaa << 1))=0x0055;
        *(unsigned volatile short *)(fbase | (0x5555 << 1))=0x00a0;
        *(unsigned volatile short *)(fbase + (b & ~1))     =*c;
      }
 
      probe = (volatile unsigned char*)(fbase + b);
      while (*probe != *c)
          break;
#endif
 
      if ((ptr & 1) == 0) { /* Even bank */
        SET_SHORT( (base | (0x5555 << 1)), 0xaa00);
        SET_SHORT( (base | (0x2aaa << 1)), 0x5500);
        SET_SHORT( (base | (0x5555 << 1)), 0xa000);
        SET_SHORT( (ptr & ~1),      *c << 8);
      } else { /* Odd bank */
        SET_SHORT( (base | (0x5555 << 1)), 0x00aa);
        SET_SHORT( (base | (0x2aaa << 1)), 0x0055);
        SET_SHORT( (base | (0x5555 << 1)), 0x00a0);
        SET_SHORT( (ptr & ~1),      *c);
      }
 
#ifdef original_write_logic
      while (GET_CHAR(ptr) != *c)
        ;
#else
	for (;;) {
		unsigned char status = GET_CHAR(ptr);
		if ((status & 0x80) == (*c & 0x80)) {
			/* Program complete */
			break;
		}
		if (status & 0x20) {
			/* Check again */
			status = GET_CHAR(ptr);
			if ((status & 0x80) == (*c & 0x80)) {
				/* Program complete */
				break;
			}
 
			/* Program failed */
			print_char('F');
 
			/* Reset FLASH unit */
			if ((ptr & 1) == 0) { /* Even bank */
				SET_SHORT( base, 0xf000);
			} else { /* Odd bank */
				SET_SHORT( base, 0x00f0);
			}
 
			failures++;
 
			/* Continue */
			break;
		}
        }
#endif
 
      /*print_char(' ');*/
    }
  }
 
  if (failures > 0) {
  	/* There were failures erasing the FLASH, so go back to the beginning and
  	try it all again -- for lack of anything better to do. */
 
  	print_char('!');
  	goto again;
  }
 
give_up:
 
  SET_ALARM_LED(1);
 
 
  HARD_RESET_NOW();
 
  i = 1;
  while(i)
    ;
 
}
 
void program_main(struct arena_t * a, struct blkmem_program_t * prog)
{
  int len;
  void (*code)(struct arena_t*, struct blkmem_program_t *);
 
  printk("program_main entered, blocks = %d\n", prog->blocks);
 
  len = &program_main-&sub_program_main;
  code = kmalloc(len, GFP_KERNEL);
  memcpy(code, &sub_program_main, len);
 
  code(a, prog);
 
  kfree(code);
 
  /*sub_program_main(a, prog);*/
}
#endif /* CONFIG_SHGLCORE */
 
int general_program_func(struct inode * inode, struct file * file, struct arena_t * a, struct blkmem_program_t * prog)
{
  int i,block;
  int err;
  unsigned int erased = 0;
  unsigned long pos = file->f_pos;
 
  /* Mandatory flush of all dirty buffers */
  fsync_dev(inode->i_rdev);
  invalidate_buffers(inode->i_rdev);
 
  for(i=0;i<prog->blocks;i++) {
    int min= prog->block[i].pos / a->blksize;
    int max = (prog->block[i].pos + prog->block[i].length - 1) / a->blksize;
    for(block=min; block <= max; block++) {
      if (!test_bit(block, &erased)) {
	printk("Erase of sector at pos %lx of arena %d (address %p)\n", block * a->blksize, MINOR(inode->i_rdev), (void*)(a->address+block*a->blksize));
 
        /* Invoke erase function */
        a->erase_func(a, block * a->blksize);
        set_bit(block, &erased);
      }
    }
 
    printk("Write of %lu bytes at pos %lu (data at %p)\n", prog->block[i].length, prog->block[i].pos, prog->block[i].data);
 
    a->write_func(a, prog->block[i].pos, prog->block[i].length, prog->block[i].data);
 
    schedule();
  }
 
  if (prog->reset)
  	HARD_RESET_NOW();
  return 0;
}
 
 
static void complete_request(void * data);
 
static struct tq_struct complete_tq = {0, 0, complete_request, NULL};
 
static void
delay_request( void )
{
  queue_task(&complete_tq, &tq_scheduler);
}
 
static void
complete_request( void * data)
{
  unsigned long start;
  unsigned long len;
  struct arena_t * a = arena + DEVICE_NR(CURRENT_DEV);
 
  for(;;) {
 
  /* sectors are 512 bytes */
  start = CURRENT->sector << 9;
  len  = CURRENT->current_nr_sectors << 9;
 
  /*printk("blkmem: re-request %d\n", CURRENT->cmd);*/
 
  if ( CURRENT->cmd == READ ) {
    /*printk("BMre-Read: %lx:%lx > %p\n", a->address + start, len, CURRENT->buffer);*/
    a->read_func(a, start, len, CURRENT->buffer);
  }
  else if (CURRENT->cmd == WRITE) {
    /*printk("BMre-Write: %p > %lx:%lx\n", CURRENT->buffer, a->address + start, len);*/
    a->write_func(a, start, len, CURRENT->buffer);
  }
  /*printk("ending blkmem request\n");*/
  end_request( TRUE );
 
  INIT_REQUEST;
  }
 
#if 0
  if (CURRENT)
    do_blkmem_request(); /* Process subsequent requests */
#endif
}
 
 
static void
do_blkmem_request( void )
{
  unsigned long start;
  unsigned long len;
  struct arena_t * a = arena + DEVICE_NR(CURRENT_DEV);
#if 0
  printk( KERN_ERR DEVICE_NAME ": request\n");
#endif
  while ( TRUE ) {
    INIT_REQUEST;
 
    /* sectors are 512 bytes */
    start = CURRENT->sector << 9;
    len  = CURRENT->current_nr_sectors << 9;
 
    if ((start + len) > a->length) {
      printk( KERN_ERR DEVICE_NAME ": bad access: block=%ld, count=%ld (pos=%lx, len=%lx)\n",
	      CURRENT->sector,
	      CURRENT->current_nr_sectors,
	      start+len,
	      a->length);
      end_request( FALSE );
      continue;
    }
 
    /*printk("blkmem: request %d\n", CURRENT->cmd);*/
 
    if ( ( CURRENT->cmd != READ ) 
	 && ( CURRENT->cmd != WRITE ) 
	 ) {
      printk( KERN_ERR DEVICE_NAME ": bad command: %d\n", CURRENT->cmd );
      end_request( FALSE );
      continue;
    }
 
    if ( CURRENT->cmd == READ ) {
      /*printk("BMRead %d, pid %d: %lx:%lx > %p\n", DEVICE_NR(CURRENT_DEV), current->pid, a->address + start, len, CURRENT->buffer);*/
      if (a->read_func) {
        delay_request();
        return;
        /*a->read_func(a, start, len, CURRENT->buffer);*/
      }
      else
        memcpy( CURRENT->buffer, (void*)(a->address + start), len );
    }
    else if (CURRENT->cmd == WRITE) {
      /*printk("BMWrite %d: %p > %lx:%lx\n", DEVICE_NR(CURRENT_DEV), CURRENT->buffer, a->address + start, len);*/
      if (a->write_func) {
        delay_request();
        return;
        /*a->write_func(a, start, len, CURRENT->buffer);*/
      } else
        memcpy( (void*)(a->address + start), CURRENT->buffer, len );
    }
    /*printk("ending blkmem request\n");*/
    end_request( TRUE );
  }
}
 
 
#ifdef MAGIC_ROM_PTR
static int
blkmem_romptr( struct inode *inode, struct file *filp, struct vm_area_struct * vma)
{
  struct arena_t * a = arena + MINOR(inode->i_rdev);
 
  if (a->read_func)
    return -ENOSYS; /* Can't do it, as this arena isn't in the main address space */
 
  vma->vm_start = a->address + vma->vm_offset;
  return 0;
}
#endif
 
static int blkmem_ioctl (struct inode *inode, struct file *file,
                         unsigned int cmd, unsigned long arg)
{
  struct arena_t * a = arena + MINOR(inode->i_rdev);
  int err;
 
  switch (cmd) {
  case BMGETSIZES:   /* Return device size in sectors */
 
    if (!arg)
      return -EINVAL;
 
    err = verify_area(VERIFY_WRITE, (unsigned long *) arg, sizeof(unsigned long));
    if (err)
      return err;
 
    put_user(a->blksize ? (a->length / a->blksize) : 0, (unsigned long *) arg);
 
    break;
 
  case BMGETSIZEB:   /* Return device size in bytes */
 
    if (!arg)
      return -EINVAL;
 
    err = verify_area(VERIFY_WRITE, (unsigned long *) arg, sizeof(unsigned long));
    if (err)
      return err;
 
    put_user(a->length, (unsigned long *) arg);
 
    break;
 
  case BMSERASE:
    if (a->erase_func) {
 
      printk("Erase of sector at pos %lx of arena %d (address %p)\n", arg, MINOR(inode->i_rdev), (void*)(a->address+arg));
 
      if (arg >= a->length)
        return -EINVAL;
 
      /* Mandatory flush of all dirty buffers */
      fsync_dev(inode->i_rdev);
 
      /* Invalidate any buffers pointing to the section that will be erased */
      invalidate_buffers_by_byte(inode->i_rdev, arg * a->blksize, a->blksize);
 
      /* Invoke erase function */
      a->erase_func(a, arg);
    } else
      return -EINVAL;
    break;
 
  case BMSGSIZE:
 
    if (!arg)
      return -EINVAL;
 
    err = verify_area(VERIFY_WRITE, (unsigned long*)arg, sizeof(unsigned long));
    if (err)
      return err;
 
    put_user(a->blksize, (unsigned long*)arg);
    break;
 
  case BMSGERASEVALUE:
    err = verify_area(VERIFY_WRITE, (unsigned char*)arg, sizeof(unsigned char));
    if (err)
      return err;
 
    *(unsigned char*)arg = a->erasevalue;
    break;
 
  case BMPROGRAM:
  {
    struct blkmem_program_t * prog;
    int i;
    if (!arg)
      return -EINVAL;
 
    prog = (struct blkmem_program_t*)arg;
 
    /* Extensive checks to verify that the programming data makes sense */
 
    err = verify_area(VERIFY_READ, prog, sizeof(struct blkmem_program_t));
    if (err)
      return err;
 
    if ((prog->magic1 != BMPROGRAM_MAGIC_1) ||
        (prog->magic2 != BMPROGRAM_MAGIC_2))
      return -EINVAL;
 
    err = verify_area(VERIFY_READ, prog, sizeof(struct blkmem_program_t) + sizeof(prog->block[0]) * prog->blocks);
    if (err)
      return err;
 
    for(i=0;i<prog->blocks;i++)
      if(prog->block[i].magic3 != BMPROGRAM_MAGIC_3)
        return -EINVAL;
 
    for(i=0;i<prog->blocks;i++)
      if ((prog->block[i].pos > a->length) ||
          ((prog->block[i].pos+prog->block[i].length-1) > a->length))
        return -EINVAL;
 
    for(i=0;i<prog->blocks;i++) {
      err = verify_area(VERIFY_READ, prog->block[i].data, prog->block[i].length);
      if (err)
        return err;
    }
 
    if (a->program_func) {
      a->program_func(a, prog);
    } else {
      return general_program_func(inode, file, a, prog);
    }
 
    break;
  }
  default:
    return -EINVAL;
  }
  return 0;
}
 
static int
blkmem_open( struct inode *inode, struct file *filp )
{
  int device;
  struct arena_t * a;
 
  device = DEVICE_NR( inode->i_rdev );
#if 0
  printk( KERN_ERR DEVICE_NAME ": open: %d\n", device );
#endif
 
  if ((MINOR(device) < 0) || (MINOR(device) >= arenas))  {
    printk("arena open of %d failed!\n", MINOR(device));
    return -ENODEV;
  }
 
  a = &arena[MINOR(device)];
 
#ifdef DEBUG
  printk("Open of blkmem arena %d at %lx, length %lx\n", MINOR(device), a->address, a->length);
#endif
 
#if defined(MODULE)
  MOD_INC_USE_COUNT;
#endif
  return 0;
}
 
static void 
blkmem_release( struct inode *inode, struct file *filp )
{
#if 0
  printk( KERN_ERR DEVICE_NAME ": release: %d\n", current_device );
#endif
 
  fsync_dev( inode->i_rdev );
 
#if defined(MODULE)
  MOD_DEC_USE_COUNT;
#endif
 
  return;
}
 
static struct file_operations blkmem_fops =
{
  NULL,                   /* lseek - default */
  block_read,             /* read - general block-dev read */
  block_write,            /* write - general block-dev write */
  NULL,                   /* readdir - bad */
  NULL,                   /* poll */
  blkmem_ioctl,           /* ioctl */
  NULL,
  blkmem_open,            /* open */
  blkmem_release,         /* release */
  block_fsync,            /* fsync */
  NULL,			  /* fasync */
  NULL,			  /* check media change */
  NULL,			  /* revalidate */
  blkmem_romptr,          /* romptr */
};
 
int
blkmem_init( void )
{
  int i;
  if ( register_blkdev( MAJOR_NR, DEVICE_NAME, &blkmem_fops )) {
    printk( KERN_ERR DEVICE_NAME ": Unable to get major %d\n",
            MAJOR_NR );
    return -EBUSY;
  }
 
#ifdef FIXUP_ARENAS
  {
  FIXUP_ARENAS
  }
#endif
 
  for(i=0;i<arenas;i++) {
    if (arena[i].length == -1)
      arena[i].length = *(volatile unsigned long *)(arena[i].address + 8);
    blkmem_blocksizes[i] = 1024;
    blkmem_sizes[i] = (arena[i].length + (1 << 10) - 1) >> 10; /* Round up */
    arena[i].length = blkmem_sizes[i] << 10;
  }
  printk("Blkmem copyright 1998,1999 D. Jeff Dionne\nBlkmem copyright 1998 Kenneth Albanowski\nBlkmem %ld disk images:\n", arenas);
 
  for(i=0;i<arenas;i++) {
    printk("%d: %lX-%lX (%s)\n", i, arena[i].address, arena[i].address+arena[i].length-1,
    	arena[i].rw 
    		? "RW"
    		: "RO"
    );
  }
 
  read_ahead[ MAJOR_NR ] = 0;
  blk_dev[ MAJOR_NR ].request_fn = DEVICE_REQUEST;
  blksize_size[ MAJOR_NR ] = blkmem_blocksizes;
  blk_size[ MAJOR_NR ] = blkmem_sizes;
 
#ifdef ROOT_ARENA
  ROOT_DEV = MKDEV(MAJOR_NR,ROOT_ARENA);
#endif
 
  return 0;
}
 
#if defined(MODULE)
 
int
init_module( void )
{
  int error;
 
  error = blkmem_init();
  if ( error == 0 ) {
    printk( KERN_INFO DEVICE_NAME ": loaded as module\n" );
  }
 
  return error;
}
 
void
cleanup_module( void )
{
  if ( unregister_blkdev( MAJOR_NR, DEVICE_NAME ) != 0 )
    printk( KERN_ERR DEVICE_NAME ": unregister of device failed\n");
 
  if ( current_device != -1 ) {
    /* free whatever we resources remain here */
  }
 
  return;
}
#endif
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.