Files
retrobsd/tools/virtualmips/dev_nor_flash_4m.c
2014-04-09 14:27:18 +01:00

327 lines
9.3 KiB
C

/*
* Copyright (C) yajin 2008 <yajinzhou@gmail.com >
*
* This file is part of the virtualmips distribution.
* See LICENSE file for terms of the license.
*/
/*
4M *byte* FLASH device simulation (device id=22F9h).
Most important part of flash simulation is CFI interface.
See flash datasheet for details.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include<fcntl.h>
#include "device.h"
#include "mips_memory.h"
#define ROM_INIT_STATE 0
/* flash private data */
struct flash_data {
struct vdevice *dev;
m_uint8_t *flash_ptr;
m_uint32_t flash_size;
m_uint8_t *flash_file_name;
m_uint32_t state;
int flash_fd;
};
typedef struct flash_data flash_data_t;
#define BPTR(d,offset) (((char *)d->flash_ptr) + offset)
m_uint16_t vendorID = 0x01; // target is little end 0x0001
m_uint16_t deviceID = 0x22f9; // target is little end 0x22F9
m_uint16_t earse_ready = 0x80; // target is little end 0X0080
m_uint32_t last_offset = 0;
m_uint32_t dump_data;
m_uint32_t cfi_data[] =
{ 0x51, 0x52, 0x59, 0x2, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x27, 0x36,
0x0, 0x0, 0x4,
0x0, 0xa, 0x0, 0x5, 0x0, 0x4, 0x0, 0x16, 0x2, 0x0, 0x0, 0x0, 0x2, 0x7,
0x0, 0x20,
0x0, 0x3e, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0,
0x50, 0x52, 0x49, 0x31, 0x31, 0x0, 0x2, 0x4, 0x1, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xc5, 0x2, //02 BOTTOM
};
static void secotor_info (m_uint32_t offset, m_uint32_t * sector_start,
m_uint32_t * sector_size)
{
if (offset <= 0x00FFF) {
*sector_start = offset & 0xFFFFFE00;
*sector_size = 0x2000;
return;
}
*sector_start = offset & (0xffff0000);
*sector_size = 0x10000;
}
void *dev_flash_access (cpu_mips_t * cpu, struct vdevice *dev,
m_uint32_t offset, u_int op_size, u_int op_type, m_uint32_t * data,
m_uint8_t * has_set_value)
{
flash_data_t *d = dev->priv_data;
m_uint32_t r_offset, sector_size, sector_start;
m_uint32_t last_sector_start, last_sector_size;
if (offset >= d->flash_size) {
*data = 0;
return NULL;
}
if (op_type == MTS_READ) {
switch (d->state) {
case 0:
return (BPTR (d, offset));
case 99: /*cfi query */
if ((offset >= 0x20) && (offset <= 0x9e)) {
*data = (cfi_data[(offset - 0x20) / 2]); //always littleend
*has_set_value = TRUE;
} else {
d->state = 0;
return (BPTR (d, offset));
}
break;
case 0x6:
d->state = 0;
if (offset == 0X0)
return &vendorID;
if (offset == 0X2)
return &deviceID;
break;
case 10:
//last cycle is chip erase or sector erase
secotor_info (offset, &sector_start, &sector_size);
secotor_info (last_offset, &last_sector_start, &last_sector_size);
d->state = 0;
if (last_sector_start == sector_start)
return &earse_ready;
else
return (BPTR (d, offset));
break;
default:
cpu_log (cpu, dev->name, "read: unhandled state %d\n", d->state);
}
return NULL;
}
if (op_type == MTS_WRITE) {
r_offset = offset;
if ((op_size == MTS_HALF_WORD) && (offset == 0X554))
offset = 0X555;
switch (d->state) {
case ROM_INIT_STATE:
switch (offset) {
case 0xAAA:
if (((*data) & 0xff) == 0xAA)
d->state = 1;
break;
case 0XAA:
if (((*data) & 0xff) == 0x98) {
d->state = 99; //CFI QUERY
}
break;
default:
switch ((*data) & 0xff) {
case 0xB0:
/* Erase/Program Suspend */
d->state = 0;
break;
case 0x30:
/* Erase/Program Resume */
d->state = 0;
break;
case 0xF0:
case 0xFF:
/*Read/Reset */
d->state = 0;
break;
default:
return ((void *) (d->flash_ptr + r_offset));
}
}
break;
case 99:
if (((*data & 0xff) == 0xff) || ((*data & 0xff) == 0xf0))
d->state = 0;
else
return ((void *) (d->flash_ptr + r_offset));
break;
case 1:
if ((offset != 0x555) && ((*data & 0xff) != 0x55))
d->state = 0;
else
d->state = 2;
break;
case 2:
d->state = 0;
if (offset == 0xAAA) {
switch ((*data) & 0xff) {
case 0x80:
d->state = 3;
break;
case 0xA0:
/* Byte/Word program */
d->state = 4;
break;
case 0x90:
/* Product ID Entry / Status of Block B protection */
d->state = 6;
break;
}
}
break;
case 3:
if ((offset != 0xAAA) && (*data != 0xAA))
d->state = 0;
else
d->state = 8;
break;
case 8:
if ((offset != 0x555) && (*data != 0x55))
d->state = 0;
else
d->state = 9;
break;
case 9:
d->state = 10;
last_offset = r_offset;
switch ((*data) & 0xff) {
case 0x10:
/* Chip Erase */
memset (BPTR (d, 0), 0, d->dev->phys_len);
break;
case 0x30:
/* Sector Erase */
secotor_info (r_offset, &sector_start, &sector_size);
break;
}
break;
/* Byte/Word Program */
case 4:
d->state = 0;
return ((void *) (d->flash_ptr + r_offset));
break;
default:
cpu_log (cpu, dev->name, "write: unhandled state %d\n", d->state);
}
return &dump_data;
}
assert (0);
}
static int dev_flash_load (char *flash_file_name, m_uint32_t flash_len,
unsigned char **flash_data_hp, u_int create)
{
int fd;
struct stat sb;
unsigned char *temp;
fd = open (flash_file_name, O_RDWR);
if ((fd < 0) && (create == 1)) {
fprintf (stderr, "Can not open flash file. name %s\n",
flash_file_name);
fprintf (stderr, "creating flash file. name %s\n", flash_file_name);
fd = open (flash_file_name, O_RDWR | O_CREAT,
S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (fd < 0) {
fprintf (stderr, "Can not create flash file. name %s\n",
flash_file_name);
return (-1);
}
temp = malloc (flash_len);
assert (temp != NULL);
memset (temp, 0xff, flash_len);
lseek (fd, 0, SEEK_SET);
write (fd, (void *) temp, flash_len);
free (temp);
fprintf (stderr, "create flash file success. name %s\n",
flash_file_name);
lseek (fd, 0, SEEK_SET);
} else if (fd < 0) {
fprintf (stderr, "%s does not exist and not allowed to create.\n",
flash_file_name);
return (-1);
}
assert (fd >= 0);
fstat (fd, &sb);
if (flash_len < sb.st_size) {
fprintf (stderr,
"Too large flash file. flash len:%d M, flash file name %s,"
"flash file legth: %d bytes.\n", flash_len, flash_file_name,
(int) sb.st_size);
return (-1);
}
*flash_data_hp =
mmap (NULL, sb.st_size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
if (*flash_data_hp == MAP_FAILED) {
fprintf (stderr, "errno %d\n", errno);
fprintf (stderr, "failed\n");
return (-1);
}
return 0;
}
/* Initialize a NOR Flash zone */
int dev_nor_flash_4m_init (vm_instance_t * vm, char *name)
{
flash_data_t *d;
unsigned char *flash_data_hp;
/*load rom data */
if (dev_flash_load (vm->flash_filename, vm->flash_size * 1048576,
&flash_data_hp, 1) < 0)
return (-1);
/* allocate the private data structure */
if (!(d = malloc (sizeof (*d)))) {
fprintf (stderr, "FLASH: unable to create device.\n");
return (-1);
}
memset (d, 0, sizeof (*d));
d->flash_ptr = flash_data_hp;
d->flash_size = vm->flash_size * 1048576;
d->state = ROM_INIT_STATE;
if (!(d->dev = dev_create (name)))
goto err_dev_create;
d->dev->priv_data = d;
d->dev->phys_addr = vm->flash_address;
d->dev->phys_len = vm->flash_size * 1048576;
d->dev->handler = dev_flash_access;
d->dev->flags = VDEVICE_FLAG_NO_MTS_MMAP;
/* Map this device to the VM */
vm_bind_device (vm, d->dev);
return (0);
err_dev_create:
free (d);
return (-1);
}